mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-19 09:28:27 +00:00
Redesign combo box API
This commit is contained in:
parent
f05fd62c30
commit
c0b9571160
@ -15,6 +15,7 @@
|
||||
- Redesigned color editor/picker API
|
||||
- Redesigned child window API (previously known as child frame)
|
||||
- Redesigned image / image button API
|
||||
- Redesigned combo box API
|
||||
- Updated layout API
|
||||
- Renderer errors implement std::error::Error
|
||||
- Glium renderer re-exports imgui and glium
|
||||
|
||||
@ -26,8 +26,8 @@ struct State {
|
||||
no_close: bool,
|
||||
wrap_width: f32,
|
||||
buf: ImString,
|
||||
item: i32,
|
||||
item2: i32,
|
||||
item: usize,
|
||||
item2: usize,
|
||||
text: ImString,
|
||||
text_multiline: ImString,
|
||||
i0: i32,
|
||||
@ -45,7 +45,7 @@ struct State {
|
||||
color_edit: ColorEditState,
|
||||
custom_rendering: CustomRenderingState,
|
||||
dont_ask_me_next_time: bool,
|
||||
stacked_modals_item: i32,
|
||||
stacked_modals_item: usize,
|
||||
stacked_modals_color: [f32; 4],
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ impl Default for ColorEditState {
|
||||
struct FileMenuState {
|
||||
enabled: bool,
|
||||
f: f32,
|
||||
n: i32,
|
||||
n: usize,
|
||||
b: bool,
|
||||
}
|
||||
|
||||
@ -438,8 +438,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
||||
|
||||
ui.separator();
|
||||
ui.label_text(im_str!("label"), im_str!("Value"));
|
||||
ui.combo(
|
||||
im_str!("combo"),
|
||||
ComboBox::new(im_str!("combo")).build_simple_string(ui,
|
||||
&mut state.item,
|
||||
&[
|
||||
im_str!("aaaa"),
|
||||
@ -447,9 +446,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
||||
im_str!("cccc"),
|
||||
im_str!("dddd"),
|
||||
im_str!("eeee"),
|
||||
],
|
||||
-1,
|
||||
);
|
||||
]);
|
||||
let items = [
|
||||
im_str!("AAAA"),
|
||||
im_str!("BBBB"),
|
||||
@ -463,7 +460,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
||||
im_str!("JJJJ"),
|
||||
im_str!("KKKK"),
|
||||
];
|
||||
ui.combo(im_str!("combo scroll"), &mut state.item2, &items, -1);
|
||||
ComboBox::new(im_str!("combo scroll")).build_simple_string(ui, &mut state.item2, &items);
|
||||
ui.input_text(im_str!("input text"), &mut state.text)
|
||||
.build();
|
||||
ui.input_int(im_str!("input int"), &mut state.i0).build();
|
||||
@ -672,7 +669,7 @@ CTRL+click on individual component to input value.\n",
|
||||
);
|
||||
|
||||
let items = &[im_str!("aaaa"), im_str!("bbbb"), im_str!("cccc"), im_str!("dddd"), im_str!("eeee")];
|
||||
ui.combo(im_str!("Combo"), &mut state.stacked_modals_item, items, -1);
|
||||
ComboBox::new(im_str!("Combo")).build_simple_string(ui, &mut state.stacked_modals_item, items);
|
||||
|
||||
ColorEdit::new(im_str!("color"), &mut state.stacked_modals_color).build(ui);
|
||||
|
||||
@ -763,7 +760,7 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) {
|
||||
.step(0.1)
|
||||
.build();
|
||||
let items = [im_str!("Yes"), im_str!("No"), im_str!("Maybe")];
|
||||
ui.combo(im_str!("Combo"), &mut state.n, &items, -1);
|
||||
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(|| {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
use bitflags::bitflags;
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use crate::render::renderer::TextureId;
|
||||
use crate::string::ImStr;
|
||||
use crate::widget::color_editors::*;
|
||||
use crate::widget::combo_box::*;
|
||||
use crate::widget::image::{Image, ImageButton};
|
||||
use crate::widget::progress_bar::ProgressBar;
|
||||
use crate::window::{Window, WindowFlags, WindowFocusedFlags};
|
||||
@ -13,31 +14,8 @@ use crate::{Id, Ui};
|
||||
#[deprecated(since = "0.2.0", note = "use ColorEditFlags instead")]
|
||||
pub type ImGuiColorEditFlags = ColorEditFlags;
|
||||
|
||||
bitflags!(
|
||||
/// Flags for combo boxes
|
||||
#[repr(C)]
|
||||
pub struct ImGuiComboFlags: c_int {
|
||||
/// Align the popup toward the left by default
|
||||
const PopupAlignLeft = 1;
|
||||
/// Max ~4 items visible.
|
||||
const HeightSmall = 1 << 1;
|
||||
/// Max ~8 items visible (default)
|
||||
const HeightRegular = 1 << 2;
|
||||
/// Max ~20 items visible
|
||||
const HeightLarge = 1 << 3;
|
||||
/// As many fitting items as possible
|
||||
const HeightLargest = 1 << 4;
|
||||
/// Display on the preview box without the square arrow button
|
||||
const NoArrowButton = 1 << 5;
|
||||
/// Display only a square arrow button
|
||||
const NoPreview = 1 << 6;
|
||||
|
||||
const HeightMask = ImGuiComboFlags::HeightSmall.bits
|
||||
| ImGuiComboFlags::HeightRegular.bits
|
||||
| ImGuiComboFlags::HeightLarge.bits
|
||||
| ImGuiComboFlags::HeightLargest.bits;
|
||||
}
|
||||
);
|
||||
#[deprecated(since = "0.2.0", note = "use ComboFlags instead")]
|
||||
pub type ImGuiComboFlags = ComboBoxFlags;
|
||||
|
||||
bitflags!(
|
||||
/// Flags for igBeginDragDropSource(), igAcceptDragDropPayload()
|
||||
@ -359,3 +337,31 @@ impl<'ui> Ui<'ui> {
|
||||
ImageButton::new(texture, size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
#[deprecated(
|
||||
since = "0.2.0",
|
||||
note = "use imgui::ComboBox::new(...), and either build_simple(), build_simple_string(), or custom rendering (e.g. selectables) instead"
|
||||
)]
|
||||
pub fn combo<'p, StringType: AsRef<ImStr> + ?Sized>(
|
||||
&self,
|
||||
label: &'p ImStr,
|
||||
current_item: &mut i32,
|
||||
items: &'p [&'p StringType],
|
||||
height_in_items: i32,
|
||||
) -> bool {
|
||||
let items_inner: Vec<*const c_char> = items
|
||||
.into_iter()
|
||||
.map(|item| item.as_ref().as_ptr())
|
||||
.collect();
|
||||
unsafe {
|
||||
sys::igCombo(
|
||||
label.as_ptr(),
|
||||
current_item,
|
||||
items_inner.as_ptr() as *mut *const c_char,
|
||||
items_inner.len() as i32,
|
||||
height_in_items,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
src/lib.rs
26
src/lib.rs
@ -43,6 +43,7 @@ pub use self::style::*;
|
||||
pub use self::trees::{CollapsingHeader, TreeNode};
|
||||
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::progress_bar::*;
|
||||
pub use self::window::child_window::*;
|
||||
@ -557,31 +558,6 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
}
|
||||
|
||||
// Widgets: Combos
|
||||
impl<'ui> Ui<'ui> {
|
||||
pub fn combo<'p, StringType: AsRef<ImStr> + ?Sized>(
|
||||
&self,
|
||||
label: &'p ImStr,
|
||||
current_item: &mut i32,
|
||||
items: &'p [&'p StringType],
|
||||
height_in_items: i32,
|
||||
) -> bool {
|
||||
let items_inner: Vec<*const c_char> = items
|
||||
.into_iter()
|
||||
.map(|item| item.as_ref().as_ptr())
|
||||
.collect();
|
||||
unsafe {
|
||||
sys::igCombo(
|
||||
label.as_ptr(),
|
||||
current_item,
|
||||
items_inner.as_ptr() as *mut *const c_char,
|
||||
items_inner.len() as i32,
|
||||
height_in_items,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Widgets: ListBox
|
||||
impl<'ui> Ui<'ui> {
|
||||
pub fn list_box<'p, StringType: AsRef<ImStr> + ?Sized>(
|
||||
|
||||
225
src/widget/combo_box.rs
Normal file
225
src/widget/combo_box.rs
Normal file
@ -0,0 +1,225 @@
|
||||
use bitflags::bitflags;
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
// TODO: support size constraints
|
||||
|
||||
/// Combo box height mode.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ComboBoxHeight {
|
||||
/// Max ~4 items visible.
|
||||
Small,
|
||||
/// Max ~8 items visible.
|
||||
Regular,
|
||||
/// Max ~20 items visible.
|
||||
Large,
|
||||
/// As many fitting items as possible visible.
|
||||
Largest,
|
||||
}
|
||||
|
||||
/// Combo box preview mode.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ComboBoxPreviewMode {
|
||||
/// Show only a box with the preview value
|
||||
Label,
|
||||
/// Show only an arrow button
|
||||
ArrowButton,
|
||||
/// Show a box with the preview value and an arrow button
|
||||
Full,
|
||||
}
|
||||
|
||||
bitflags!(
|
||||
/// Flags for combo boxes
|
||||
#[repr(transparent)]
|
||||
pub struct ComboBoxFlags: u32 {
|
||||
/// Align the popup toward the left by default
|
||||
const POPUP_ALIGN_LEFT = sys::ImGuiComboFlags_PopupAlignLeft;
|
||||
/// Max ~4 items visible.
|
||||
const HEIGHT_SMALL = sys::ImGuiComboFlags_HeightSmall;
|
||||
/// Max ~8 items visible (default)
|
||||
const HEIGHT_REGULAR = sys::ImGuiComboFlags_HeightRegular;
|
||||
/// Max ~20 items visible
|
||||
const HEIGHT_LARGE = sys::ImGuiComboFlags_HeightLarge;
|
||||
/// As many fitting items as possible
|
||||
const HEIGHT_LARGEST = sys::ImGuiComboFlags_HeightLargest;
|
||||
/// Display on the preview box without the square arrow button
|
||||
const NO_ARROW_BUTTON = sys::ImGuiComboFlags_NoArrowButton;
|
||||
/// Display only a square arrow button
|
||||
const NO_PREVIEW = sys::ImGuiComboFlags_NoPreview;
|
||||
}
|
||||
);
|
||||
|
||||
/// Builder for a combo box widget
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use]
|
||||
pub struct ComboBox<'a> {
|
||||
label: &'a ImStr,
|
||||
preview_value: Option<&'a ImStr>,
|
||||
flags: ComboBoxFlags,
|
||||
}
|
||||
|
||||
impl<'a> ComboBox<'a> {
|
||||
/// Constructs a new combo box builder.
|
||||
pub fn new(label: &'a ImStr) -> ComboBox<'a> {
|
||||
ComboBox {
|
||||
label,
|
||||
preview_value: None,
|
||||
flags: ComboBoxFlags::empty(),
|
||||
}
|
||||
}
|
||||
/// Sets the preview value displayed in the preview box (if visible).
|
||||
#[inline]
|
||||
pub fn preview_value(mut self, preview_value: &'a ImStr) -> Self {
|
||||
self.preview_value = Some(preview_value);
|
||||
self
|
||||
}
|
||||
/// Replaces all current settings with the given flags.
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
/// Enables/disables aligning the combo box popup toward the left.
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[inline]
|
||||
pub fn popup_align_left(mut self, popup_align_left: bool) -> Self {
|
||||
self.flags
|
||||
.set(ComboBoxFlags::POPUP_ALIGN_LEFT, popup_align_left);
|
||||
self
|
||||
}
|
||||
/// Sets the combo box height.
|
||||
///
|
||||
/// Default: `ComboBoxHeight::Regular`
|
||||
#[inline]
|
||||
pub fn height(mut self, height: ComboBoxHeight) -> Self {
|
||||
self.flags
|
||||
.set(ComboBoxFlags::HEIGHT_SMALL, height == ComboBoxHeight::Small);
|
||||
self.flags.set(
|
||||
ComboBoxFlags::HEIGHT_REGULAR,
|
||||
height == ComboBoxHeight::Regular,
|
||||
);
|
||||
self.flags
|
||||
.set(ComboBoxFlags::HEIGHT_LARGE, height == ComboBoxHeight::Large);
|
||||
self.flags.set(
|
||||
ComboBoxFlags::HEIGHT_LARGEST,
|
||||
height == ComboBoxHeight::Largest,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Sets the combo box preview mode.
|
||||
///
|
||||
/// Default: `ComboBoxPreviewMode::Full`
|
||||
#[inline]
|
||||
pub fn preview_mode(mut self, preview_mode: ComboBoxPreviewMode) -> Self {
|
||||
self.flags.set(
|
||||
ComboBoxFlags::NO_ARROW_BUTTON,
|
||||
preview_mode == ComboBoxPreviewMode::Label,
|
||||
);
|
||||
self.flags.set(
|
||||
ComboBoxFlags::NO_PREVIEW,
|
||||
preview_mode == ComboBoxPreviewMode::ArrowButton,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Builds this combo box, and starts appending to it.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
pub fn begin<'ui>(self, _: &'ui Ui<'ui>) -> Option<ComboBoxToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginCombo(
|
||||
self.label.as_ptr(),
|
||||
self.preview_value.map(ImStr::as_ptr).unwrap_or(ptr::null()),
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
};
|
||||
if should_render {
|
||||
Some(ComboBoxToken {
|
||||
should_end: true,
|
||||
_ui: PhantomData,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Builds the combo box.
|
||||
///
|
||||
/// Note: the closure is not called if the combo box is not open
|
||||
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
|
||||
if let Some(_combo) = self.begin(ui) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a combo box pushed to the window stack
|
||||
pub struct ComboBoxToken<'ui> {
|
||||
should_end: bool,
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
}
|
||||
|
||||
impl<'ui> ComboBoxToken<'ui> {
|
||||
/// Finishes the current combo box and pops it from the window stack
|
||||
pub fn end(mut self) {
|
||||
self.should_end = false;
|
||||
unsafe { sys::igEndCombo() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for ComboBoxToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
if self.should_end {
|
||||
unsafe { sys::igEndCombo() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Convenience functions
|
||||
impl<'a> ComboBox<'a> {
|
||||
/// Builds a simple combo box for choosing from a slice of values
|
||||
pub fn build_simple<T, L>(
|
||||
self,
|
||||
ui: &Ui,
|
||||
current_item: &mut usize,
|
||||
items: &[T],
|
||||
label_fn: &L,
|
||||
) -> bool
|
||||
where
|
||||
for<'b> L: Fn(&'b T) -> Cow<'b, ImStr>,
|
||||
{
|
||||
let mut result = false;
|
||||
let mut cb = self;
|
||||
let preview_value = items.get(*current_item).map(label_fn);
|
||||
if cb.preview_value.is_none() {
|
||||
if let Some(preview_value) = preview_value.as_ref() {
|
||||
cb = cb.preview_value(preview_value);
|
||||
}
|
||||
}
|
||||
if let Some(_cb) = cb.begin(ui) {
|
||||
for (idx, item) in items.iter().enumerate() {
|
||||
let text = label_fn(item);
|
||||
let selected = idx == *current_item;
|
||||
// TODO: use safe API
|
||||
let change =
|
||||
unsafe { sys::igSelectable(text.as_ptr(), selected, 0, [0.0, 0.0].into()) };
|
||||
if change {
|
||||
*current_item = idx;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
/// Builds a simple combo box for choosing from a slice of strings
|
||||
pub fn build_simple_string<S>(self, ui: &Ui, current_item: &mut usize, items: &[&S]) -> bool
|
||||
where
|
||||
S: AsRef<ImStr> + ?Sized,
|
||||
{
|
||||
self.build_simple(ui, current_item, items, &|&s| s.as_ref().into())
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
pub mod color_editors;
|
||||
pub mod combo_box;
|
||||
pub mod image;
|
||||
pub mod misc;
|
||||
pub mod progress_bar;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user