Skip to main content

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