1use self::style::{Status, StyleFn};
6
7use super::overlay::color_picker::{
8 self, ColorBarDragged, ColorPickerOverlay, ColorPickerOverlayButtons,
9};
10
11use iced_core::{
12 Clipboard, Color, Element, Event, Layout, Length, Point, Rectangle, Shell, Vector, Widget,
13 layout::{Limits, Node},
14 mouse::{self, Cursor},
15 overlay, renderer,
16 widget::tree::{self, Tag, Tree},
17};
18use iced_widget::Renderer;
19
20pub use crate::style::{self, color_picker::Style};
21
22#[allow(missing_debug_implementations)]
47pub struct ColorPicker<'a, Message, Theme = iced_widget::Theme>
48where
49 Message: Clone,
50 Theme: style::color_picker::Catalog + iced_widget::button::Catalog,
51{
52 show_picker: bool,
54 color: Color,
56 underlay: Element<'a, Message, Theme, Renderer>,
58 on_cancel: Message,
60 on_submit: Box<dyn Fn(Color) -> Message>,
62 class: <Theme as style::color_picker::Catalog>::Class<'a>,
64 overlay_state: Element<'a, Message, Theme, Renderer>,
66}
67
68impl<'a, Message, Theme> ColorPicker<'a, Message, Theme>
69where
70 Message: 'a + Clone,
71 Theme: 'a
72 + style::color_picker::Catalog
73 + iced_widget::button::Catalog
74 + iced_widget::text::Catalog,
75{
76 pub fn new<U, F>(
88 show_picker: bool,
89 color: Color,
90 underlay: U,
91 on_cancel: Message,
92 on_submit: F,
93 ) -> Self
94 where
95 U: Into<Element<'a, Message, Theme, Renderer>>,
96 F: 'static + Fn(Color) -> Message,
97 {
98 Self {
99 show_picker,
100 color,
101 underlay: underlay.into(),
102 on_cancel,
103 on_submit: Box::new(on_submit),
104 class: <Theme as style::color_picker::Catalog>::default(),
105 overlay_state: ColorPickerOverlayButtons::default().into(),
106 }
107 }
108
109 #[must_use]
111 pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
112 where
113 <Theme as style::color_picker::Catalog>::Class<'a>: From<StyleFn<'a, Theme, Style>>,
114 {
115 self.class = (Box::new(style) as StyleFn<'a, Theme, Style>).into();
116 self
117 }
118
119 #[must_use]
121 pub fn class(
122 mut self,
123 class: impl Into<<Theme as style::color_picker::Catalog>::Class<'a>>,
124 ) -> Self {
125 self.class = class.into();
126 self
127 }
128}
129
130#[derive(Debug, Default)]
132pub struct State {
133 pub(crate) overlay_state: color_picker::State,
135 pub(crate) old_show_picker: bool,
137}
138
139impl State {
140 #[must_use]
142 pub fn new(color: Color) -> Self {
143 Self {
144 overlay_state: color_picker::State::new(color),
145 old_show_picker: false,
146 }
147 }
148
149 pub fn reset(&mut self) {
151 self.overlay_state.color = Color::from_rgb(0.5, 0.25, 0.25);
152 self.overlay_state.color_bar_dragged = ColorBarDragged::None;
153 }
154
155 fn synchronize(&mut self, show_picker: bool, color: Color) {
161 if show_picker && (!self.old_show_picker || self.overlay_state.initial_color != color) {
162 self.overlay_state.force_synchronize(color);
163 }
164 self.old_show_picker = show_picker;
165 }
166}
167
168impl<'a, Message, Theme> Widget<Message, Theme, Renderer> for ColorPicker<'a, Message, Theme>
169where
170 Message: 'static + Clone,
171 Theme: 'a
172 + style::color_picker::Catalog
173 + iced_widget::button::Catalog
174 + iced_widget::text::Catalog,
175{
176 fn tag(&self) -> Tag {
177 Tag::of::<State>()
178 }
179
180 fn state(&self) -> tree::State {
181 tree::State::new(State::new(self.color))
182 }
183
184 fn children(&self) -> Vec<Tree> {
185 vec![Tree::new(&self.underlay), Tree::new(&self.overlay_state)]
186 }
187
188 fn diff(&self, tree: &mut Tree) {
189 let color_picker_state = tree.state.downcast_mut::<State>();
190
191 color_picker_state.synchronize(self.show_picker, self.color);
192
193 tree.diff_children(&[&self.underlay, &self.overlay_state]);
194 }
195
196 fn size(&self) -> iced_core::Size<Length> {
197 self.underlay.as_widget().size()
198 }
199
200 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
201 self.underlay
202 .as_widget_mut()
203 .layout(&mut tree.children[0], renderer, limits)
204 }
205
206 fn update(
207 &mut self,
208 state: &mut Tree,
209 event: &Event,
210 layout: Layout<'_>,
211 cursor: Cursor,
212 renderer: &Renderer,
213 clipboard: &mut dyn Clipboard,
214 shell: &mut Shell<'_, Message>,
215 viewport: &Rectangle,
216 ) {
217 self.underlay.as_widget_mut().update(
218 &mut state.children[0],
219 event,
220 layout,
221 cursor,
222 renderer,
223 clipboard,
224 shell,
225 viewport,
226 );
227 }
228
229 fn mouse_interaction(
230 &self,
231 state: &Tree,
232 layout: Layout<'_>,
233 cursor: Cursor,
234 viewport: &Rectangle,
235 renderer: &Renderer,
236 ) -> mouse::Interaction {
237 self.underlay.as_widget().mouse_interaction(
238 &state.children[0],
239 layout,
240 cursor,
241 viewport,
242 renderer,
243 )
244 }
245
246 fn draw(
247 &self,
248 state: &Tree,
249 renderer: &mut Renderer,
250 theme: &Theme,
251 style: &renderer::Style,
252 layout: Layout<'_>,
253 cursor: Cursor,
254 viewport: &Rectangle,
255 ) {
256 self.underlay.as_widget().draw(
257 &state.children[0],
258 renderer,
259 theme,
260 style,
261 layout,
262 cursor,
263 viewport,
264 );
265 }
266
267 fn overlay<'b>(
268 &'b mut self,
269 tree: &'b mut Tree,
270 layout: Layout<'b>,
271 renderer: &Renderer,
272 viewport: &Rectangle,
273 translation: Vector,
274 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
275 let picker_state: &mut State = tree.state.downcast_mut();
276
277 if !self.show_picker {
278 return self.underlay.as_widget_mut().overlay(
279 &mut tree.children[0],
280 layout,
281 renderer,
282 viewport,
283 translation,
284 );
285 }
286
287 let bounds = layout.bounds();
288 let position = Point::new(bounds.center_x(), bounds.center_y());
289
290 Some(
291 ColorPickerOverlay::new(
292 picker_state,
293 self.on_cancel.clone(),
294 &self.on_submit,
295 position,
296 &self.class,
297 &mut tree.children[1],
298 *viewport,
299 )
300 .overlay(),
301 )
302 }
303}
304
305impl<'a, Message, Theme> From<ColorPicker<'a, Message, Theme>>
306 for Element<'a, Message, Theme, Renderer>
307where
308 Message: 'static + Clone,
309 Theme: 'a
310 + style::color_picker::Catalog
311 + iced_widget::button::Catalog
312 + iced_widget::text::Catalog,
313{
314 fn from(color_picker: ColorPicker<'a, Message, Theme>) -> Self {
315 Element::new(color_picker)
316 }
317}