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