Skip to main content

inputs/
inputs.rs

1//! Demonstrates a mutable application state manipulated over a number of UIs
2
3extern crate libui;
4use libui::controls::{
5    Entry, Group, HorizontalBox, HorizontalSeparator, Label, MultilineEntry, PasswordEntry,
6    ProgressBar, Slider, Spacer, Spinbox, VerticalBox,
7};
8use libui::prelude::*;
9use std::cell::RefCell;
10use std::rc::Rc;
11
12/// This struct will hold the values that multiple callbacks will need to access.
13struct State {
14    slider_val: i32,
15    spinner_val: i32,
16    entry_val: String,
17    password_val: String,
18    multi_val: String,
19}
20
21fn main() {
22    // Initialize the UI framework.
23    let ui = UI::init().unwrap();
24
25    // Initialize the state of the application.
26    let state = Rc::new(RefCell::new(State {
27        slider_val: 1,
28        spinner_val: 1,
29        entry_val: "".into(),
30        password_val: "".into(),
31        multi_val: "".into(),
32    }));
33
34    // Set up the inputs for the application.
35    // While it's not necessary to create a block for this, it makes the code a lot easier
36    // to read; the indentation presents a visual cue informing the reader that these
37    // statements are related.
38    let (input_group, mut slider, mut spinner, mut entry, mut password, mut multi) = {
39        // The group will hold all the inputs
40        let mut input_group = Group::new("Inputs");
41        // The vertical box arranges the inputs within the groups
42        let mut input_vbox = VerticalBox::new();
43        input_vbox.set_padded(true);
44        // Numerical inputs
45        let slider = Slider::new(1, 100);
46        let spinner = Spinbox::new(1, 100);
47        let entry = Entry::new();
48        let password = PasswordEntry::new();
49        let multi = MultilineEntry::new();
50        // Add everything in hierarchy
51        // Note the reverse order here. Again, it's not necessary, but it improves
52        // readability.
53        input_vbox.append(slider.clone(), LayoutStrategy::Compact);
54        input_vbox.append(spinner.clone(), LayoutStrategy::Compact);
55        input_vbox.append(Spacer::new(), LayoutStrategy::Compact);
56        input_vbox.append(HorizontalSeparator::new(), LayoutStrategy::Compact);
57        input_vbox.append(Spacer::new(), LayoutStrategy::Compact);
58        input_vbox.append(entry.clone(), LayoutStrategy::Compact);
59        input_vbox.append(password.clone(), LayoutStrategy::Compact);
60        input_vbox.append(multi.clone(), LayoutStrategy::Stretchy);
61        input_group.set_child(input_vbox);
62        (input_group, slider, spinner, entry, password, multi)
63    };
64
65    // Set up the outputs for the application. Organization is very similar to the
66    // previous setup.
67    let (
68        output_group,
69        add_label,
70        sub_label,
71        text_label,
72        password_label,
73        bigtext_label,
74        progress_bar,
75    ) = {
76        let mut output_group = Group::new("Outputs");
77        let mut output_vbox = VerticalBox::new();
78        let add_label = Label::new("");
79        let sub_label = Label::new("");
80        let text_label = Label::new("");
81        let password_label = Label::new("");
82        let bigtext_label = Label::new("");
83        let progress_bar = ProgressBar::indeterminate();
84        output_vbox.append(add_label.clone(), LayoutStrategy::Compact);
85        output_vbox.append(sub_label.clone(), LayoutStrategy::Compact);
86        output_vbox.append(progress_bar.clone(), LayoutStrategy::Compact);
87        output_vbox.append(text_label.clone(), LayoutStrategy::Compact);
88        output_vbox.append(password_label.clone(), LayoutStrategy::Compact);
89        output_vbox.append(bigtext_label.clone(), LayoutStrategy::Stretchy);
90        output_group.set_child(output_vbox);
91        (
92            output_group,
93            add_label,
94            sub_label,
95            text_label,
96            password_label,
97            bigtext_label,
98            progress_bar,
99        )
100    };
101
102    // This horizontal box will arrange the two groups of controls.
103    let mut hbox = HorizontalBox::new();
104    hbox.append(input_group, LayoutStrategy::Stretchy);
105    hbox.append(output_group, LayoutStrategy::Stretchy);
106
107    // The window allows all constituent components to be displayed.
108    let mut window = Window::new(
109        &ui.clone(),
110        "Input Output Test",
111        300,
112        150,
113        WindowType::NoMenubar,
114    );
115    window.set_child(hbox);
116    window.show();
117
118    // These on_changed functions allow updating the application state when a
119    // control changes its value.
120
121    slider.on_changed({
122        let state = state.clone();
123        move |val| {
124            state.borrow_mut().slider_val = val;
125        }
126    });
127
128    spinner.on_changed({
129        let state = state.clone();
130        move |val| {
131            state.borrow_mut().spinner_val = val;
132        }
133    });
134
135    entry.on_changed({
136        let state = state.clone();
137        move |val| {
138            state.borrow_mut().entry_val = val;
139        }
140    });
141
142    password.on_changed({
143        let state = state.clone();
144        move |val| {
145            state.borrow_mut().password_val = val;
146        }
147    });
148
149    multi.on_changed({
150        let state = state.clone();
151        move |val| {
152            state.borrow_mut().multi_val = val;
153        }
154    });
155
156    // Rather than just invoking ui.run(), using EventLoop gives a lot more control
157    // over the user interface event loop.
158    // Here, the on_tick() callback is used to update the view against the state.
159    let mut event_loop = ui.event_loop();
160    event_loop.on_tick({
161        let mut add_label = add_label.clone();
162        let mut sub_label = sub_label.clone();
163        let mut text_label = text_label.clone();
164        let mut password_label = password_label.clone();
165        let mut bigtext_label = bigtext_label.clone();
166        let mut progress_bar = progress_bar.clone();
167        move || {
168            let state = state.borrow();
169
170            // Update all the outputs
171            add_label.set_text(&format!("Added: {}", state.slider_val + state.spinner_val));
172            sub_label.set_text(&format!(
173                "Subtracted: {}",
174                state.slider_val - state.spinner_val
175            ));
176            text_label.set_text(&format!("Text: {}", state.entry_val));
177            password_label.set_text(&format!("Secret Text: {}", state.password_val));
178            bigtext_label.set_text(&format!("Multiline Text: {}", state.multi_val));
179            progress_bar.set_value((state.slider_val + state.spinner_val) as u32)
180        }
181    });
182    event_loop.run();
183}