1
//! Rectangular input that displays a color and invokes a callback when clicked
2

            
3
use azul_core::{
4
    callbacks::Update,
5
    dom::Dom,
6
    refany::RefAny,
7
};
8
use azul_css::dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec};
9
use azul_css::{
10
    props::{
11
        basic::*,
12
        layout::*,
13
        property::{CssProperty, *},
14
        style::*,
15
    },
16
    *,
17
};
18

            
19
use crate::callbacks::{Callback, CallbackInfo};
20

            
21
/// Rectangular input that displays a color and triggers a callback when clicked.
22
#[derive(Debug, Default, Clone, PartialEq)]
23
#[repr(C)]
24
pub struct ColorInput {
25
    pub color_input_state: ColorInputStateWrapper,
26
    pub style: CssPropertyWithConditionsVec,
27
}
28

            
29
/// Callback function type invoked when the color input value changes.
30
pub type ColorInputOnValueChangeCallbackType =
31
    extern "C" fn(RefAny, CallbackInfo, ColorInputState) -> Update;
32
impl_widget_callback!(
33
    ColorInputOnValueChange,
34
    OptionColorInputOnValueChange,
35
    ColorInputOnValueChangeCallback,
36
    ColorInputOnValueChangeCallbackType
37
);
38

            
39
azul_core::impl_managed_callback! {
40
    wrapper:        ColorInputOnValueChangeCallback,
41
    info_ty:        CallbackInfo,
42
    return_ty:      Update,
43
    default_ret:    Update::DoNothing,
44
    invoker_static: COLOR_INPUT_ON_VALUE_CHANGE_INVOKER,
45
    invoker_ty:     AzColorInputOnValueChangeCallbackInvoker,
46
    thunk_fn:       az_color_input_on_value_change_callback_thunk,
47
    setter_fn:      AzApp_setColorInputOnValueChangeCallbackInvoker,
48
    from_handle_fn: AzColorInputOnValueChangeCallback_createFromHostHandle,
49
    extra_args:     [ state: ColorInputState ],
50
}
51

            
52
/// Wrapper around [`ColorInputState`] that includes a title and an optional value-change callback.
53
#[derive(Debug, Clone, PartialEq, PartialOrd)]
54
#[repr(C)]
55
pub struct ColorInputStateWrapper {
56
    pub inner: ColorInputState,
57
    pub title: AzString,
58
    pub on_value_change: OptionColorInputOnValueChange,
59
}
60

            
61
impl Default for ColorInputStateWrapper {
62
    fn default() -> Self {
63
        Self {
64
            inner: ColorInputState::default(),
65
            title: AzString::from_const_str("Pick color"),
66
            on_value_change: None.into(),
67
        }
68
    }
69
}
70

            
71
/// Holds the current color value of a [`ColorInput`] widget.
72
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
73
#[repr(C)]
74
pub struct ColorInputState {
75
    pub color: ColorU,
76
}
77

            
78
impl Default for ColorInputState {
79
    fn default() -> Self {
80
        Self {
81
            color: ColorU {
82
                r: 255,
83
                g: 255,
84
                b: 255,
85
                a: 255,
86
            },
87
        }
88
    }
89
}
90

            
91
static DEFAULT_COLOR_INPUT_STYLE: &[CssPropertyWithConditions] = &[
92
    CssPropertyWithConditions::simple(CssProperty::const_display(LayoutDisplay::Block)),
93
    CssPropertyWithConditions::simple(CssProperty::const_flex_grow(LayoutFlexGrow::const_new(0))),
94
    CssPropertyWithConditions::simple(CssProperty::const_width(LayoutWidth::const_px(14))),
95
    CssPropertyWithConditions::simple(CssProperty::const_height(LayoutHeight::const_px(14))),
96
    CssPropertyWithConditions::simple(CssProperty::const_cursor(StyleCursor::Pointer)),
97
];
98

            
99
impl ColorInput {
100
    /// Creates a new `ColorInput` displaying the given color.
101
    #[inline]
102
    #[must_use]
103
    pub fn create(color: ColorU) -> Self {
104
        Self {
105
            color_input_state: ColorInputStateWrapper {
106
                inner: ColorInputState { color },
107
                ..Default::default()
108
            },
109
            style: CssPropertyWithConditionsVec::from_const_slice(DEFAULT_COLOR_INPUT_STYLE),
110
        }
111
    }
112

            
113
    /// Sets the callback invoked when the color value changes.
114
    #[inline]
115
    pub fn set_on_value_change<I: Into<ColorInputOnValueChangeCallback>>(
116
        &mut self,
117
        data: RefAny,
118
        callback: I,
119
    ) {
120
        self.color_input_state.on_value_change = Some(ColorInputOnValueChange {
121
            callback: callback.into(),
122
            refany: data,
123
        })
124
        .into();
125
    }
126

            
127
    /// Builder-style method to set the value-change callback.
128
    #[inline]
129
    #[must_use]
130
    pub fn with_on_value_change<C: Into<ColorInputOnValueChangeCallback>>(
131
        mut self,
132
        data: RefAny,
133
        callback: C,
134
    ) -> Self {
135
        self.set_on_value_change(data, callback);
136
        self
137
    }
138

            
139
    /// Replaces `self` with a default `ColorInput` and returns the previous value.
140
    #[inline]
141
    #[must_use]
142
    pub fn swap_with_default(&mut self) -> Self {
143
        let mut s = Self::default();
144
        core::mem::swap(&mut s, self);
145
        s
146
    }
147

            
148
    /// Converts this `ColorInput` into a styled [`Dom`] node with a click callback.
149
    #[inline]
150
    #[must_use]
151
    pub fn dom(self) -> Dom {
152
        use azul_core::{
153
            callbacks::{CoreCallback, CoreCallbackData},
154
            dom::{EventFilter, HoverEventFilter, IdOrClass::Class},
155
        };
156

            
157
        let mut style = self.style.into_library_owned_vec();
158
        style.push(CssPropertyWithConditions::simple(
159
            CssProperty::const_background_content(
160
                vec![StyleBackgroundContent::Color(
161
                    self.color_input_state.inner.color,
162
                )]
163
                .into(),
164
            ),
165
        ));
166

            
167
        Dom::create_div()
168
            .with_ids_and_classes(vec![Class("__azul_native_color_input".into())].into())
169
            .with_css_props(style.into())
170
            .with_callbacks(
171
                vec![CoreCallbackData {
172
                    event: EventFilter::Hover(HoverEventFilter::MouseUp),
173
                    refany: RefAny::new(self.color_input_state),
174
                    callback: CoreCallback {
175
                        cb: on_color_input_clicked as usize,
176
                        ctx: azul_core::refany::OptionRefAny::None,
177
                    },
178
                }]
179
                .into(),
180
            )
181
    }
182
}
183

            
184
extern "C" fn on_color_input_clicked(mut data: RefAny, mut info: CallbackInfo) -> Update {
185
    let mut color_input = match data.downcast_mut::<ColorInputStateWrapper>() {
186
        Some(s) => s,
187
        None => return Update::DoNothing,
188
    };
189

            
190
    // No built-in color picker dialog — the on_value_change callback
191
    // receives the current color so the caller can open their own picker.
192
    let color_input = &mut *color_input;
193
    let onvaluechange = &mut color_input.on_value_change;
194
    let inner = color_input.inner.clone();
195

            
196
    match onvaluechange.as_mut() {
197
        Some(ColorInputOnValueChange {
198
            callback,
199
            refany: data,
200
        }) => (callback.cb)(data.clone(), info.clone(), inner),
201
        None => Update::DoNothing,
202
    }
203
}