Skip to main content

azul_layout/widgets/
color_input.rs

1//! Rectangular input that, when clicked, spawns a color dialog
2
3use azul_core::{
4    callbacks::{CoreCallbackData, Update},
5    dom::Dom,
6    refany::RefAny,
7};
8use azul_css::dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec};
9use azul_css::{
10    props::{
11        basic::*,
12        layout::*,
13        property::{CssProperty, *},
14        style::*,
15    },
16    *,
17};
18
19use crate::callbacks::{Callback, CallbackInfo};
20
21#[derive(Debug, Default, Clone, PartialEq)]
22#[repr(C)]
23pub struct ColorInput {
24    pub color_input_state: ColorInputStateWrapper,
25    pub style: CssPropertyWithConditionsVec,
26}
27
28pub type ColorInputOnValueChangeCallbackType =
29    extern "C" fn(RefAny, CallbackInfo, ColorInputState) -> Update;
30impl_widget_callback!(
31    ColorInputOnValueChange,
32    OptionColorInputOnValueChange,
33    ColorInputOnValueChangeCallback,
34    ColorInputOnValueChangeCallbackType
35);
36
37#[derive(Debug, Clone, PartialEq, PartialOrd)]
38#[repr(C)]
39pub struct ColorInputStateWrapper {
40    pub inner: ColorInputState,
41    pub title: AzString,
42    pub on_value_change: OptionColorInputOnValueChange,
43}
44
45impl Default for ColorInputStateWrapper {
46    fn default() -> Self {
47        Self {
48            inner: ColorInputState::default(),
49            title: AzString::from_const_str("Pick color"),
50            on_value_change: None.into(),
51        }
52    }
53}
54
55#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
56#[repr(C)]
57pub struct ColorInputState {
58    pub color: ColorU,
59}
60
61impl Default for ColorInputState {
62    fn default() -> Self {
63        Self {
64            color: ColorU {
65                r: 255,
66                g: 255,
67                b: 255,
68                a: 255,
69            },
70        }
71    }
72}
73
74static DEFAULT_COLOR_INPUT_STYLE: &[CssPropertyWithConditions] = &[
75    CssPropertyWithConditions::simple(CssProperty::const_display(LayoutDisplay::Block)),
76    CssPropertyWithConditions::simple(CssProperty::const_flex_grow(LayoutFlexGrow::const_new(0))),
77    CssPropertyWithConditions::simple(CssProperty::const_width(LayoutWidth::const_px(14))),
78    CssPropertyWithConditions::simple(CssProperty::const_height(LayoutHeight::const_px(14))),
79    CssPropertyWithConditions::simple(CssProperty::const_cursor(StyleCursor::Pointer)),
80];
81
82impl ColorInput {
83    #[inline]
84    pub fn create(color: ColorU) -> Self {
85        Self {
86            color_input_state: ColorInputStateWrapper {
87                inner: ColorInputState {
88                    color,
89                    ..Default::default()
90                },
91                ..Default::default()
92            },
93            style: CssPropertyWithConditionsVec::from_const_slice(DEFAULT_COLOR_INPUT_STYLE),
94        }
95    }
96
97    #[inline]
98    pub fn set_on_value_change<I: Into<ColorInputOnValueChangeCallback>>(
99        &mut self,
100        data: RefAny,
101        callback: I,
102    ) {
103        self.color_input_state.on_value_change = Some(ColorInputOnValueChange {
104            callback: callback.into(),
105            refany: data,
106        })
107        .into();
108    }
109
110    #[inline]
111    pub fn with_on_value_change<C: Into<ColorInputOnValueChangeCallback>>(
112        mut self,
113        data: RefAny,
114        callback: C,
115    ) -> Self {
116        self.set_on_value_change(data, callback);
117        self
118    }
119
120    #[inline]
121    pub fn swap_with_default(&mut self) -> Self {
122        let mut s = Self::default();
123        core::mem::swap(&mut s, self);
124        s
125    }
126
127    #[inline]
128    pub fn dom(self) -> Dom {
129        use azul_core::{
130            callbacks::{CoreCallback, CoreCallbackData},
131            dom::{EventFilter, HoverEventFilter, IdOrClass::Class},
132        };
133
134        let mut style = self.style.into_library_owned_vec();
135        style.push(CssPropertyWithConditions::simple(
136            CssProperty::const_background_content(
137                vec![StyleBackgroundContent::Color(
138                    self.color_input_state.inner.color,
139                )]
140                .into(),
141            ),
142        ));
143
144        Dom::create_div()
145            .with_ids_and_classes(vec![Class("__azul_native_color_input".into())].into())
146            .with_css_props(style.into())
147            .with_callbacks(
148                vec![CoreCallbackData {
149                    event: EventFilter::Hover(HoverEventFilter::MouseUp),
150                    refany: RefAny::new(self.color_input_state),
151                    callback: CoreCallback {
152                        cb: on_color_input_clicked as usize,
153                        ctx: azul_core::refany::OptionRefAny::None,
154                    },
155                }]
156                .into(),
157            )
158    }
159}
160
161extern "C" fn on_color_input_clicked(mut data: RefAny, mut info: CallbackInfo) -> Update {
162    let mut color_input = match data.downcast_mut::<ColorInputStateWrapper>() {
163        Some(s) => s,
164        None => return Update::DoNothing,
165    };
166
167    // Color picker dialog is not available in azul_layout
168    // The user must provide their own color picker callback via on_value_change
169    // For now, just trigger the callback with the current color
170    let result = {
171        let color_input = &mut *color_input;
172        let onvaluechange = &mut color_input.on_value_change;
173        let inner = color_input.inner.clone();
174
175        match onvaluechange.as_mut() {
176            Some(ColorInputOnValueChange {
177                callback,
178                refany: data,
179            }) => (callback.cb)(data.clone(), info.clone(), inner),
180            None => Update::DoNothing,
181        }
182    };
183
184    result
185}