basic/test/
widgets.rs

1//! https://github.com/compio-rs/winio/blob/ca97049907a0151168100365ce5e13410f508792/winio/examples/widgets.rs
2//!
3//! Legacy style: https://github.com/Chaoses-Ib/IbEverythingLib/blob/c8e6e5f175cff2b4ff2e93acf4c265e9c59ddb39/everything-plugin/examples/test/widgets.rs
4
5use everything_plugin::ui::winio::prelude::*;
6
7use crate::{App, HANDLER};
8
9#[allow(dead_code)]
10fn main() {
11    // #[cfg(feature = "enable_log")]
12    // tracing_subscriber::fmt()
13    //     .with_max_level(compio_log::Level::INFO)
14    //     .init();
15
16    winio::ui::App::new("rs.compio.winio.widgets").run::<MainModel>(());
17}
18
19pub struct MainModel {
20    window: Child<Window>,
21    ulabel: Child<Label>,
22    plabel: Child<Label>,
23    uentry: Child<Edit>,
24    pentry: Child<Edit>,
25    pcheck: Child<CheckBox>,
26    canvas: Child<Canvas>,
27    combo: Child<ComboBox>,
28    list: Child<ObservableVec<String>>,
29    index: Option<usize>,
30    r1: Child<RadioButton>,
31    r2: Child<RadioButton>,
32    r3: Child<RadioButton>,
33    rindex: usize,
34    push_button: Child<Button>,
35    pop_button: Child<Button>,
36    show_button: Child<Button>,
37    progress: Child<Progress>,
38    mltext: Child<TextBox>,
39}
40
41#[derive(Debug)]
42pub enum MainMessage {
43    Noop,
44    Close,
45    Redraw,
46    List(ObservableVecEvent<String>),
47    Select,
48    Push,
49    Pop,
50    Show,
51    RSelect(usize),
52    PasswordCheck,
53    OptionsPage(OptionsPageMessage<App>),
54}
55
56impl From<OptionsPageMessage<App>> for MainMessage {
57    fn from(value: OptionsPageMessage<App>) -> Self {
58        Self::OptionsPage(value)
59    }
60}
61
62impl Component for MainModel {
63    type Event = ();
64    type Init<'a> = OptionsPageInit<'a, App>;
65    type Message = MainMessage;
66
67    fn init(mut init: Self::Init<'_>, sender: &ComponentSender<Self>) -> Self {
68        // let mut window = Child::<Window>::init(init);
69        let mut window = init.window(sender);
70        window.set_text("Widgets example");
71        window.set_size(Size::new(800.0, 600.0));
72        init! {
73            canvas: Canvas = (&window),
74            ulabel: Label = (&window) => {
75                text: "Username:",
76                halign: HAlign::Right,
77            },
78            plabel: Label = (&window) => {
79                text: "Password:",
80                halign: HAlign::Right,
81            },
82            uentry: Edit = (&window) => {
83                text: "AAA",
84            },
85            pentry: Edit = (&window) => {
86                text: "123456",
87                password: true,
88            },
89            pcheck: CheckBox = (&window) => {
90                text: "Show",
91                checked: false,
92            },
93            combo: ComboBox = (&window),
94            list: ObservableVec<String> = (()) => {
95                // https://www.zhihu.com/question/23600507/answer/140640887
96                items: [
97                    "烫烫烫",
98                    "昍昍昍",
99                    "フフフフフフ",
100                    "쳌쳌쳌"
101                ],
102            },
103            r1: RadioButton = (&window) => {
104                text: "屯屯屯",
105                checked: true,
106            },
107            r2: RadioButton = (&window) => {
108                text: "锟斤拷",
109            },
110            r3: RadioButton = (&window) => {
111                text: "╠╠╠"
112            },
113            push_button: Button = (&window) => {
114                text: "Push",
115            },
116            pop_button: Button = (&window) => {
117                text: "Pop",
118            },
119            show_button: Button = (&window) => {
120                text: "Show",
121            },
122            progress: Progress = (&window) => {
123                indeterminate: true,
124            },
125            mltext: TextBox = (&window) => {
126            },
127        }
128        HANDLER.with_app(|a| mltext.set_text(&a.config().s));
129
130        window.show();
131
132        Self {
133            window,
134            ulabel,
135            plabel,
136            uentry,
137            pentry,
138            pcheck,
139            canvas,
140            combo,
141            list,
142            index: None,
143            r1,
144            r2,
145            r3,
146            rindex: 0,
147            push_button,
148            pop_button,
149            show_button,
150            progress,
151            mltext,
152        }
153    }
154
155    async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
156        let mut radio_group = RadioButtonGroup::new([&mut *self.r1, &mut self.r2, &mut self.r3]);
157        start! {
158            sender, default: MainMessage::Noop,
159            self.window => {
160                WindowEvent::Close => MainMessage::Close,
161                WindowEvent::Resize => MainMessage::Redraw,
162            },
163            self.pcheck => {
164                CheckBoxEvent::Click => MainMessage::PasswordCheck,
165            },
166            self.combo => {
167                ComboBoxEvent::Select => MainMessage::Select,
168            },
169            self.push_button => {
170                ButtonEvent::Click => MainMessage::Push,
171            },
172            self.pop_button => {
173                ButtonEvent::Click => MainMessage::Pop,
174            },
175            self.show_button => {
176                ButtonEvent::Click => MainMessage::Show,
177            },
178            self.list => {
179                e => MainMessage::List(e),
180            },
181            radio_group => {
182                |i| Some(MainMessage::RSelect(i))
183            }
184        }
185    }
186
187    async fn update(&mut self, message: Self::Message, sender: &ComponentSender<Self>) -> bool {
188        futures_util::future::join(self.window.update(), self.canvas.update()).await;
189        match message {
190            MainMessage::Noop => false,
191            MainMessage::Close => {
192                sender.output(());
193                false
194            }
195            MainMessage::Redraw => true,
196            MainMessage::PasswordCheck => {
197                self.pentry.set_password(!self.pcheck.is_checked());
198                true
199            }
200            MainMessage::List(e) => {
201                self.pop_button.set_enabled(!self.list.is_empty());
202                self.combo
203                    .emit(ComboBoxMessage::from_observable_vec_event(e))
204                    .await
205            }
206            MainMessage::Select => {
207                self.index = self.combo.selection();
208                false
209            }
210            MainMessage::Push => {
211                self.list.push(
212                    match self.rindex {
213                        0 => &self.r1,
214                        1 => &self.r2,
215                        2 => &self.r3,
216                        _ => unreachable!(),
217                    }
218                    .text(),
219                );
220                false
221            }
222            MainMessage::Pop => {
223                self.list.pop();
224                false
225            }
226            MainMessage::RSelect(i) => {
227                self.rindex = i;
228                false
229            }
230            MainMessage::Show => {
231                MessageBox::new()
232                    .title("Show selected item")
233                    .message(
234                        self.index
235                            .and_then(|index| self.list.get(index))
236                            .map(|s| s.as_str())
237                            .unwrap_or("No selection."),
238                    )
239                    .buttons(MessageBoxButton::Ok)
240                    .show(&self.window)
241                    .await;
242                false
243            }
244            MainMessage::OptionsPage(m) => {
245                tracing::debug!(?m, "Options page message");
246                match m {
247                    OptionsPageMessage::Save(config, tx) => {
248                        config.s = self.mltext.text();
249                        tx.send(config).unwrap()
250                    }
251                }
252                false
253            }
254        }
255    }
256
257    fn render(&mut self, _sender: &ComponentSender<Self>) {
258        let csize = self.window.client_size();
259        {
260            let mut cred_panel = layout! {
261                Grid::from_str("auto,1*,auto", "1*,auto,auto,1*").unwrap(),
262                self.ulabel => { column: 0, row: 1, valign: VAlign::Center },
263                self.uentry => { column: 1, row: 1, margin: Margin::new_all_same(4.0) },
264                self.plabel => { column: 0, row: 2, valign: VAlign::Center },
265                self.pentry => { column: 1, row: 2, margin: Margin::new_all_same(4.0) },
266                self.pcheck => { column: 2, row: 2 },
267            };
268
269            let mut rgroup_panel = layout! {
270                Grid::from_str("auto", "1*,auto,auto,auto,1*").unwrap(),
271                self.r1 => { row: 1 },
272                self.r2 => { row: 2 },
273                self.r3 => { row: 3 },
274            };
275
276            let mut buttons_panel = layout! {
277                StackPanel::new(Orient::Vertical),
278                self.push_button => { margin: Margin::new_all_same(4.0) },
279                self.pop_button  => { margin: Margin::new_all_same(4.0) },
280                self.show_button => { margin: Margin::new_all_same(4.0) },
281            };
282
283            let mut root_panel = layout! {
284                Grid::from_str("1*,1*,1*", "1*,auto,1*").unwrap(),
285                cred_panel    => { column: 1, row: 0 },
286                rgroup_panel  => { column: 2, row: 0, halign: HAlign::Center },
287                self.canvas   => { column: 0, row: 1, row_span: 2 },
288                self.combo    => { column: 1, row: 1, halign: HAlign::Center },
289                self.progress => { column: 2, row: 1 },
290                self.mltext   => { column: 1, row: 2, margin: Margin::new_all_same(8.0) },
291                buttons_panel => { column: 2, row: 2 },
292            };
293
294            root_panel.set_size(csize);
295        }
296
297        let size = self.canvas.size();
298        let is_dark = ColorTheme::current() == ColorTheme::Dark;
299        let back_color = if is_dark {
300            Color::new(255, 255, 255, 255)
301        } else {
302            Color::new(0, 0, 0, 255)
303        };
304        let brush = SolidColorBrush::new(back_color);
305        let pen = BrushPen::new(&brush, 1.0);
306        let mut ctx = self.canvas.context();
307        let cx = size.width / 2.0;
308        let cy = size.height / 2.0;
309        let r = cx.min(cy) - 2.0;
310        ctx.draw_pie(
311            &pen,
312            Rect::new(Point::new(cx - r, cy - r), Size::new(r * 2.0, r * 2.0)),
313            std::f64::consts::PI,
314            std::f64::consts::PI * 2.0,
315        );
316
317        let brush2 = LinearGradientBrush::new(
318            [
319                GradientStop::new(Color::new(0x87, 0xCE, 0xEB, 0xFF), 0.0),
320                GradientStop::new(back_color, 1.0),
321            ],
322            RelativePoint::zero(),
323            RelativePoint::new(0.0, 1.0),
324        );
325        let pen2 = BrushPen::new(&brush2, 1.0);
326        ctx.draw_round_rect(
327            &pen2,
328            Rect::new(
329                Point::new(cx - r - 1.0, cy - r - 1.0),
330                Size::new(r * 2.0 + 2.0, r * 1.618 + 2.0),
331            ),
332            Size::new(r / 10.0, r / 10.0),
333        );
334        let mut path = ctx.create_path_builder(Point::new(cx + r + 1.0 - r / 10.0, cy));
335        path.add_arc(
336            Point::new(cx, cy + r * 0.618 + 1.0),
337            Size::new(r + 1.0 - r / 10.0, r * 0.382 / 2.0),
338            0.0,
339            std::f64::consts::PI,
340            true,
341        );
342        path.add_line(Point::new(cx - r - 1.0 + r / 10.0, cy));
343        let path = path.build(false);
344        ctx.draw_path(&pen, &path);
345        let brush3 = RadialGradientBrush::new(
346            [
347                GradientStop::new(Color::new(0xF5, 0xF5, 0xF5, 0xFF), 0.0),
348                GradientStop::new(
349                    Color::accent().unwrap_or(Color::new(0xFF, 0xC0, 0xCB, 0xFF)),
350                    1.0,
351                ),
352            ],
353            RelativePoint::new(0.5, 0.5),
354            RelativePoint::new(0.2, 0.5),
355            RelativeSize::new(0.5, 0.5),
356        );
357        let font = DrawingFontBuilder::new()
358            .family("Arial")
359            .size(r / 5.0)
360            .halign(HAlign::Center)
361            .valign(VAlign::Bottom)
362            .build();
363        ctx.draw_str(&brush3, font, Point::new(cx, cy), "Hello world!");
364    }
365}