Skip to content

Commit a17a4c7

Browse files
committed
Add textual inputs and flesh out input example
1 parent f971194 commit a17a4c7

File tree

4 files changed

+197
-39
lines changed

4 files changed

+197
-39
lines changed

iui/examples/inputs.rs

Lines changed: 90 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,83 @@
22
33
extern crate iui;
44
use iui::prelude::*;
5-
use iui::controls::{Label, Spinbox, Slider, VerticalBox, HorizontalBox, Group};
5+
use iui::controls::{Label, Spinbox, Slider, Entry, MultilineEntry, VerticalBox, HorizontalBox, HorizontalSeparator, Group, Spacer};
66
use std::rc::Rc;
77
use std::cell::RefCell;
88

99
/// This struct will hold the values that multiple callbacks will need to access.
1010
struct State {
1111
slider_val: i64,
12-
spinner_val: i64
12+
spinner_val: i64,
13+
entry_val: String,
14+
multi_val: String,
1315
}
1416

1517
fn main() {
1618
// Initialize the UI framework.
1719
let ui = UI::init().unwrap();
1820

1921
// Initialize the state of the application.
20-
let state = Rc::new(RefCell::new(State { slider_val: 0, spinner_val: 0}));
21-
22-
let input_group = Group::new(&ui, "Inputs");
23-
let input_vbox = VerticalBox::new(&ui);
24-
let slider = Slider::new(&ui, 1, 100);
25-
let spinner = Spinbox::new(&ui, 1, 100);
26-
input_vbox.append(&ui, slider.clone(), LayoutStrategy::Compact);
27-
input_vbox.append(&ui, spinner.clone(), LayoutStrategy::Compact);
28-
input_group.set_child(&ui, input_vbox);
29-
30-
let output_group = Group::new(&ui, "Outputs");
31-
let output_vbox = VerticalBox::new(&ui);
32-
let add_label = Label::new(&ui, "");
33-
let sub_label = Label::new(&ui, "");
34-
output_vbox.append(&ui, add_label.clone(), LayoutStrategy::Compact);
35-
output_vbox.append(&ui, sub_label.clone(), LayoutStrategy::Compact);
36-
output_group.set_child(&ui, output_vbox);
22+
let state = Rc::new(RefCell::new(State { slider_val: 0, spinner_val: 0, entry_val: "".into(), multi_val: "".into() }));
3723

24+
// Set up the inputs for the application.
25+
// While it's not necessary to create a block for this, it makes the code a lot easier
26+
// to read; the indentation presents a visual cue informing the reader that these
27+
// statements are related.
28+
let (input_group, slider, spinner, entry, multi) = {
29+
// The group will hold all the inputs
30+
let input_group = Group::new(&ui, "Inputs");
31+
// The vertical box arranges the inputs within the groups
32+
let input_vbox = VerticalBox::new(&ui);
33+
input_vbox.set_padded(&ui, true);
34+
// Numerical inputs
35+
let slider = Slider::new(&ui, 1, 100);
36+
let spinner = Spinbox::new(&ui, 1, 100);
37+
let entry = Entry::new(&ui);
38+
let multi = MultilineEntry::new(&ui);
39+
// Add everything in hierarchy
40+
// Note the reverse order here. Again, it's not necessary, but it improves
41+
// readability.
42+
input_vbox.append(&ui, slider.clone(), LayoutStrategy::Compact);
43+
input_vbox.append(&ui, spinner.clone(), LayoutStrategy::Compact);
44+
input_vbox.append(&ui, Spacer::new(&ui), LayoutStrategy::Compact);
45+
input_vbox.append(&ui, HorizontalSeparator::new(&ui), LayoutStrategy::Compact);
46+
input_vbox.append(&ui, Spacer::new(&ui), LayoutStrategy::Compact);
47+
input_vbox.append(&ui, entry.clone(), LayoutStrategy::Compact);
48+
input_vbox.append(&ui, multi.clone(), LayoutStrategy::Stretchy);
49+
input_group.set_child(&ui, input_vbox);
50+
(input_group, slider, spinner, entry, multi)
51+
};
52+
53+
// Set up the outputs for the application.
54+
let (output_group, add_label, sub_label, text_label, bigtext_label) = {
55+
let output_group = Group::new(&ui, "Outputs");
56+
let output_vbox = VerticalBox::new(&ui);
57+
let add_label = Label::new(&ui, "");
58+
let sub_label = Label::new(&ui, "");
59+
let text_label = Label::new(&ui, "");
60+
let bigtext_label = Label::new(&ui, "");
61+
output_vbox.append(&ui, add_label.clone(), LayoutStrategy::Compact);
62+
output_vbox.append(&ui, sub_label.clone(), LayoutStrategy::Compact);
63+
output_vbox.append(&ui, text_label.clone(), LayoutStrategy::Compact);
64+
output_vbox.append(&ui, bigtext_label.clone(), LayoutStrategy::Stretchy);
65+
output_group.set_child(&ui, output_vbox);
66+
(output_group, add_label, sub_label, text_label, bigtext_label)
67+
};
68+
69+
// This horizontal box will arrange the two groups of controls.
3870
let hbox = HorizontalBox::new(&ui);
3971
hbox.append(&ui, input_group, LayoutStrategy::Stretchy);
4072
hbox.append(&ui, output_group, LayoutStrategy::Stretchy);
4173

74+
// The window allows all constituent components to be displayed.
4275
let window = Window::new(&ui, "Input Output Test", 300, 150, WindowType::NoMenubar);
4376
window.set_child(&ui, hbox);
4477
window.show(&ui);
4578

79+
// This update_view function is defined inline so that it can capture the environment, rather than
80+
// needing to be passed ui, the labels, and the state each time it is invoked.
81+
// It is wrapped in a refcounted pointer so it can be shared with several other closures.
4682
let update_view = Rc::new({
4783
let ui = ui.clone();
4884
let add_label = add_label.clone();
@@ -52,30 +88,54 @@ fn main() {
5288
let state = state.borrow();
5389
add_label.set_text(&ui, &format!("Added: {}", state.slider_val + state.spinner_val));
5490
sub_label.set_text(&ui, &format!("Subtracted: {}", state.slider_val - state.spinner_val));
91+
text_label.set_text(&ui, &format!("Text: {}", state.entry_val));
92+
bigtext_label.set_text(&ui, &format!("Multiline Text: {}", state.multi_val));
5593
}
5694
});
5795

58-
update_view();
96+
// These on_changed functions allow updating the application state when a
97+
// control changes its value.
98+
// Note the calls to update_view after each change to the state!
5999

60-
{
61-
let ui = ui.clone();
100+
slider.on_changed(&ui, {
62101
let update_view = update_view.clone();
63102
let state = state.clone();
64-
slider.on_changed(&ui, |val| {
103+
move |val| {
65104
state.borrow_mut().slider_val = val;
66105
update_view();
67-
});
68-
}
106+
}
107+
});
69108

70-
{
71-
let ui = ui.clone();
109+
spinner.on_changed(&ui, {
72110
let update_view = update_view.clone();
73111
let state = state.clone();
74-
spinner.on_changed(&ui, |val| {
112+
move |val| {
75113
state.borrow_mut().spinner_val = val;
76114
update_view();
77-
});
78-
}
115+
}
116+
});
117+
118+
entry.on_changed(&ui, {
119+
let update_view = update_view.clone();
120+
let state = state.clone();
121+
move |val| {
122+
state.borrow_mut().entry_val = val;
123+
update_view();
124+
}
125+
});
126+
127+
multi.on_changed(&ui, {
128+
let update_view = update_view.clone();
129+
let state = state.clone();
130+
move |val| {
131+
state.borrow_mut().multi_val = val;
132+
update_view();
133+
}
134+
});
135+
136+
// The initial call to update_view sets up the initial GUI state.
137+
update_view();
79138

139+
// Start the application.
80140
ui.main();
81141
}

iui/src/controls/entry.rs

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
//! User input mechanisms: numbers, colors, and text in various forms.
22
33
use std::mem;
4-
use std::ffi::{CString};
4+
use std::ffi::{CStr, CString};
55
use std::i64;
66
use libc::c_void;
7-
use ui_sys::{self, uiControl, uiSpinbox, uiSlider};
7+
use ui_sys::{self, uiControl, uiSpinbox, uiSlider, uiEntry, uiMultilineEntry};
88
use super::Control;
99
use ui::UI;
1010

@@ -21,13 +21,13 @@ pub trait TextEntry {
2121
}
2222

2323
define_control!{
24-
/// Numerical entry control which allows users to set any value in a range.
24+
/// Numerical entry control which allows users to set any value in a range by typing or incrementing/decrementing.
2525
rust_type: Spinbox,
2626
sys_type: uiSpinbox
2727
}
2828

2929
define_control!{
30-
/// Allows users to select a value by picking a location along a line.
30+
/// Numerical entry which allows users to select a value by picking a location along a line.
3131
rust_type: Slider,
3232
sys_type: uiSlider
3333
}
@@ -108,3 +108,87 @@ impl NumericEntry for Slider {
108108
}
109109
}
110110
}
111+
112+
define_control! {
113+
/// Single-line editable text buffer.
114+
rust_type: Entry,
115+
sys_type: uiEntry
116+
}
117+
118+
define_control! {
119+
/// Multi-line editable text buffer.
120+
rust_type: MultilineEntry,
121+
sys_type: uiMultilineEntry
122+
}
123+
124+
impl Entry {
125+
pub fn new(_ctx: &UI) -> Entry {
126+
unsafe { Entry::from_raw(ui_sys::uiNewEntry()) }
127+
}
128+
}
129+
130+
impl MultilineEntry {
131+
pub fn new(_ctx: &UI) -> MultilineEntry {
132+
unsafe { MultilineEntry::from_raw(ui_sys::uiNewMultilineEntry()) }
133+
}
134+
}
135+
136+
impl TextEntry for Entry {
137+
fn value(&self, _ctx: &UI) -> String {
138+
unsafe { CStr::from_ptr(ui_sys::uiEntryText(self.uiEntry)).to_string_lossy().into_owned() }
139+
}
140+
fn set_value(&self, ctx: &UI, value: &str) {
141+
let cstring = CString::new(value.as_bytes().to_vec()).unwrap();
142+
unsafe { ui_sys::uiEntrySetText(self.uiEntry, cstring.as_ptr()) }
143+
}
144+
145+
fn on_changed<F: FnMut(String)>(&self, _ctx: &UI, callback: F) {
146+
unsafe {
147+
let mut data: Box<Box<FnMut(String)>> = Box::new(Box::new(callback));
148+
ui_sys::uiEntryOnChanged(
149+
self.uiEntry,
150+
c_callback,
151+
&mut *data as *mut Box<FnMut(String)> as *mut c_void,
152+
);
153+
mem::forget(data);
154+
}
155+
156+
extern "C" fn c_callback(entry: *mut uiEntry, data: *mut c_void) {
157+
unsafe {
158+
let string = CStr::from_ptr(ui_sys::uiEntryText(entry)).to_string_lossy().into_owned();
159+
mem::transmute::<*mut c_void, &mut Box<FnMut(String)>>(data)(string);
160+
mem::forget(entry);
161+
}
162+
}
163+
}
164+
}
165+
166+
impl TextEntry for MultilineEntry {
167+
fn value(&self, _ctx: &UI) -> String {
168+
unsafe { CStr::from_ptr(ui_sys::uiMultilineEntryText(self.uiMultilineEntry)).to_string_lossy().into_owned() }
169+
}
170+
fn set_value(&self, _ctx: &UI, value: &str) {
171+
let cstring = CString::new(value.as_bytes().to_vec()).unwrap();
172+
unsafe { ui_sys::uiMultilineEntrySetText(self.uiMultilineEntry, cstring.as_ptr()) }
173+
}
174+
175+
fn on_changed<F: FnMut(String)>(&self, _ctx: &UI, callback: F) {
176+
unsafe {
177+
let mut data: Box<Box<FnMut(String)>> = Box::new(Box::new(callback));
178+
ui_sys::uiMultilineEntryOnChanged(
179+
self.uiMultilineEntry,
180+
c_callback,
181+
&mut *data as *mut Box<FnMut(String)> as *mut c_void,
182+
);
183+
mem::forget(data);
184+
}
185+
186+
extern "C" fn c_callback(entry: *mut uiMultilineEntry, data: *mut c_void) {
187+
unsafe {
188+
let string = CStr::from_ptr(ui_sys::uiMultilineEntryText(entry)).to_string_lossy().into_owned();
189+
mem::transmute::<*mut c_void, &mut Box<FnMut(String)>>(data)(string);
190+
mem::forget(entry);
191+
}
192+
}
193+
}
194+
}

iui/src/controls/layout.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ pub enum LayoutStrategy {
1515
}
1616

1717
define_control! {
18-
/// A box that lays out its children vertically; see [`BoxExt`](trait.BoxExt.html) for functionality.
18+
/// Lays out its children vertically; see [`BoxExt`](trait.BoxExt.html) for functionality.
1919
rust_type: VerticalBox,
2020
sys_type: uiBox
2121
}
2222

2323
define_control! {
24-
/// A box that lays out its children horizontally; see [`BoxExt`](trait.BoxExt.html) for functionality.
24+
/// Lays out its children horizontally; see [`BoxExt`](trait.BoxExt.html) for functionality.
2525
rust_type: HorizontalBox,
2626
sys_type: uiBox
2727
}
@@ -99,13 +99,13 @@ impl HorizontalBox {
9999
}
100100

101101
define_control! {
102-
/// A group of tabs, each of which shows a different sub-control.
102+
/// Group of tabs, each of which shows a different sub-control.
103103
rust_type: TabGroup,
104104
sys_type: uiTab
105105
}
106106

107107
define_control! {
108-
/// A group collects controls together, with (optionally) a margin and/or title.
108+
/// Collects controls together, with (optionally) a margin and/or title.
109109
rust_type: Group,
110110
sys_type: uiGroup
111111
}
@@ -217,7 +217,7 @@ impl TabGroup {
217217
}
218218

219219
define_control!{
220-
/// Simply adds a horizontal line, to seperate things visually.
220+
/// Horizontal line, to seperate things visually.
221221
rust_type: HorizontalSeparator,
222222
sys_type: uiSeparator
223223
}
@@ -227,3 +227,15 @@ impl HorizontalSeparator {
227227
unsafe { HorizontalSeparator::from_raw(ui_sys::uiNewHorizontalSeparator()) }
228228
}
229229
}
230+
231+
define_control! {
232+
/// Seperates components with empty space.
233+
rust_type: Spacer,
234+
sys_type: uiBox
235+
}
236+
237+
impl Spacer {
238+
pub fn new(_ctx: &UI) -> Self {
239+
unsafe { Spacer::from_raw(ui_sys::uiNewHorizontalBox()) }
240+
}
241+
}

iui/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
//!
1010
//! Most of the functionality of the crate is exposed via the [UI](struct.UI.html) RAII guard, which handles all initialization and cleanup for the
1111
//! underlying library.
12+
//!
13+
//! After initialization, all the functionality used for creating actual UIs is in the [`controls`](controls/index.html) module.
1214
1315
#[macro_use]
1416
extern crate bitflags;

0 commit comments

Comments
 (0)