use iced_graphics::{Backend, Renderer};
use iced_native::{
event, mouse, overlay, renderer, widget::button, Clipboard, Color, Event, Layout, Length,
Point, Rectangle, Shell,
};
use iced_native::{
widget::{
tree::{self, Tag},
Tree,
},
Element, Widget,
};
pub use crate::style::color_picker::{Appearance, StyleSheet};
use super::overlay::color_picker::{
self, ColorBarDragged, ColorPickerOverlay, ColorPickerOverlayButtons,
};
#[allow(missing_debug_implementations)]
pub struct ColorPicker<'a, Message, B, Theme>
where
Message: Clone,
B: Backend + iced_graphics::backend::Text,
Theme: StyleSheet + button::StyleSheet,
{
show_picker: bool,
color: Color,
underlay: Element<'a, Message, Renderer<B, Theme>>,
on_cancel: Message,
on_submit: Box<dyn Fn(Color) -> Message>,
style: <Theme as StyleSheet>::Style,
overlay_state: Element<'a, Message, Renderer<B, Theme>>,
}
impl<'a, Message, B, Theme> ColorPicker<'a, Message, B, Theme>
where
Message: 'a + Clone,
B: 'a + Backend + iced_graphics::backend::Text,
Theme: 'a + StyleSheet + button::StyleSheet + iced_style::text::StyleSheet,
{
pub fn new<U, F>(
show_picker: bool,
color: Color,
underlay: U,
on_cancel: Message,
on_submit: F,
) -> Self
where
U: Into<Element<'a, Message, Renderer<B, Theme>>>,
F: 'static + Fn(Color) -> Message,
{
Self {
show_picker,
color,
underlay: underlay.into(),
on_cancel,
on_submit: Box::new(on_submit),
style: <Theme as StyleSheet>::Style::default(),
overlay_state: ColorPickerOverlayButtons::default().into(),
}
}
#[must_use]
pub fn style(mut self, style: <Theme as StyleSheet>::Style) -> Self {
self.style = style;
self
}
}
#[derive(Debug, Default)]
pub struct State {
pub(crate) overlay_state: color_picker::State,
}
impl State {
#[must_use]
pub fn new(color: Color) -> Self {
Self {
overlay_state: color_picker::State::new(color),
}
}
pub fn reset(&mut self) {
self.overlay_state.color = Color::from_rgb(0.5, 0.25, 0.25);
self.overlay_state.color_bar_dragged = ColorBarDragged::None;
}
}
impl<'a, Message, B, Theme> Widget<Message, Renderer<B, Theme>>
for ColorPicker<'a, Message, B, Theme>
where
Message: 'static + Clone,
B: 'a + Backend + iced_graphics::backend::Text,
Theme: StyleSheet + button::StyleSheet + iced_style::text::StyleSheet,
{
fn tag(&self) -> Tag {
Tag::of::<State>()
}
fn state(&self) -> tree::State {
tree::State::new(State::new(self.color))
}
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.underlay), Tree::new(&self.overlay_state)]
}
fn diff(&self, tree: &mut Tree) {
tree.diff_children(&[&self.underlay, &self.overlay_state]);
}
fn width(&self) -> Length {
self.underlay.as_widget().width()
}
fn height(&self) -> Length {
self.underlay.as_widget().width()
}
fn layout(
&self,
renderer: &Renderer<B, Theme>,
limits: &iced_native::layout::Limits,
) -> iced_native::layout::Node {
self.underlay.as_widget().layout(renderer, limits)
}
fn on_event(
&mut self,
state: &mut Tree,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer<B, Theme>,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.underlay.as_widget_mut().on_event(
&mut state.children[0],
event,
layout,
cursor_position,
renderer,
clipboard,
shell,
)
}
fn mouse_interaction(
&self,
state: &Tree,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
renderer: &Renderer<B, Theme>,
) -> mouse::Interaction {
self.underlay.as_widget().mouse_interaction(
&state.children[0],
layout,
cursor_position,
viewport,
renderer,
)
}
fn draw(
&self,
state: &iced_native::widget::Tree,
renderer: &mut Renderer<B, Theme>,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
) {
self.underlay.as_widget().draw(
&state.children[0],
renderer,
theme,
style,
layout,
cursor_position,
viewport,
);
}
fn overlay<'b>(
&'b mut self,
state: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer<B, Theme>,
) -> Option<overlay::Element<'b, Message, Renderer<B, Theme>>> {
let picker_state: &mut State = state.state.downcast_mut();
if !self.show_picker {
return self
.underlay
.as_widget_mut()
.overlay(&mut state.children[0], layout, renderer);
}
let bounds = layout.bounds();
let position = Point::new(bounds.center_x(), bounds.center_y());
Some(
ColorPickerOverlay::new(
picker_state,
self.on_cancel.clone(),
&self.on_submit,
position,
self.style,
&mut state.children[1],
)
.overlay(),
)
}
}
impl<'a, Message, B, Theme> From<ColorPicker<'a, Message, B, Theme>>
for Element<'a, Message, Renderer<B, Theme>>
where
Message: 'static + Clone,
B: 'a + Backend + iced_graphics::backend::Text,
Theme: 'a + StyleSheet + button::StyleSheet + iced_style::text::StyleSheet,
{
fn from(color_picker: ColorPicker<'a, Message, B, Theme>) -> Self {
Element::new(color_picker)
}
}