Skip to main content

native_windows_gui2/resources/
color_dialog.rs

1use crate::NwgError;
2use crate::controls::ControlHandle;
3use std::cell::RefCell;
4use std::pin::Pin;
5use std::{mem, ptr};
6use winapi::shared::{minwindef::DWORD, windef::COLORREF};
7use winapi::um::commdlg::{CC_RGBINIT, CHOOSECOLORW, ChooseColorW};
8use winapi::um::wingdi::{GetBValue, GetGValue, GetRValue, RGB};
9
10struct InnerColorDialog {
11    custom_colors: Pin<Box<[COLORREF; 16]>>,
12    dialog: CHOOSECOLORW,
13}
14
15/**
16 Displays a modal dialog box that allows the user to choose a specific color value.
17*/
18pub struct ColorDialog {
19    data: RefCell<InnerColorDialog>,
20}
21
22impl ColorDialog {
23    pub fn builder() -> ColorDialogBuilder {
24        ColorDialogBuilder {
25            default_colors: Default::default(),
26        }
27    }
28
29    /**
30    Execute the color dialog.
31    This function will return `true` if the user select a color or `false` if the dialog is cancelled
32    */
33    pub fn run<C: Into<ControlHandle>>(&self, owner: Option<C>) -> bool {
34        if owner.is_some() {
35            let ownder_handle = owner.unwrap().into();
36            self.data.borrow_mut().dialog.hwndOwner = ownder_handle
37                .hwnd()
38                .expect("Color dialog must be a window control");
39        }
40
41        unsafe {
42            let mut data = self.data.borrow_mut();
43            let data_ref = &mut data.dialog;
44            ChooseColorW(data_ref as *mut CHOOSECOLORW) > 0
45        }
46    }
47
48    /**
49    Return the color choosen by the user. The returned color is a [r, g, b] array.
50    If the dialog was never executed, this returns `[0, 0, 0]` (black);
51    */
52    pub fn color(&self) -> [u8; 3] {
53        let v = self.data.borrow().dialog.rgbResult;
54        [GetRValue(v), GetGValue(v), GetBValue(v)]
55    }
56
57    /**
58        Sets one of the saved color in the dialog. A dialog supports up to 16 colors (index: 0 to 15).
59
60        Panics:
61            - If the index is out of bound
62    */
63    pub fn set_saved_color(&self, index: usize, color: &[u8; 3]) {
64        if index > 15 {
65            panic!("{:?} is outside the dialog saved color bounds", index);
66        }
67        self.data.borrow_mut().custom_colors[index] = RGB(color[0], color[1], color[2]);
68    }
69
70    /**
71        Returns one of the saved color in the dialog. A dialog supports up to 16 colors (index: 0 to 15).
72
73        Panics:
74            - If the index is out of bound
75    */
76    pub fn saved_color(&self, index: usize) -> [u8; 3] {
77        if index > 15 {
78            panic!("{:?} is outside the dialog saved color bounds", index);
79        }
80        let v = self.data.borrow().custom_colors[index];
81        [GetRValue(v), GetGValue(v), GetBValue(v)]
82    }
83}
84
85/// The builder for a `ColorDialog` object. Use `ColorDialog::builder` to create one.
86pub struct ColorDialogBuilder {
87    default_colors: [COLORREF; 16],
88}
89
90impl ColorDialogBuilder {
91    pub fn saved_color(mut self, index: usize, color: &[u8; 3]) -> ColorDialogBuilder {
92        self.default_colors[index] = RGB(color[0], color[1], color[2]);
93        self
94    }
95
96    pub fn build(self, out: &mut ColorDialog) -> Result<(), NwgError> {
97        *out.data.borrow_mut().custom_colors.as_mut() = self.default_colors;
98        Ok(())
99    }
100}
101
102impl Default for ColorDialog {
103    fn default() -> ColorDialog {
104        let dialog = CHOOSECOLORW {
105            lStructSize: mem::size_of::<CHOOSECOLORW>() as DWORD,
106            hwndOwner: ptr::null_mut(),
107            hInstance: ptr::null_mut(),
108            rgbResult: 0,
109            lpCustColors: ptr::null_mut(),
110            Flags: CC_RGBINIT,
111            lCustData: 0,
112            lpfnHook: None,
113            lpTemplateName: ptr::null(),
114        };
115
116        let mut inner = InnerColorDialog {
117            custom_colors: Box::pin(Default::default()),
118            dialog,
119        };
120
121        let mut cols = inner.custom_colors.as_mut();
122        let cols_ref: &mut [COLORREF; 16] = &mut cols;
123        inner.dialog.lpCustColors = cols_ref as *mut [COLORREF; 16] as *mut COLORREF;
124
125        ColorDialog {
126            data: RefCell::new(inner),
127        }
128    }
129}