1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Copyright 2020 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::piet::{FixedGradient, LinearGradient, PaintBrush, RadialGradient};
use crate::widget::prelude::*;
use crate::{Color, Data, Key};
use tracing::instrument;

/// A widget that only handles painting.
///
/// This is useful in a situation where layout is controlled elsewhere and you
/// do not need to handle events, but you would like to customize appearance.
///
/// **When is paint called?**
///
/// The `Painter` widget will call its [`paint`]  method anytime its [`Data`]
/// is changed. If you would like it to repaint at other times (such as when
/// hot or active state changes) you will need to call [`request_paint`] further
/// up the tree, perhaps in a [`Controller`] widget.
///
/// # Examples
///
/// Changing background color based on some part of data:
///
/// ```
/// use druid::{Env, PaintCtx,Rect, RenderContext};
/// use druid::widget::Painter;
/// # const ENABLED_BG_COLOR: druid::Key<druid::Color> = druid::Key::new("fake key");
/// # const DISABLED_BG_COLOR: druid::Key<druid::Color> = druid::Key::new("fake key 2");
///
/// struct MyData { is_enabled: bool }
///
/// let my_painter = Painter::new(|ctx, data: &MyData, env| {
///     let bounds = ctx.size().to_rect();
///     if data.is_enabled {
///         ctx.fill(bounds, &env.get(ENABLED_BG_COLOR));
///     } else {
///
///         ctx.fill(bounds, &env.get(DISABLED_BG_COLOR));
///     }
/// });
/// ```
///
/// Using painter to make a simple widget that will draw a selected color
///
///
/// ```
/// use druid::{Color, Env, PaintCtx,Rect, RenderContext};
/// use druid::widget::Painter;
///
/// const CORNER_RADIUS: f64 = 4.0;
/// const STROKE_WIDTH: f64 = 2.0;
///
/// let colorwell: Painter<Color> = Painter::new(|ctx, data: &Color, env| {
///     // Shrink the bounds a little, to ensure that our stroke remains within
///     // the paint bounds.
///     let bounds = ctx.size().to_rect().inset(-STROKE_WIDTH / 2.0);
///     let rounded = bounds.to_rounded_rect(CORNER_RADIUS);
///     ctx.fill(rounded, data);
///     ctx.stroke(rounded, &env.get(druid::theme::PRIMARY_DARK), STROKE_WIDTH);
/// });
/// ```
///
/// [`paint`]: Widget::paint
/// [`request_paint`]: EventCtx::request_paint
/// [`Controller`]: super::Controller
pub struct Painter<T>(Box<dyn FnMut(&mut PaintCtx, &T, &Env)>);

/// Something that can be used as the background for a widget.
///
/// This represents anything that can be painted inside a widgets [`paint`]
/// method; that is, it may have access to the [`Data`] and the [`Env`].
///
/// [`paint`]: Widget::paint
#[non_exhaustive]
#[allow(missing_docs)]
pub enum BackgroundBrush<T> {
    Color(Color),
    ColorKey(Key<Color>),
    Linear(LinearGradient),
    Radial(RadialGradient),
    Fixed(FixedGradient),
    Painter(Painter<T>),
}

impl<T> Painter<T> {
    /// Create a new `Painter` with the provided [`paint`] fn.
    ///
    /// [`paint`]: Widget::paint
    pub fn new(f: impl FnMut(&mut PaintCtx, &T, &Env) + 'static) -> Self {
        Painter(Box::new(f))
    }
}

impl<T: Data> BackgroundBrush<T> {
    /// Request paint if the BackgroundBrush changed.
    pub fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
        match self {
            Self::ColorKey(key) if ctx.env_key_changed(key) => {
                ctx.request_paint();
            }
            Self::Painter(p) => p.update(ctx, old_data, data, env),
            _ => (),
        }
    }

    /// Draw this `BackgroundBrush` into a provided [`PaintCtx`].
    pub fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
        let bounds = ctx.size().to_rect();
        match self {
            Self::Color(color) => ctx.fill(bounds, color),
            Self::ColorKey(key) => ctx.fill(bounds, &env.get(key)),
            Self::Linear(grad) => ctx.fill(bounds, grad),
            Self::Radial(grad) => ctx.fill(bounds, grad),
            Self::Fixed(grad) => ctx.fill(bounds, grad),
            Self::Painter(painter) => painter.paint(ctx, data, env),
        }
    }
}

impl<T: Data> Widget<T> for Painter<T> {
    fn event(&mut self, _: &mut EventCtx, _: &Event, _: &mut T, _: &Env) {}
    fn lifecycle(&mut self, _: &mut LifeCycleCtx, _: &LifeCycle, _: &T, _: &Env) {}
    #[instrument(name = "Painter", level = "trace", skip(self, ctx, old_data, data))]
    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, _: &Env) {
        if !old_data.same(data) {
            ctx.request_paint();
        }
    }
    #[instrument(name = "Painter", level = "trace", skip(self, _ctx, bc))]
    fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints, _: &T, _: &Env) -> Size {
        bc.max()
    }
    #[instrument(name = "Painter", level = "trace", skip(self, ctx, data, env))]
    fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
        (self.0)(ctx, data, env)
    }
}

impl<T> From<Color> for BackgroundBrush<T> {
    fn from(src: Color) -> BackgroundBrush<T> {
        BackgroundBrush::Color(src)
    }
}

impl<T> From<Key<Color>> for BackgroundBrush<T> {
    fn from(src: Key<Color>) -> BackgroundBrush<T> {
        BackgroundBrush::ColorKey(src)
    }
}

impl<T> From<LinearGradient> for BackgroundBrush<T> {
    fn from(src: LinearGradient) -> BackgroundBrush<T> {
        BackgroundBrush::Linear(src)
    }
}

impl<T> From<RadialGradient> for BackgroundBrush<T> {
    fn from(src: RadialGradient) -> BackgroundBrush<T> {
        BackgroundBrush::Radial(src)
    }
}

impl<T> From<FixedGradient> for BackgroundBrush<T> {
    fn from(src: FixedGradient) -> BackgroundBrush<T> {
        BackgroundBrush::Fixed(src)
    }
}

impl<T> From<Painter<T>> for BackgroundBrush<T> {
    fn from(src: Painter<T>) -> BackgroundBrush<T> {
        BackgroundBrush::Painter(src)
    }
}

impl<T> From<PaintBrush> for BackgroundBrush<T> {
    fn from(src: PaintBrush) -> BackgroundBrush<T> {
        match src {
            PaintBrush::Linear(grad) => BackgroundBrush::Linear(grad),
            PaintBrush::Radial(grad) => BackgroundBrush::Radial(grad),
            PaintBrush::Fixed(grad) => BackgroundBrush::Fixed(grad),
            PaintBrush::Color(color) => BackgroundBrush::Color(color),
        }
    }
}