Use mutable references instead of return values

It seems to be more ergonomical with the underlying library
This commit is contained in:
Joonas Javanainen 2015-08-21 00:08:40 +03:00
parent 9a37676580
commit eef67ddecc
8 changed files with 97 additions and 149 deletions

View File

@ -33,7 +33,7 @@ frame.window()
## Core design questions and current choices
* Closures VS begin/end pairs (current choice: closures)
* Mutable references VS return values (current choice: return values)
* Mutable references VS return values (current choice: mutable references)
* Passing around Frame<'fr> VS passing around &'fr Frame (current choice: Frame<'fr>)
* Splitting the API to smaller pieces VS all draw calls in Frame (current choice: all draw calls in Frame)
* Builder pattern for optional arguments VS something else (current choice: builder)

View File

@ -23,7 +23,7 @@ fn main() {
}
}
fn hello_world<'a>(frame: &Frame<'a>) -> bool {
fn hello_world<'a>(frame: &Frame<'a>) {
frame.window()
.name(im_str!("Hello world"))
.size((300.0, 100.0), ImGuiSetCond_FirstUseEver)

View File

@ -39,9 +39,8 @@ impl Support {
self.imgui.set_mouse_down(&[self.mouse_pressed.0, self.mouse_pressed.1, self.mouse_pressed.2, false, false]);
}
pub fn render<'fr, 'a: 'fr , F: FnMut(&Frame<'fr>) -> bool>(
pub fn render<'fr, 'a: 'fr , F: FnMut(&Frame<'fr>)>(
&'a mut self, clear_color: (f32, f32, f32, f32), mut f: F) -> bool {
let mut result;
let now = SteadyTime::now();
let delta = now - self.last_frame;
let delta_f = delta.num_nanoseconds().unwrap() as f32 / 1_000_000_000.0;
@ -55,7 +54,7 @@ impl Support {
let (width, height) = target.get_dimensions();
let frame = self.imgui.frame(width, height, delta_f);
result = f(&frame);
f(&frame);
self.renderer.render(&mut target, frame).unwrap();
target.finish().unwrap();
@ -64,7 +63,7 @@ impl Support {
match event {
Event::Closed |
Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape))
=> result = false,
=> return false,
Event::MouseMoved(pos) => self.mouse_pos = pos,
Event::MouseInput(state, MouseButton::Left) =>
self.mouse_pressed.0 = state == ElementState::Pressed,
@ -75,6 +74,6 @@ impl Support {
_ => ()
}
}
result
true
}
}

View File

@ -75,7 +75,7 @@ impl Default for FileMenuState {
}
struct AutoResizeState {
lines: usize
lines: i32
}
impl Default for AutoResizeState {
@ -91,12 +91,13 @@ fn main() {
.. Default::default()
};
let mut support = Support::init();
let mut opened = true;
loop {
let active = support.render(state.clear_color, |frame| {
show_test_window(frame, &mut state)
show_test_window(frame, &mut state, &mut opened);
});
if !active { break }
if !active || !opened { break }
}
}
@ -119,25 +120,25 @@ fn show_user_guide<'a>(frame: &Frame<'a>) {
Use +- to subtract."));
}
fn show_test_window<'a>(frame: &Frame<'a>, state: &mut State) -> bool {
fn show_test_window<'a>(frame: &Frame<'a>, state: &mut State, opened: &mut bool) {
if state.show_app_metrics {
state.show_app_metrics = frame.show_metrics_window();
frame.show_metrics_window(&mut state.show_app_metrics);
}
if state.show_app_main_menu_bar { show_example_app_main_menu_bar(frame, state) }
if state.show_app_auto_resize {
state.show_app_auto_resize = show_example_app_auto_resize(frame, &mut state.auto_resize_state);
show_example_app_auto_resize(frame, &mut state.auto_resize_state, &mut state.show_app_auto_resize);
}
if state.show_app_fixed_overlay {
state.show_app_fixed_overlay = show_example_app_fixed_overlay(frame);
show_example_app_fixed_overlay(frame, &mut state.show_app_fixed_overlay);
}
if state.show_app_manipulating_window_title {
show_example_app_manipulating_window_title(frame);
}
if state.show_app_about {
state.show_app_about = frame.window()
frame.window()
.name(im_str!("About ImGui"))
.always_auto_resize(true)
.closable(true)
.opened(&mut state.show_app_about)
.build(|| {
frame.text(ImStr::from_str(&format!("ImGui {}", imgui::get_version())));
frame.separator();
@ -157,7 +158,7 @@ fn show_test_window<'a>(frame: &Frame<'a>, state: &mut State) -> bool {
.menu_bar(!state.no_menu)
.bg_alpha(state.bg_alpha)
.size((550.0, 680.0), ImGuiSetCond_FirstUseEver)
.closable(true)
.opened(opened)
.build(|| {
frame.text(im_str!("ImGui says hello."));
frame.menu_bar(|| {
@ -165,39 +166,28 @@ fn show_test_window<'a>(frame: &Frame<'a>, state: &mut State) -> bool {
show_example_menu_file(frame, &mut state.file_menu);
});
frame.menu(im_str!("Examples")).build(|| {
if frame.menu_item(im_str!("Main menu bar")).build() {
state.show_app_main_menu_bar = !state.show_app_main_menu_bar;
}
if frame.menu_item(im_str!("Console")).build() {
state.show_app_console = !state.show_app_console;
}
if frame.menu_item(im_str!("Simple layout")).build() {
state.show_app_layout = !state.show_app_layout;
}
if frame.menu_item(im_str!("Long text display")).build() {
state.show_app_long_text = !state.show_app_long_text;
}
if frame.menu_item(im_str!("Auto-resizing window")).build() {
state.show_app_auto_resize = !state.show_app_auto_resize;
}
if frame.menu_item(im_str!("Simple overlay")).build() {
state.show_app_fixed_overlay = !state.show_app_fixed_overlay;
}
if frame.menu_item(im_str!("Manipulating window title")).build() {
state.show_app_manipulating_window_title =
!state.show_app_manipulating_window_title;
}
if frame.menu_item(im_str!("Custom rendering")).build() {
state.show_app_custom_rendering = !state.show_app_custom_rendering;
}
frame.menu_item(im_str!("Main menu bar"))
.selected(&mut state.show_app_main_menu_bar).build();
frame.menu_item(im_str!("Console"))
.selected(&mut state.show_app_console).build();
frame.menu_item(im_str!("Simple layout"))
.selected(&mut state.show_app_layout).build();
frame.menu_item(im_str!("Long text display"))
.selected(&mut state.show_app_long_text).build();
frame.menu_item(im_str!("Auto-resizing window"))
.selected(&mut state.show_app_auto_resize).build();
frame.menu_item(im_str!("Simple overlay"))
.selected(&mut state.show_app_fixed_overlay).build();
frame.menu_item(im_str!("Manipulating window title"))
.selected(&mut state.show_app_manipulating_window_title).build();
frame.menu_item(im_str!("Custom rendering"))
.selected(&mut state.show_app_custom_rendering).build();
});
frame.menu(im_str!("Help")).build(|| {
if frame.menu_item(im_str!("Metrics")).build() {
state.show_app_metrics = !state.show_app_metrics;
}
if frame.menu_item(im_str!("About ImGui")).build() {
state.show_app_about = !state.show_app_about;
}
frame.menu_item(im_str!("Metrics"))
.selected(&mut state.show_app_metrics).build();
frame.menu_item(im_str!("About ImGui"))
.selected(&mut state.show_app_about).build();
});
});
frame.spacing();
@ -207,35 +197,18 @@ fn show_test_window<'a>(frame: &Frame<'a>, state: &mut State) -> bool {
}
if frame.collapsing_header(im_str!("Window options")).build() {
if let Some(no_titlebar) = frame.checkbox(im_str!("no titlebar"), state.no_titlebar) {
state.no_titlebar = no_titlebar;
}
frame.checkbox(im_str!("no titlebar"), &mut state.no_titlebar);
frame.same_line(150.0);
if let Some(no_border) = frame.checkbox(im_str!("no border"), state.no_border) {
state.no_border = no_border;
}
frame.checkbox(im_str!("no border"), &mut state.no_border);
frame.same_line(300.0);
if let Some(no_resize) = frame.checkbox(im_str!("no resize"), state.no_resize) {
state.no_resize = no_resize;
}
if let Some(no_move) = frame.checkbox(im_str!("no move"), state.no_move) {
state.no_move = no_move;
}
frame.checkbox(im_str!("no resize"), &mut state.no_resize);
frame.checkbox(im_str!("no move"), &mut state.no_move);
frame.same_line(150.0);
if let Some(no_scrollbar) = frame.checkbox(im_str!("no scrollbar"), state.no_scrollbar) {
state.no_scrollbar = no_scrollbar;
}
frame.checkbox(im_str!("no scrollbar"), &mut state.no_scrollbar);
frame.same_line(300.0);
if let Some(no_collapse) = frame.checkbox(im_str!("no collapse"), state.no_collapse) {
state.no_collapse = no_collapse;
}
if let Some(no_menu) = frame.checkbox(im_str!("no menu"), state.no_menu) {
state.no_menu = no_menu;
}
if let Some(bg_alpha) = frame.slider_f32(im_str!("bg alpha"),
state.bg_alpha, 0.0, 1.0).build() {
state.bg_alpha = bg_alpha;
}
frame.checkbox(im_str!("no collapse"), &mut state.no_collapse);
frame.checkbox(im_str!("no menu"), &mut state.no_menu);
frame.slider_f32(im_str!("bg alpha"), &mut state.bg_alpha, 0.0, 1.0).build();
}
})
}
@ -246,21 +219,21 @@ fn show_example_app_main_menu_bar<'a>(frame: &Frame<'a>, state: &mut State) {
show_example_menu_file(frame, &mut state.file_menu);
});
frame.menu(im_str!("Edit")).build(|| {
if frame.menu_item(im_str!("Undo")).shortcut(im_str!("CTRL+Z")).build() { }
if frame.menu_item(im_str!("Redo"))
.shortcut(im_str!("CTRL+Y")).enabled(false).build() { }
frame.menu_item(im_str!("Undo")).shortcut(im_str!("CTRL+Z")).build();
frame.menu_item(im_str!("Redo"))
.shortcut(im_str!("CTRL+Y")).enabled(false).build();
frame.separator();
if frame.menu_item(im_str!("Cut")).shortcut(im_str!("CTRL+X")).build() { }
if frame.menu_item(im_str!("Copy")).shortcut(im_str!("CTRL+C")).build() { }
if frame.menu_item(im_str!("Paste")).shortcut(im_str!("CTRL+V")).build() { }
frame.menu_item(im_str!("Cut")).shortcut(im_str!("CTRL+X")).build();
frame.menu_item(im_str!("Copy")).shortcut(im_str!("CTRL+C")).build();
frame.menu_item(im_str!("Paste")).shortcut(im_str!("CTRL+V")).build();
});
});
}
fn show_example_menu_file<'a>(frame: &Frame<'a>, state: &mut FileMenuState) {
frame.menu_item(im_str!("(dummy menu)")).enabled(false).build();
if frame.menu_item(im_str!("New")).build() { }
if frame.menu_item(im_str!("Open")).shortcut(im_str!("Ctrl+O")).build() { }
frame.menu_item(im_str!("New")).build();
frame.menu_item(im_str!("Open")).shortcut(im_str!("Ctrl+O")).build();
frame.menu(im_str!("Open Recent")).build(|| {
frame.menu_item(im_str!("fish_hat.c")).build();
frame.menu_item(im_str!("fish_hat.inl")).build();
@ -273,13 +246,11 @@ fn show_example_menu_file<'a>(frame: &Frame<'a>, state: &mut FileMenuState) {
});
});
});
if frame.menu_item(im_str!("Save")).shortcut(im_str!("Ctrl+S")).build() { }
if frame.menu_item(im_str!("Save As..")).build() { }
frame.menu_item(im_str!("Save")).shortcut(im_str!("Ctrl+S")).build();
frame.menu_item(im_str!("Save As..")).build();
frame.separator();
frame.menu(im_str!("Options")).build(|| {
if frame.menu_item(im_str!("Enabled")).selected(state.enabled).build() {
state.enabled = !state.enabled;
}
frame.menu_item(im_str!("Enabled")).selected(&mut state.enabled).build();
// TODO
});
frame.menu(im_str!("Colors")).build(|| {
@ -288,33 +259,31 @@ fn show_example_menu_file<'a>(frame: &Frame<'a>, state: &mut FileMenuState) {
frame.menu(im_str!("Disabled")).enabled(false).build(|| {
unreachable!();
});
if frame.menu_item(im_str!("Checked")).selected(true).build() { }
if frame.menu_item(im_str!("Quit")).shortcut(im_str!("Alt+F4")).build() { }
let mut checked = true;
frame.menu_item(im_str!("Checked")).selected(&mut checked).build();
frame.menu_item(im_str!("Quit")).shortcut(im_str!("Alt+F4")).build();
}
fn show_example_app_auto_resize<'a>(frame: &Frame<'a>, state: &mut AutoResizeState) -> bool {
fn show_example_app_auto_resize<'a>(frame: &Frame<'a>, state: &mut AutoResizeState, opened: &mut bool) {
frame.window()
.name(im_str!("Example: Auto-resizing window"))
.closable(true)
.opened(opened)
.always_auto_resize(true)
.build(|| {
frame.text(im_str!("Window will resize every-frame to the size of its content.
Note that you probably don't want to query the window size to
output your content because that would create a feedback loop."));
if let Some(lines) = frame.slider_i32(im_str!("Number of lines"),
state.lines as i32, 1, 20).build() {
state.lines = lines as usize;
}
frame.slider_i32(im_str!("Number of lines"), &mut state.lines, 1, 20).build();
for i in 0 .. state.lines {
frame.text(im_str!("{:2$}This is line {}", "", i, i * 4));
frame.text(im_str!("{:2$}This is line {}", "", i, i as usize * 4));
}
})
}
fn show_example_app_fixed_overlay<'a>(frame: &Frame<'a>) -> bool {
fn show_example_app_fixed_overlay<'a>(frame: &Frame<'a>, opened: &mut bool) {
frame.window()
.name(im_str!("Example: Fixed Overlay"))
.closable(true)
.opened(opened)
.bg_alpha(0.3)
.title_bar(false)
.resizable(false)

View File

@ -208,19 +208,15 @@ impl<'fr> Frame<'fr> {
Ok(())
}
pub fn show_user_guide(&self) { unsafe { ffi::igShowUserGuide() }; }
pub fn show_test_window(&self) -> bool {
let mut opened = true;
pub fn show_test_window(&self, opened: &mut bool) {
unsafe {
ffi::igShowTestWindow(&mut opened);
ffi::igShowTestWindow(opened);
}
opened
}
pub fn show_metrics_window(&self) -> bool {
let mut opened = true;
pub fn show_metrics_window(&self, opened: &mut bool) {
unsafe {
ffi::igShowMetricsWindow(&mut opened);
ffi::igShowMetricsWindow(opened);
}
opened
}
}
@ -286,23 +282,19 @@ impl<'fr> Frame<'fr> {
pub fn collapsing_header<'p>(&self, label: ImStr<'p>) -> CollapsingHeader<'fr, 'p> {
CollapsingHeader::new(label)
}
pub fn checkbox<'p>(&self, label: ImStr<'p>, value: bool) -> Option<bool> {
let mut result = value;
let changed = unsafe {
ffi::igCheckbox(label.as_ptr(), &mut result)
};
if changed { Some(result) } else { None }
pub fn checkbox<'p>(&self, label: ImStr<'p>, value: &'p mut bool) -> bool {
unsafe { ffi::igCheckbox(label.as_ptr(), value) }
}
}
// Widgets: Sliders
impl<'fr> Frame<'fr> {
pub fn slider_f32<'p>(&self, label: ImStr<'p>,
value: f32, min: f32, max: f32) -> SliderFloat<'fr, 'p> {
value: &'p mut f32, min: f32, max: f32) -> SliderFloat<'fr, 'p> {
SliderFloat::new(label, value, min, max)
}
pub fn slider_i32<'p>(&self, label: ImStr<'p>,
value: i32, min: i32, max: i32) -> SliderInt<'fr, 'p> {
value: &'p mut i32, min: i32, max: i32) -> SliderInt<'fr, 'p> {
SliderInt::new(label, value, min, max)
}
}

View File

@ -37,7 +37,7 @@ impl<'fr, 'p> Menu<'fr, 'p> {
pub struct MenuItem<'fr, 'p> {
label: ImStr<'p>,
shortcut: Option<ImStr<'p>>,
selected: bool,
selected: Option<&'p mut bool>,
enabled: bool,
_phantom: PhantomData<&'fr Frame<'fr>>
}
@ -47,7 +47,7 @@ impl<'fr, 'p> MenuItem<'fr, 'p> {
MenuItem {
label: label,
shortcut: None,
selected: false,
selected: None,
enabled: true,
_phantom: PhantomData
}
@ -60,9 +60,9 @@ impl<'fr, 'p> MenuItem<'fr, 'p> {
}
}
#[inline]
pub fn selected(self, selected: bool) -> Self {
pub fn selected(self, selected: &'p mut bool) -> Self {
MenuItem {
selected: selected,
selected: Some(selected),
.. self
}
}
@ -76,10 +76,10 @@ impl<'fr, 'p> MenuItem<'fr, 'p> {
pub fn build(self) -> bool {
let label = self.label.as_ptr();
let shortcut = self.shortcut.map(|x| x.as_ptr()).unwrap_or(ptr::null());
let selected = self.selected;
let selected = self.selected.map(|x| x as *mut bool).unwrap_or(ptr::null_mut());
let enabled = self.enabled;
unsafe {
ffi::igMenuItem(label, shortcut, selected, enabled)
ffi::igMenuItemPtr(label, shortcut, selected, enabled)
}
}
}

View File

@ -1,4 +1,3 @@
use libc::{c_float, c_int};
use std::marker::PhantomData;
use super::ffi;
@ -8,7 +7,7 @@ use super::{Frame, ImStr};
pub struct SliderInt<'fr, 'p> {
label: ImStr<'p>,
value: i32,
value: &'p mut i32,
min: i32,
max: i32,
display_format: ImStr<'p>,
@ -16,7 +15,7 @@ pub struct SliderInt<'fr, 'p> {
}
impl<'fr, 'p> SliderInt<'fr, 'p> {
pub fn new(label: ImStr<'p>, value: i32, min: i32, max: i32) -> Self {
pub fn new(label: ImStr<'p>, value: &'p mut i32, min: i32, max: i32) -> Self {
SliderInt {
label: label,
value: value,
@ -33,22 +32,18 @@ impl<'fr, 'p> SliderInt<'fr, 'p> {
.. self
}
}
pub fn build(self) -> Option<i32> {
let mut value = self.value;
let changed = unsafe {
ffi::igSliderInt(self.label.as_ptr(),
&mut value,
self.min, self.max,
pub fn build(self) -> bool {
unsafe {
ffi::igSliderInt(self.label.as_ptr(), self.value, self.min, self.max,
self.display_format.as_ptr()
)
};
if changed { Some(value) } else { None }
}
}
}
pub struct SliderFloat<'fr, 'p> {
label: ImStr<'p>,
value: f32,
value: &'p mut f32,
min: f32,
max: f32,
display_format: ImStr<'p>,
@ -57,7 +52,7 @@ pub struct SliderFloat<'fr, 'p> {
}
impl<'fr, 'p> SliderFloat<'fr, 'p> {
pub fn new(label: ImStr<'p>, value: f32, min: f32, max: f32) -> Self {
pub fn new(label: ImStr<'p>, value: &'p mut f32, min: f32, max: f32) -> Self {
SliderFloat {
label: label,
value: value,
@ -82,17 +77,13 @@ impl<'fr, 'p> SliderFloat<'fr, 'p> {
.. self
}
}
pub fn build(self) -> Option<f32> {
let mut value = self.value;
let changed = unsafe {
ffi::igSliderFloat(self.label.as_ptr(),
&mut value,
self.min, self.max,
pub fn build(self) -> bool {
unsafe {
ffi::igSliderFloat(self.label.as_ptr(), self.value, self.min, self.max,
self.display_format.as_ptr(),
self.power
)
};
if changed { Some(value) } else { None }
}
}
}

View File

@ -1,4 +1,3 @@
use libc::c_float;
use std::marker::PhantomData;
use std::ptr;
@ -20,7 +19,7 @@ pub struct Window<'fr, 'p> {
size: (f32, f32),
size_cond: ImGuiSetCond,
name: ImStr<'p>,
closable: bool,
opened: Option<&'p mut bool>,
bg_alpha: f32,
flags: ImGuiWindowFlags,
_phantom: PhantomData<&'fr Frame<'fr>>
@ -34,7 +33,7 @@ impl<'fr, 'p> Window<'fr, 'p> {
size: (0.0, 0.0),
size_cond: ImGuiSetCond::empty(),
name: unsafe { ImStr::from_bytes(b"Debug\0") },
closable: false,
opened: None,
bg_alpha: -1.0,
flags: ImGuiWindowFlags::empty(),
_phantom: PhantomData
@ -64,9 +63,9 @@ impl<'fr, 'p> Window<'fr, 'p> {
}
}
#[inline]
pub fn closable(self, closable: bool) -> Self {
pub fn opened(self, opened: &'p mut bool) -> Self {
Window {
closable: closable,
opened: Some(opened),
.. self
}
}
@ -160,8 +159,7 @@ impl<'fr, 'p> Window<'fr, 'p> {
.. self
}
}
pub fn build<F: FnOnce()>(self, f: F) -> bool {
let mut opened = true;
pub fn build<F: FnOnce()>(self, f: F) {
let render = unsafe {
if !self.pos_cond.is_empty() {
ffi::igSetNextWindowPos(ImVec2::new(self.pos.0, self.pos.1), self.pos_cond);
@ -170,7 +168,7 @@ impl<'fr, 'p> Window<'fr, 'p> {
ffi::igSetNextWindowSize(ImVec2::new(self.size.0, self.size.1), self.size_cond);
}
ffi::igBegin2(self.name.as_ptr(),
if self.closable { &mut opened } else { ptr::null_mut() },
self.opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut()),
ImVec2::new(0.0, 0.0), self.bg_alpha, self.flags
)
};
@ -178,6 +176,5 @@ impl<'fr, 'p> Window<'fr, 'p> {
f();
}
unsafe { ffi::igEnd() };
opened
}
}