Redesign menu API

This commit is contained in:
Joonas Javanainen 2019-07-13 15:27:24 +03:00
parent 664efd91a7
commit 83c2503134
No known key found for this signature in database
GPG Key ID: D39CCA5CB19B9179
7 changed files with 236 additions and 209 deletions

View File

@ -19,6 +19,7 @@
- Redesigned selectable API
- Redesigned slider API. Generic scalar sliders support all main data types and replace
previous individual sliders (int, int2, int3, int4, etc...)
- Redesigned menu API
- Updated layout API
- Renderer errors implement std::error::Error
- Glium renderer re-exports imgui and glium

View File

@ -285,57 +285,43 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
window.build(ui, || {
let _token = ui.push_item_width(-140.0);
ui.text(format!("dear imgui says hello. ({})", imgui::dear_imgui_version()));
ui.menu_bar(|| {
ui.menu(im_str!("Menu")).build(|| {
if let Some(_) = ui.menu_bar() {
if let Some(_) = ui.menu(im_str!("Menu"), true) {
show_example_menu_file(ui, &mut state.file_menu);
});
ui.menu(im_str!("Examples")).build(|| {
ui.menu_item(im_str!("Main menu bar"))
.selected(&mut state.show_app_main_menu_bar)
.build();
ui.menu_item(im_str!("Console"))
.selected(&mut state.show_app_console)
.build();
ui.menu_item(im_str!("Log"))
.selected(&mut state.show_app_log)
.build();
ui.menu_item(im_str!("Simple layout"))
.selected(&mut state.show_app_layout)
.build();
ui.menu_item(im_str!("Property editor"))
.selected(&mut state.show_app_property_editor)
.build();
ui.menu_item(im_str!("Long text display"))
.selected(&mut state.show_app_long_text)
.build();
ui.menu_item(im_str!("Auto-resizing window"))
.selected(&mut state.show_app_auto_resize)
.build();
ui.menu_item(im_str!("Constrained-resizing window"))
.selected(&mut state.show_app_constrained_resize)
.build();
ui.menu_item(im_str!("Simple overlay"))
.selected(&mut state.show_app_fixed_overlay)
.build();
ui.menu_item(im_str!("Manipulating window title"))
.selected(&mut state.show_app_manipulating_window_title)
.build();
ui.menu_item(im_str!("Custom rendering"))
.selected(&mut state.show_app_custom_rendering)
.build();
});
ui.menu(im_str!("Help")).build(|| {
ui.menu_item(im_str!("Metrics"))
.selected(&mut state.show_app_metrics)
.build();
ui.menu_item(im_str!("Style Editor"))
.selected(&mut state.show_app_style_editor)
.build();
ui.menu_item(im_str!("About ImGui"))
.selected(&mut state.show_app_about)
.build();
});
});
}
if let Some(_) = ui.menu(im_str!("Examples"), true) {
MenuItem::new(im_str!("Main menu bar"))
.build_with_ref(ui, &mut state.show_app_main_menu_bar);
MenuItem::new(im_str!("Console"))
.build_with_ref(ui, &mut state.show_app_console);
MenuItem::new(im_str!("Log"))
.build_with_ref(ui, &mut state.show_app_log);
MenuItem::new(im_str!("Simple layout"))
.build_with_ref(ui, &mut state.show_app_layout);
MenuItem::new(im_str!("Property editor"))
.build_with_ref(ui, &mut state.show_app_property_editor);
MenuItem::new(im_str!("Long text display"))
.build_with_ref(ui, &mut state.show_app_long_text);
MenuItem::new(im_str!("Auto-resizing window"))
.build_with_ref(ui, &mut state.show_app_auto_resize);
MenuItem::new(im_str!("Constrained-resizing window"))
.build_with_ref(ui, &mut state.show_app_constrained_resize);
MenuItem::new(im_str!("Simple overlay"))
.build_with_ref(ui, &mut state.show_app_fixed_overlay);
MenuItem::new(im_str!("Manipulating window title"))
.build_with_ref(ui, &mut state.show_app_manipulating_window_title);
MenuItem::new(im_str!("Custom rendering"))
.build_with_ref(ui, &mut state.show_app_custom_rendering);
}
if let Some(_) = ui.menu(im_str!("Help"), true) {
MenuItem::new(im_str!("Metrics"))
.build_with_ref(ui, &mut state.show_app_metrics);
MenuItem::new(im_str!("Style Editor"))
.build_with_ref(ui, &mut state.show_app_style_editor);
MenuItem::new(im_str!("About ImGui"))
.build_with_ref(ui, &mut state.show_app_about);
}
}
ui.spacing();
if ui.collapsing_header(im_str!("Help")).build() {
ui.text_wrapped(im_str!(
@ -688,59 +674,59 @@ CTRL+click on individual component to input value.\n",
}
fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) {
ui.main_menu_bar(|| {
ui.menu(im_str!("File")).build(|| {
if let Some(_) = ui.main_menu_bar() {
if let Some(_) = ui.menu(im_str!("File"), true) {
show_example_menu_file(ui, &mut state.file_menu);
});
ui.menu(im_str!("Edit")).build(|| {
ui.menu_item(im_str!("Undo"))
}
if let Some(_) = ui.menu(im_str!("Edit"), true) {
MenuItem::new(im_str!("Undo"))
.shortcut(im_str!("CTRL+Z"))
.build();
ui.menu_item(im_str!("Redo"))
.build(ui);
MenuItem::new(im_str!("Redo"))
.shortcut(im_str!("CTRL+Y"))
.enabled(false)
.build();
.build(ui);
ui.separator();
ui.menu_item(im_str!("Cut"))
MenuItem::new(im_str!("Cut"))
.shortcut(im_str!("CTRL+X"))
.build();
ui.menu_item(im_str!("Copy"))
.build(ui);
MenuItem::new(im_str!("Copy"))
.shortcut(im_str!("CTRL+C"))
.build();
ui.menu_item(im_str!("Paste"))
.build(ui);
MenuItem::new(im_str!("Paste"))
.shortcut(im_str!("CTRL+V"))
.build();
});
});
.build(ui);
}
}
}
fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) {
ui.menu_item(im_str!("(dummy menu)")).enabled(false).build();
ui.menu_item(im_str!("New")).build();
ui.menu_item(im_str!("Open"))
MenuItem::new(im_str!("(dummy menu)"))
.enabled(false)
.build(ui);
MenuItem::new(im_str!("New")).build(ui);
MenuItem::new(im_str!("Open"))
.shortcut(im_str!("Ctrl+O"))
.build();
ui.menu(im_str!("Open Recent")).build(|| {
ui.menu_item(im_str!("fish_hat.c")).build();
ui.menu_item(im_str!("fish_hat.inl")).build();
ui.menu_item(im_str!("fish_hat.h")).build();
ui.menu(im_str!("More..")).build(|| {
ui.menu_item(im_str!("Hello")).build();
ui.menu_item(im_str!("Sailor")).build();
ui.menu(im_str!("Recurse..")).build(|| {
.build(ui);
if let Some(_) = ui.menu(im_str!("Open Recent"), true) {
MenuItem::new(im_str!("fish_hat.c")).build(ui);
MenuItem::new(im_str!("fish_hat.inl")).build(ui);
MenuItem::new(im_str!("fish_hat.h")).build(ui);
if let Some(_) = ui.menu(im_str!("More.."), true) {
MenuItem::new(im_str!("Hello")).build(ui);
MenuItem::new(im_str!("Sailor")).build(ui);
if let Some(_) = ui.menu(im_str!("Recurse.."), true) {
show_example_menu_file(ui, state);
});
});
});
ui.menu_item(im_str!("Save"))
}
}
}
MenuItem::new(im_str!("Save"))
.shortcut(im_str!("Ctrl+S"))
.build();
ui.menu_item(im_str!("Save As..")).build();
.build(ui);
MenuItem::new(im_str!("Save As..")).build(ui);
ui.separator();
ui.menu(im_str!("Options")).build(|| {
ui.menu_item(im_str!("Enabled"))
.selected(&mut state.enabled)
.build();
if let Some(_) = ui.menu(im_str!("Options"), true) {
MenuItem::new(im_str!("Enabled")).build_with_ref(ui, &mut state.enabled);
ChildWindow::new("child")
.size([0.0, 60.0])
.border(true)
@ -757,19 +743,19 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) {
let items = [im_str!("Yes"), im_str!("No"), im_str!("Maybe")];
ComboBox::new(im_str!("Combo")).build_simple_string(ui, &mut state.n, &items);
ui.checkbox(im_str!("Check"), &mut state.b);
});
ui.menu(im_str!("Colors")).build(|| {
}
if let Some(_) = ui.menu(im_str!("Colors"), true) {
for &col in StyleColor::VARIANTS.iter() {
ui.menu_item(&im_str!("{:?}", col)).build();
MenuItem::new(&im_str!("{:?}", col)).build(ui);
}
});
ui.menu(im_str!("Disabled")).enabled(false).build(|| {
}
if let Some(_) = ui.menu(im_str!("Disabled"), false) {
unreachable!();
});
ui.menu_item(im_str!("Checked")).selected(&mut true).build();
ui.menu_item(im_str!("Quit"))
}
MenuItem::new(im_str!("Checked")).selected(true).build(ui);
MenuItem::new(im_str!("Quit"))
.shortcut(im_str!("Alt+F4"))
.build();
.build(ui);
}
fn show_example_app_auto_resize(ui: &Ui, state: &mut AutoResizeState, opened: &mut bool) {

View File

@ -7,6 +7,7 @@ use crate::string::ImStr;
use crate::widget::color_editors::*;
use crate::widget::combo_box::*;
use crate::widget::image::{Image, ImageButton};
use crate::widget::menu::*;
use crate::widget::progress_bar::ProgressBar;
use crate::widget::selectable::*;
use crate::window::{Window, WindowFlags, WindowFocusedFlags};
@ -367,3 +368,10 @@ impl<'ui> Ui<'ui> {
unsafe { sys::igSelectable(label.as_ptr(), selected, flags.bits() as i32, size.into()) }
}
}
impl<'ui> Ui<'ui> {
#[deprecated(since = "0.2.0", note = "use imgui::MenuItem::new(...) instead")]
pub fn menu_item<'a>(&self, label: &'a ImStr) -> MenuItem<'a> {
MenuItem::new(label)
}
}

View File

@ -27,7 +27,6 @@ pub use self::input_widget::{
};
pub use self::io::*;
pub use self::legacy::*;
pub use self::menus::{Menu, MenuItem};
pub use self::plothistogram::PlotHistogram;
pub use self::plotlines::PlotLines;
pub use self::popup_modal::PopupModal;
@ -41,6 +40,7 @@ pub use self::utils::*;
pub use self::widget::color_editors::*;
pub use self::widget::combo_box::*;
pub use self::widget::image::*;
pub use self::widget::menu::*;
pub use self::widget::progress_bar::*;
pub use self::widget::selectable::*;
pub use self::widget::slider::*;
@ -60,7 +60,6 @@ pub mod internal;
mod io;
mod layout;
mod legacy;
mod menus;
mod plothistogram;
mod plotlines;
mod popup_modal;
@ -393,36 +392,6 @@ impl<'ui> Ui<'ui> {
}
}
// Widgets: Menus
impl<'ui> Ui<'ui> {
pub fn main_menu_bar<F>(&self, f: F)
where
F: FnOnce(),
{
let render = unsafe { sys::igBeginMainMenuBar() };
if render {
f();
unsafe { sys::igEndMainMenuBar() };
}
}
pub fn menu_bar<F>(&self, f: F)
where
F: FnOnce(),
{
let render = unsafe { sys::igBeginMenuBar() };
if render {
f();
unsafe { sys::igEndMenuBar() };
}
}
pub fn menu<'p>(&self, label: &'p ImStr) -> Menu<'ui, 'p> {
Menu::new(self, label)
}
pub fn menu_item<'p>(&self, label: &'p ImStr) -> MenuItem<'ui, 'p> {
MenuItem::new(self, label)
}
}
// Widgets: Popups
impl<'ui> Ui<'ui> {
pub fn open_popup<'p>(&self, str_id: &'p ImStr) {

View File

@ -1,80 +0,0 @@
use std::marker::PhantomData;
use std::ptr;
use sys;
use super::{ImStr, Ui};
#[must_use]
pub struct Menu<'ui, 'p> {
label: &'p ImStr,
enabled: bool,
_phantom: PhantomData<&'ui Ui<'ui>>,
}
impl<'ui, 'p> Menu<'ui, 'p> {
pub fn new(_: &Ui<'ui>, label: &'p ImStr) -> Self {
Menu {
label,
enabled: true,
_phantom: PhantomData,
}
}
#[inline]
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn build<F: FnOnce()>(self, f: F) {
let render = unsafe { sys::igBeginMenu(self.label.as_ptr(), self.enabled) };
if render {
f();
unsafe { sys::igEndMenu() };
}
}
}
#[must_use]
pub struct MenuItem<'ui, 'p> {
label: &'p ImStr,
shortcut: Option<&'p ImStr>,
selected: Option<&'p mut bool>,
enabled: bool,
_phantom: PhantomData<&'ui Ui<'ui>>,
}
impl<'ui, 'p> MenuItem<'ui, 'p> {
pub fn new(_: &Ui<'ui>, label: &'p ImStr) -> Self {
MenuItem {
label,
shortcut: None,
selected: None,
enabled: true,
_phantom: PhantomData,
}
}
#[inline]
pub fn shortcut(mut self, shortcut: &'p ImStr) -> Self {
self.shortcut = Some(shortcut);
self
}
#[inline]
pub fn selected(mut self, selected: &'p mut bool) -> Self {
self.selected = Some(selected);
self
}
#[inline]
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
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
.map(|x| x as *mut bool)
.unwrap_or(ptr::null_mut());
let enabled = self.enabled;
unsafe { sys::igMenuItemBoolPtr(label, shortcut, selected, enabled) }
}
}

142
src/widget/menu.rs Normal file
View File

@ -0,0 +1,142 @@
use std::marker::PhantomData;
use std::ptr;
use crate::string::ImStr;
use crate::sys;
use crate::Ui;
/// # Widgets: Menus
impl<'ui> Ui<'ui> {
/// Creates and starts appending to a full-screen menu bar.
///
/// Returns `None` if the menu bar is not visible and no content should be rendered.
pub fn main_menu_bar<'a>(&'a self) -> Option<MainMenuBarToken<'a>> {
match unsafe { sys::igBeginMainMenuBar() } {
true => Some(MainMenuBarToken { _ui: PhantomData }),
false => None,
}
}
/// Creates and starts appending to the menu bar of the current window.
///
/// Returns `None` if the menu bar is not visible and no content should be rendered.
pub fn menu_bar<'a>(&'a self) -> Option<MenuBarToken<'a>> {
match unsafe { sys::igBeginMenuBar() } {
true => Some(MenuBarToken { _ui: PhantomData }),
false => None,
}
}
/// Creates and starts appending to a sub-menu entry.
///
/// Returns `None` if the menu is not visible and no content should be rendered.
pub fn menu<'a>(&'a self, label: &ImStr, enabled: bool) -> Option<MenuToken<'a>> {
match unsafe { sys::igBeginMenu(label.as_ptr(), enabled) } {
true => Some(MenuToken { _ui: PhantomData }),
false => None,
}
}
}
/// Builder for a menu item.
#[derive(Copy, Clone, Debug)]
#[must_use]
pub struct MenuItem<'a> {
label: &'a ImStr,
shortcut: Option<&'a ImStr>,
selected: bool,
enabled: bool,
}
impl<'a> MenuItem<'a> {
/// Construct a new menu item builder.
pub fn new(label: &ImStr) -> MenuItem {
MenuItem {
label,
shortcut: None,
selected: false,
enabled: true,
}
}
/// Sets the menu item shortcut.
///
/// Shortcuts are displayed for convenience only and are not automatically handled.
#[inline]
pub fn shortcut(mut self, shortcut: &'a ImStr) -> Self {
self.shortcut = Some(shortcut);
self
}
/// Sets the selected state of the menu item.
///
/// Default: false
#[inline]
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
/// Enables/disables the menu item.
///
/// Default: enabled
#[inline]
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
/// Builds the menu item.
///
/// Returns true if the menu item is activated.
pub fn build(self, _: &Ui) -> bool {
unsafe {
sys::igMenuItemBool(
self.label.as_ptr(),
self.shortcut.map(ImStr::as_ptr).unwrap_or(ptr::null()),
self.selected,
self.enabled,
)
}
}
}
/// # Convenience functions
impl<'a> MenuItem<'a> {
/// Builds the menu item using a mutable reference to selected state.
pub fn build_with_ref(self, ui: &Ui, selected: &mut bool) -> bool {
if self.selected(*selected).build(ui) {
*selected = true;
true
} else {
false
}
}
}
/// Represents a main menu bar
pub struct MainMenuBarToken<'ui> {
_ui: PhantomData<&'ui Ui<'ui>>,
}
impl<'ui> Drop for MainMenuBarToken<'ui> {
fn drop(&mut self) {
unsafe { sys::igEndMainMenuBar() };
}
}
/// Represents a menu bar
pub struct MenuBarToken<'ui> {
_ui: PhantomData<&'ui Ui<'ui>>,
}
impl<'ui> Drop for MenuBarToken<'ui> {
fn drop(&mut self) {
unsafe { sys::igEndMenuBar() };
}
}
/// Represents a menu
pub struct MenuToken<'ui> {
_ui: PhantomData<&'ui Ui<'ui>>,
}
impl<'ui> Drop for MenuToken<'ui> {
fn drop(&mut self) {
unsafe { sys::igEndMenu() };
}
}

View File

@ -1,6 +1,7 @@
pub mod color_editors;
pub mod combo_box;
pub mod image;
pub mod menu;
pub mod misc;
pub mod progress_bar;
pub mod selectable;