Skip to main content

partials/
partials.rs

1/*!
2    An application that load different interfaces using the partial feature.
3    Partials can be used to split large GUI application into smaller bits.
4
5    Requires the following features: `cargo run --example partials --features "listbox frame combobox flexbox"`
6*/
7
8extern crate native_windows_gui2 as nwg;
9use nwg::NativeUi;
10
11#[derive(Default)]
12pub struct PartialDemo {
13    window: nwg::Window,
14    layout: nwg::FlexboxLayout,
15    menu: nwg::ListBox<&'static str>,
16    frame1: nwg::Frame,
17    frame2: nwg::Frame,
18    frame3: nwg::Frame,
19
20    people_ui: PeopleUi,
21    animal_ui: AnimalUi,
22    food_ui: FoodUi,
23}
24
25impl PartialDemo {
26    fn change_interface(&self) {
27        self.frame1.set_visible(false);
28        self.frame2.set_visible(false);
29        self.frame3.set_visible(false);
30
31        let layout = &self.layout;
32        if layout.has_child(&self.frame1) {
33            layout.remove_child(&self.frame1);
34        }
35        if layout.has_child(&self.frame2) {
36            layout.remove_child(&self.frame2);
37        }
38        if layout.has_child(&self.frame3) {
39            layout.remove_child(&self.frame3);
40        }
41
42        use nwg::stretch::{
43            geometry::Size,
44            style::{Dimension as D, Style},
45        };
46        let mut style = Style::default();
47        style.size = Size {
48            width: D::Percent(1.0),
49            height: D::Auto,
50        };
51
52        match self.menu.selection() {
53            None | Some(0) => {
54                layout.add_child(&self.frame1, style).unwrap();
55                self.frame1.set_visible(true);
56            }
57            Some(1) => {
58                layout.add_child(&self.frame2, style).unwrap();
59                self.frame2.set_visible(true);
60            }
61            Some(2) => {
62                layout.add_child(&self.frame3, style).unwrap();
63                self.frame3.set_visible(true);
64            }
65            Some(_) => unreachable!(),
66        }
67    }
68
69    fn save(&self) {
70        nwg::simple_message("Saved!", "Data saved!");
71    }
72
73    fn exit(&self) {
74        nwg::stop_thread_dispatch();
75    }
76}
77
78#[derive(Default)]
79pub struct PeopleUi {
80    layout: nwg::GridLayout,
81    layout2: nwg::GridLayout,
82
83    label1: nwg::Label,
84    label2: nwg::Label,
85    label3: nwg::Label,
86
87    name_input: nwg::TextInput,
88    age_input: nwg::TextInput,
89    job_input: nwg::TextInput,
90
91    save_btn: nwg::Button,
92}
93
94#[derive(Default)]
95pub struct AnimalUi {
96    layout: nwg::GridLayout,
97    layout2: nwg::GridLayout,
98
99    label1: nwg::Label,
100    label2: nwg::Label,
101    label3: nwg::Label,
102
103    name_input: nwg::TextInput,
104    race_input: nwg::ComboBox<&'static str>,
105    is_soft_input: nwg::CheckBox,
106
107    save_btn: nwg::Button,
108}
109
110#[derive(Default)]
111pub struct FoodUi {
112    layout: nwg::GridLayout,
113    layout2: nwg::GridLayout,
114
115    label1: nwg::Label,
116    label2: nwg::Label,
117
118    name_input: nwg::TextInput,
119    tasty_input: nwg::CheckBox,
120
121    save_btn: nwg::Button,
122}
123
124//
125// ALL of this stuff is handled by native-windows-derive
126//
127mod partial_demo_ui {
128    use self::nwg::PartialUi;
129    use super::*;
130    use native_windows_gui2 as nwg;
131    use std::cell::RefCell;
132    use std::ops::Deref;
133    use std::rc::Rc;
134
135    pub struct PartialDemoUi {
136        inner: PartialDemo,
137        default_handler: RefCell<Vec<nwg::EventHandler>>,
138    }
139
140    impl nwg::NativeUi<Rc<PartialDemoUi>> for PartialDemo {
141        fn build_ui(mut data: PartialDemo) -> Result<Rc<PartialDemoUi>, nwg::NwgError> {
142            use nwg::Event as E;
143
144            // Controls
145            nwg::Window::builder()
146                .size((500, 400))
147                .position((300, 300))
148                .title("Many UI")
149                .build(&mut data.window)?;
150
151            nwg::ListBox::builder()
152                .collection(vec!["People", "Animals", "Food"])
153                .focus(true)
154                .parent(&data.window)
155                .build(&mut data.menu)?;
156
157            nwg::Frame::builder()
158                .parent(&data.window)
159                .build(&mut data.frame1)?;
160
161            nwg::Frame::builder()
162                .flags(nwg::FrameFlags::BORDER)
163                .parent(&data.window)
164                .build(&mut data.frame2)?;
165
166            nwg::Frame::builder()
167                .flags(nwg::FrameFlags::BORDER)
168                .parent(&data.window)
169                .build(&mut data.frame3)?;
170
171            // Partials
172            PeopleUi::build_partial(&mut data.people_ui, Some(&data.frame1))?;
173            AnimalUi::build_partial(&mut data.animal_ui, Some(&data.frame2))?;
174            FoodUi::build_partial(&mut data.food_ui, Some(&data.frame3))?;
175
176            // Wrap-up
177            let ui = Rc::new(PartialDemoUi {
178                inner: data,
179                default_handler: Default::default(),
180            });
181
182            // Events
183            let mut window_handles = vec![&ui.window.handle];
184            window_handles.append(&mut ui.people_ui.handles());
185            window_handles.append(&mut ui.animal_ui.handles());
186            window_handles.append(&mut ui.food_ui.handles());
187
188            for handle in window_handles.iter() {
189                let evt_ui = ui.clone();
190                let handle_events = move |evt, evt_data, handle| {
191                    evt_ui.people_ui.process_event(evt, &evt_data, handle);
192                    evt_ui.animal_ui.process_event(evt, &evt_data, handle);
193                    evt_ui.food_ui.process_event(evt, &evt_data, handle);
194
195                    match evt {
196                        E::OnListBoxSelect => {
197                            if &handle == &evt_ui.menu {
198                                PartialDemo::change_interface(&evt_ui.inner);
199                            }
200                        }
201                        E::OnWindowClose => {
202                            if &handle == &evt_ui.window {
203                                PartialDemo::exit(&evt_ui.inner);
204                            }
205                        }
206                        E::OnButtonClick => {
207                            if &handle == &evt_ui.people_ui.save_btn
208                                || &handle == &evt_ui.animal_ui.save_btn
209                                || &handle == &evt_ui.food_ui.save_btn
210                            {
211                                PartialDemo::save(&evt_ui.inner);
212                            }
213                        }
214                        _ => {}
215                    }
216                };
217
218                ui.default_handler
219                    .borrow_mut()
220                    .push(nwg::full_bind_event_handler(handle, handle_events));
221            }
222
223            // Layout
224            use nwg::stretch::{geometry::Size, style::Dimension as D};
225
226            nwg::FlexboxLayout::builder()
227                .parent(&ui.window)
228                .child(&ui.menu)
229                .child_size(Size {
230                    width: D::Percent(0.3),
231                    height: D::Auto,
232                })
233                .child(&ui.frame1)
234                .child_size(Size {
235                    width: D::Percent(1.0),
236                    height: D::Auto,
237                })
238                .build(&ui.layout)?;
239
240            return Ok(ui);
241        }
242    }
243
244    impl PartialDemoUi {
245        /// To make sure that everything is freed without issues, the default handler must be unbound.
246        pub fn destroy(&self) {
247            let mut handlers = self.default_handler.borrow_mut();
248            for handler in handlers.drain(0..) {
249                nwg::unbind_event_handler(&handler);
250            }
251        }
252    }
253
254    impl Deref for PartialDemoUi {
255        type Target = PartialDemo;
256
257        fn deref(&self) -> &PartialDemo {
258            &self.inner
259        }
260    }
261}
262
263mod partial_people_ui {
264    use self::nwg::{ControlHandle, NwgError, PartialUi};
265    use super::*;
266    use native_windows_gui2 as nwg;
267
268    impl PartialUi for PeopleUi {
269        fn build_partial<W: Into<ControlHandle>>(
270            data: &mut PeopleUi,
271            parent: Option<W>,
272        ) -> Result<(), NwgError> {
273            let parent = parent.unwrap().into();
274
275            nwg::Label::builder()
276                .text("Name:")
277                .h_align(nwg::HTextAlign::Right)
278                .parent(&parent)
279                .build(&mut data.label1)?;
280
281            nwg::Label::builder()
282                .text("Age:")
283                .h_align(nwg::HTextAlign::Right)
284                .parent(&parent)
285                .build(&mut data.label2)?;
286
287            nwg::Label::builder()
288                .text("Job:")
289                .h_align(nwg::HTextAlign::Right)
290                .parent(&parent)
291                .build(&mut data.label3)?;
292
293            nwg::TextInput::builder()
294                .text("John Doe")
295                .parent(&parent)
296                .build(&mut data.name_input)?;
297
298            nwg::TextInput::builder()
299                .text("75")
300                .flags(nwg::TextInputFlags::VISIBLE | nwg::TextInputFlags::NUMBER)
301                .parent(&parent)
302                .build(&mut data.age_input)?;
303
304            nwg::TextInput::builder()
305                .text("Programmer")
306                .parent(&parent)
307                .build(&mut data.job_input)?;
308
309            nwg::Button::builder()
310                .text("Save")
311                .parent(&parent)
312                .build(&mut data.save_btn)?;
313
314            nwg::GridLayout::builder()
315                .parent(&parent)
316                .max_size([1000, 150])
317                .min_size([100, 120])
318                .child(0, 0, &data.label1)
319                .child(0, 1, &data.label2)
320                .child(0, 2, &data.label3)
321                .child(1, 0, &data.name_input)
322                .child(1, 1, &data.age_input)
323                .child(1, 2, &data.job_input)
324                .build(&data.layout)?;
325
326            nwg::GridLayout::builder()
327                .min_size([100, 200])
328                .max_column(Some(2))
329                .max_row(Some(6))
330                .child(1, 5, &data.save_btn)
331                .parent(&parent)
332                .build(&data.layout2)?;
333
334            Ok(())
335        }
336
337        fn process_event<'a>(
338            &self,
339            _evt: nwg::Event,
340            _evt_data: &nwg::EventData,
341            _handle: ControlHandle,
342        ) {
343        }
344
345        fn handles(&self) -> Vec<&ControlHandle> {
346            Vec::new()
347        }
348    }
349}
350
351mod partial_animal_ui {
352    use self::nwg::{ControlHandle, NwgError, PartialUi};
353    use super::*;
354    use native_windows_gui2 as nwg;
355
356    impl PartialUi for AnimalUi {
357        fn build_partial<W: Into<ControlHandle>>(
358            data: &mut AnimalUi,
359            parent: Option<W>,
360        ) -> Result<(), NwgError> {
361            let parent = parent.unwrap().into();
362
363            nwg::Label::builder()
364                .text("Name:")
365                .h_align(nwg::HTextAlign::Right)
366                .parent(&parent)
367                .build(&mut data.label1)?;
368
369            nwg::Label::builder()
370                .text("Race:")
371                .h_align(nwg::HTextAlign::Right)
372                .parent(&parent)
373                .build(&mut data.label2)?;
374
375            nwg::Label::builder()
376                .text("Is fluffy:")
377                .h_align(nwg::HTextAlign::Right)
378                .parent(&parent)
379                .build(&mut data.label3)?;
380
381            nwg::TextInput::builder()
382                .text("Mittens")
383                .parent(&parent)
384                .build(&mut data.name_input)?;
385
386            nwg::ComboBox::builder()
387                .collection(vec!["Cat", "Dog", "Pidgeon", "Monkey"])
388                .selected_index(Some(0))
389                .parent(&parent)
390                .build(&mut data.race_input)?;
391
392            nwg::CheckBox::builder()
393                .text("")
394                .check_state(nwg::CheckBoxState::Checked)
395                .parent(&parent)
396                .build(&mut data.is_soft_input)?;
397
398            nwg::Button::builder()
399                .text("Save")
400                .parent(&parent)
401                .build(&mut data.save_btn)?;
402
403            nwg::GridLayout::builder()
404                .parent(&parent)
405                .max_size([1000, 150])
406                .min_size([100, 120])
407                .child(0, 0, &data.label1)
408                .child(0, 1, &data.label2)
409                .child(0, 2, &data.label3)
410                .child(1, 0, &data.name_input)
411                .child(1, 1, &data.race_input)
412                .child(1, 2, &data.is_soft_input)
413                .build(&data.layout)?;
414
415            nwg::GridLayout::builder()
416                .min_size([100, 200])
417                .max_column(Some(2))
418                .max_row(Some(6))
419                .child(1, 5, &data.save_btn)
420                .parent(&parent)
421                .build(&data.layout2)?;
422
423            Ok(())
424        }
425
426        fn process_event<'a>(
427            &self,
428            _evt: nwg::Event,
429            _evt_data: &nwg::EventData,
430            _handle: ControlHandle,
431        ) {
432        }
433
434        fn handles(&self) -> Vec<&ControlHandle> {
435            Vec::new()
436        }
437    }
438}
439
440mod partial_food_ui {
441    use self::nwg::{ControlHandle, NwgError, PartialUi};
442    use super::*;
443    use native_windows_gui2 as nwg;
444
445    impl PartialUi for FoodUi {
446        fn build_partial<W: Into<ControlHandle>>(
447            data: &mut FoodUi,
448            parent: Option<W>,
449        ) -> Result<(), NwgError> {
450            let parent = parent.unwrap().into();
451
452            nwg::Label::builder()
453                .text("Name:")
454                .h_align(nwg::HTextAlign::Right)
455                .parent(&parent)
456                .build(&mut data.label1)?;
457
458            nwg::Label::builder()
459                .text("Tasty:")
460                .h_align(nwg::HTextAlign::Right)
461                .parent(&parent)
462                .build(&mut data.label2)?;
463
464            nwg::TextInput::builder()
465                .text("Banana")
466                .parent(&parent)
467                .build(&mut data.name_input)?;
468
469            nwg::CheckBox::builder()
470                .text("")
471                .check_state(nwg::CheckBoxState::Checked)
472                .parent(&parent)
473                .build(&mut data.tasty_input)?;
474
475            nwg::Button::builder()
476                .text("Save")
477                .parent(&parent)
478                .build(&mut data.save_btn)?;
479
480            nwg::GridLayout::builder()
481                .parent(&parent)
482                .max_size([1000, 90])
483                .min_size([100, 80])
484                .child(0, 0, &data.label1)
485                .child(0, 1, &data.label2)
486                .child(1, 0, &data.name_input)
487                .child(1, 1, &data.tasty_input)
488                .build(&data.layout)?;
489
490            nwg::GridLayout::builder()
491                .min_size([100, 200])
492                .max_column(Some(2))
493                .max_row(Some(6))
494                .child(1, 5, &data.save_btn)
495                .parent(&parent)
496                .build(&data.layout2)?;
497
498            Ok(())
499        }
500
501        fn process_event<'a>(
502            &self,
503            _evt: nwg::Event,
504            _evt_data: &nwg::EventData,
505            _handle: ControlHandle,
506        ) {
507        }
508
509        fn handles(&self) -> Vec<&ControlHandle> {
510            Vec::new()
511        }
512    }
513}
514
515fn main() {
516    nwg::init().expect("Failed to init Native Windows GUI");
517    nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");
518
519    let ui = PartialDemo::build_ui(Default::default()).expect("Failed to build UI");
520    nwg::dispatch_thread_events();
521    ui.destroy();
522}