From eef67ddecc3219733dfb4ff273c09329a1793c6f Mon Sep 17 00:00:00 2001 From: Joonas Javanainen Date: Fri, 21 Aug 2015 00:08:40 +0300 Subject: [PATCH] Use mutable references instead of return values It seems to be more ergonomical with the underlying library --- README.markdown | 2 +- examples/hello_world.rs | 2 +- examples/support/mod.rs | 9 ++- examples/test_window.rs | 149 ++++++++++++++++------------------------ src/lib.rs | 24 +++---- src/menus.rs | 12 ++-- src/sliders.rs | 33 ++++----- src/window.rs | 15 ++-- 8 files changed, 97 insertions(+), 149 deletions(-) diff --git a/README.markdown b/README.markdown index cea3821..68eb210 100644 --- a/README.markdown +++ b/README.markdown @@ -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) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 1095da7..7931959 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -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) diff --git a/examples/support/mod.rs b/examples/support/mod.rs index 126d6d5..ef2d552 100644 --- a/examples/support/mod.rs +++ b/examples/support/mod.rs @@ -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 } } diff --git a/examples/test_window.rs b/examples/test_window.rs index c8d42a0..3cc6e7c 100644 --- a/examples/test_window.rs +++ b/examples/test_window.rs @@ -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) diff --git a/src/lib.rs b/src/lib.rs index c127ada..c228d24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { - 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) } } diff --git a/src/menus.rs b/src/menus.rs index 28db298..56f8398 100644 --- a/src/menus.rs +++ b/src/menus.rs @@ -37,7 +37,7 @@ impl<'fr, 'p> Menu<'fr, 'p> { pub struct MenuItem<'fr, 'p> { label: ImStr<'p>, shortcut: Option>, - 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) } } } diff --git a/src/sliders.rs b/src/sliders.rs index 385d694..34ba152 100644 --- a/src/sliders.rs +++ b/src/sliders.rs @@ -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 { - 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 { - 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 } + } } } diff --git a/src/window.rs b/src/window.rs index 87f3563..e568445 100644 --- a/src/window.rs +++ b/src/window.rs @@ -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(self, f: F) -> bool { - let mut opened = true; + pub fn build(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 } }