Skip to content

Commit bfa37ee

Browse files
authored
Merge pull request #12 from LeoTindall/uigrid
Implement UI Grid
2 parents be4b18e + 00121df commit bfa37ee

File tree

4 files changed

+256
-2
lines changed

4 files changed

+256
-2
lines changed

iui/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ maintenance = { status = "actively-developed" }
4646
bitflags = "1.0"
4747
libc = "0.2"
4848
failure = "0.1.1"
49-
ui-sys = "0.1.1"
49+
ui-sys = { path = "../ui-sys" }

iui/examples/inputs-grid.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//! Demonstrates a mutable application state manipulated over a number of UIs
2+
//! Using the UIGrid function for a prettier interface.
3+
4+
extern crate iui;
5+
use iui::prelude::*;
6+
use iui::controls::{Label, Spinbox, Slider, Entry, MultilineEntry, LayoutGrid,
7+
GridAlignment, HorizontalSeparator};
8+
use std::rc::Rc;
9+
use std::cell::RefCell;
10+
11+
/// This struct will hold the values that multiple callbacks will need to access.
12+
struct State {
13+
slider_val: i64,
14+
spinner_val: i64,
15+
entry_val: String,
16+
multi_val: String,
17+
}
18+
19+
fn main() {
20+
// Initialize the UI framework.
21+
let ui = UI::init().unwrap();
22+
23+
// Initialize the state of the application.
24+
let state = Rc::new(RefCell::new(State { slider_val: 0, spinner_val: 0, entry_val: "".into(), multi_val: "".into() }));
25+
26+
// Create the grid which we'll use to lay out controls
27+
let mut grid = LayoutGrid::new(&ui);
28+
grid.set_padded(&ui, true);
29+
30+
// Set up the inputs for the application.
31+
// While it's not necessary to create a block for this, it makes the code a lot easier
32+
// to read; the indentation presents a visual cue informing the reader that these
33+
// statements are related.
34+
let (mut slider, mut spinner, mut entry, mut multi) = {
35+
// Numerical inputs
36+
let slider = Slider::new(&ui, 1, 100);
37+
let spinner = Spinbox::new(&ui, 1, 100);
38+
// Text inputs
39+
let entry = Entry::new(&ui);
40+
let multi = MultilineEntry::new(&ui);
41+
// Add everything into the grid
42+
grid.append(&ui, slider.clone(),
43+
// This is position (by slot) and size, expansion, and alignment.
44+
// In this case, row 0, col 0, 1 by 1, compress as much as possible,
45+
// and align to the fill.
46+
0, 0, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
47+
grid.append(&ui, spinner.clone(),
48+
// This one is at column zero, row 1.
49+
0, 1, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
50+
grid.append(&ui, HorizontalSeparator::new(&ui),
51+
0, 3, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
52+
grid.append(&ui, entry.clone(),
53+
0, 4, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
54+
grid.append(&ui, multi.clone(),
55+
// The multiline entry is at column 0, row 1, and expands vertically.
56+
0, 5, 1, 1, false, true, GridAlignment::Fill, GridAlignment::Fill);
57+
(slider, spinner, entry, multi)
58+
};
59+
60+
// Set up the outputs for the application. Organization is very similar to the
61+
// previous setup.
62+
let (add_label, sub_label, text_label, bigtext_label) = {
63+
let add_label = Label::new(&ui, "");
64+
let sub_label = Label::new(&ui, "");
65+
let text_label = Label::new(&ui, "");
66+
let bigtext_label = Label::new(&ui, "");
67+
grid.append(&ui, add_label.clone(),
68+
1, 0, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
69+
grid.append(&ui, sub_label.clone(),
70+
1, 1, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
71+
grid.append(&ui, text_label.clone(),
72+
1, 2, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
73+
grid.append(&ui, bigtext_label.clone(),
74+
1, 3, 1, 1, false, false, GridAlignment::Fill, GridAlignment::Fill);
75+
(add_label, sub_label, text_label, bigtext_label)
76+
};
77+
78+
// The window allows all constituent components to be displayed.
79+
let mut window = Window::new(&ui, "Input Output Test", 300, 150, WindowType::NoMenubar);
80+
window.set_child(&ui, grid);
81+
window.show(&ui);
82+
83+
// These on_changed functions allow updating the application state when a
84+
// control changes its value.
85+
86+
slider.on_changed(&ui, {
87+
let state = state.clone();
88+
move |val| { state.borrow_mut().slider_val = val; }
89+
});
90+
91+
spinner.on_changed(&ui, {
92+
let state = state.clone();
93+
move |val| { state.borrow_mut().spinner_val = val; }
94+
});
95+
96+
entry.on_changed(&ui, {
97+
let state = state.clone();
98+
move |val| { state.borrow_mut().entry_val = val; }
99+
});
100+
101+
multi.on_changed(&ui, {
102+
let state = state.clone();
103+
move |val| { state.borrow_mut().multi_val = val; }
104+
});
105+
106+
107+
// Rather than just invoking ui.run(), using EventLoop gives a lot more control
108+
// over the user interface event loop.
109+
// Here, the on_tick() callback is used to update the view against the state.
110+
let mut event_loop = ui.event_loop();
111+
event_loop.on_tick(&ui, {
112+
let ui = ui.clone();
113+
let mut add_label = add_label.clone();
114+
let mut sub_label = sub_label.clone();
115+
let mut text_label = text_label.clone();
116+
let mut bigtext_label = bigtext_label.clone();
117+
move || {
118+
let state = state.borrow();
119+
120+
// Update all the labels
121+
add_label.set_text(&ui, &format!("Added: {}", state.slider_val + state.spinner_val));
122+
sub_label.set_text(&ui, &format!("Subtracted: {}", state.slider_val - state.spinner_val));
123+
text_label.set_text(&ui, &format!("Text: {}", state.entry_val));
124+
bigtext_label.set_text(&ui, &format!("Multiline Text: {}", state.multi_val));
125+
}
126+
});
127+
event_loop.run(&ui);
128+
}

iui/src/controls/layout.rs

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use libc::c_int;
44
use std::ffi::{CStr, CString};
55
use std::mem;
66
use ui::UI;
7-
use ui_sys::{self, uiBox, uiControl, uiGroup, uiSeparator, uiTab};
7+
use ui_sys::{self, uiBox, uiControl, uiGroup, uiSeparator, uiTab, uiGrid, uiAlign, uiAt};
88

99
/// Defines the ways in which the children of boxes can be layed out.
1010
pub enum LayoutStrategy {
@@ -250,3 +250,100 @@ impl Spacer {
250250
unsafe { Spacer::from_raw(ui_sys::uiNewHorizontalBox()) }
251251
}
252252
}
253+
254+
/// Informs a `LayoutGrid` how to align a control.
255+
#[derive(Clone, Copy, PartialEq)]
256+
pub enum GridAlignment {
257+
/// Expand to use all available space.
258+
Fill,
259+
/// Collapse toward the start of the available space.
260+
Start,
261+
/// Collapse equally on both sides of the available space.
262+
Center,
263+
/// Collapse toward the end of the available space.
264+
End
265+
}
266+
267+
impl GridAlignment {
268+
fn into_ui_align(self) -> uiAlign {
269+
use self::GridAlignment::*;
270+
use self::uiAlign::*;
271+
match self {
272+
Fill => uiAlignFill,
273+
Start => uiAlignStart,
274+
Center => uiAlignCenter,
275+
End => uiAlignEnd
276+
}
277+
}
278+
}
279+
280+
/// Informs a `LayoutGrid` as to position a control.
281+
#[derive(Clone, Copy, PartialEq)]
282+
pub enum GridInsertionStrategy {
283+
Leading,
284+
Top,
285+
Trailing,
286+
Bottom
287+
}
288+
289+
impl GridInsertionStrategy {
290+
fn into_ui_at(self) -> uiAt {
291+
use self::GridInsertionStrategy::*;
292+
use self::uiAt::*;
293+
match self {
294+
Leading => uiAtLeading,
295+
Top => uiAtTop,
296+
Trailing => uiAtTrailing,
297+
Bottom => uiAtBottom
298+
}
299+
}
300+
}
301+
302+
define_control!{
303+
/// Lays out its children in a grid according to insertion instructions.
304+
rust_type: LayoutGrid,
305+
sys_type: uiGrid
306+
}
307+
308+
309+
impl LayoutGrid {
310+
/// Creates a new `LayoutGrid`.
311+
pub fn new(_ctx: &UI) -> Self {
312+
unsafe { LayoutGrid::from_raw(ui_sys::uiNewGrid()) }
313+
}
314+
315+
/// Returns `true` if the `LayoutGrid` is padded and `false` if not.
316+
pub fn padded(&self, _ctx: &UI) -> bool {
317+
if unsafe { ui_sys::uiGridPadded(self.uiGrid) } == 0 {
318+
true
319+
} else {
320+
false
321+
}
322+
}
323+
324+
/// Sets the padding state of the `LayoutGrid`
325+
pub fn set_padded(&mut self, _ctx: &UI, padded: bool) {
326+
let v = if padded { 1 } else { 0 };
327+
328+
unsafe {
329+
ui_sys::uiGridSetPadded(self.uiGrid, v);
330+
}
331+
}
332+
333+
/// Adds a control to the `LayoutGrid`.
334+
pub fn append<T: Into<Control>>(&mut self, _ctx: &UI, control: T,
335+
left: i32, height: i32,
336+
xspan: i32, yspan: i32,
337+
hexpand: bool, vexpand: bool,
338+
halign: GridAlignment, valign: GridAlignment) {
339+
let hexpand = if hexpand { 1 } else { 0 };
340+
let vexpand = if vexpand { 1 } else { 0 };
341+
unsafe {
342+
ui_sys::uiGridAppend(
343+
self.uiGrid, control.into().ui_control, left, height, xspan, yspan,
344+
hexpand, halign.into_ui_align(), vexpand, valign.into_ui_align()
345+
);
346+
}
347+
}
348+
}
349+

ui-sys/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,3 +761,32 @@ extern {
761761
xScale: c_double,
762762
yScale: c_double);
763763
}
764+
765+
#[repr(C)]
766+
#[derive(Clone, Copy, PartialEq)]
767+
pub enum uiAlign {
768+
uiAlignFill,
769+
uiAlignStart,
770+
uiAlignCenter,
771+
uiAlignEnd,
772+
}
773+
774+
#[repr(C)]
775+
#[derive(Clone, Copy, PartialEq)]
776+
pub enum uiAt {
777+
uiAtLeading,
778+
uiAtTop,
779+
uiAtTrailing,
780+
uiAtBottom,
781+
}
782+
783+
pub enum uiGrid {}
784+
785+
extern {
786+
pub fn uiGridAppend(g: *mut uiGrid, c: *mut uiControl, left: c_int, height: c_int, xspan: c_int, yspan: c_int, hexpand: c_int, halign: uiAlign, vexpand: c_int, valign: uiAlign);
787+
pub fn uiGridInsertAt(g: *mut uiGrid, c: *mut uiControl, existing: *mut uiControl, at: uiAt, left: c_int, height: c_int, xspan: c_int, yspan: c_int, hexpand: c_int, halign: uiAlign, vexpand: c_int, valign: uiAlign);
788+
pub fn uiGridPadded(g: *mut uiGrid) -> c_int;
789+
pub fn uiGridSetPadded(g: *mut uiGrid, padded: c_int);
790+
pub fn uiNewGrid() -> *mut uiGrid;
791+
}
792+

0 commit comments

Comments
 (0)