added richer support for ListBox

This commit is contained in:
Willem Verstraeten 2020-12-11 14:53:21 +01:00 committed by Thom Chiovoloni
parent e7b8af5939
commit 9210130872
4 changed files with 183 additions and 0 deletions

View File

@ -28,6 +28,7 @@ struct State {
buf: ImString,
item: usize,
item2: usize,
item3: i32,
text: ImString,
text_multiline: ImString,
i0: i32,
@ -39,6 +40,7 @@ struct State {
col1: [f32; 3],
col2: [f32; 4],
selected_fish: Option<usize>,
selected_fish2: Option<usize>,
auto_resize_state: AutoResizeState,
file_menu: FileMenuState,
radio_button: i32,
@ -85,6 +87,7 @@ impl Default for State {
buf,
item: 0,
item2: 0,
item3: 0,
text,
text_multiline,
i0: 123,
@ -96,6 +99,7 @@ impl Default for State {
col1: [1.0, 0.0, 0.2],
col2: [0.4, 0.7, 0.0, 0.5],
selected_fish: None,
selected_fish2: None,
auto_resize_state: Default::default(),
file_menu: Default::default(),
radio_button: 0,
@ -490,6 +494,37 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
im_str!("KKKK"),
];
ComboBox::new(im_str!("combo scroll")).build_simple_string(ui, &mut state.item2, &items);
ui.list_box(im_str!("list"), &mut state.item3, &items, 8);
let names = [
im_str!("Bream"),
im_str!("Haddock"),
im_str!("Mackerel"),
im_str!("Pollock"),
im_str!("Tilefish"),
];
ListBox::new(im_str!("selectables list")).calculate_size(8, 4).build(ui, || {
for (index, name) in names.iter().enumerate() {
let selected = matches!(state.selected_fish2, Some(i) if i == index );
if Selectable::new(name).selected(selected).build(ui) {
state.selected_fish2 = Some(index);
}
}
});
let last_size = ui.item_rect_size();
ListBox::new(im_str!("selectable list 2")).size([0.0, last_size[1] * 0.66]).build(ui, || {
for (index, name) in names.iter().enumerate() {
let selected = matches!(state.selected_fish2, Some(i) if i == index );
if Selectable::new(name).selected(selected).build(ui) {
state.selected_fish2 = Some(index);
}
}
});
ui.input_text(im_str!("input text"), &mut state.text)
.build();
ui.input_int(im_str!("input int"), &mut state.i0).build();

View File

@ -36,6 +36,7 @@ pub use self::widget::color_editors::*;
pub use self::widget::combo_box::*;
pub use self::widget::drag::*;
pub use self::widget::image::*;
pub use self::widget::list_box::*;
pub use self::widget::menu::*;
pub use self::widget::progress_bar::*;
pub use self::widget::selectable::*;

View File

@ -0,0 +1,146 @@
use std::borrow::Cow;
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::string::ImStr;
use crate::sys;
use crate::Ui;
#[derive(Copy, Clone, Debug)]
enum Size {
Vec {
size: sys::ImVec2,
},
Items {
items_count: i32,
height_in_items: i32,
},
}
/// Builder for a list box widget
#[derive(Copy, Clone, Debug)]
#[must_use]
pub struct ListBox<'a> {
label: &'a ImStr,
size: Size,
}
impl<'a> ListBox<'a> {
/// Constructs a new list box builder.
pub fn new(label: &'a ImStr) -> ListBox<'a> {
ListBox {
label,
size: Size::Vec {
size: [0.0, 0.0].into(),
},
}
}
/// Sets the list box size based on the number of items that you want to make visible
/// Size default to hold ~7.25 items.
/// We add +25% worth of item height to allow the user to see at a glance if there are more items up/down, without looking at the scrollbar.
/// We don't add this extra bit if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
#[inline]
pub fn calculate_size(mut self, items_count: i32, height_in_items: i32) -> Self {
self.size = Size::Items {
items_count,
height_in_items,
};
self
}
/// Sets the list box size based on the given width and height
/// If width or height are 0 or smaller, a default value is calculated
/// Helper to calculate the size of a listbox and display a label on the right.
/// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty"
///
/// Default: [0.0, 0.0], in which case the combobox calculates a sensible width and height
#[inline]
pub fn size(mut self, size: [f32; 2]) -> Self {
self.size = Size::Vec { size: size.into() };
self
}
/// Creates a list box and starts appending to it.
///
/// Returns `Some(ListBoxToken)` if the list box is open. After content has been
/// rendered, the token must be ended by calling `.end()`.
///
/// Returns `None` if the list box is not open and no content should be rendered.
#[must_use]
pub fn begin(self, ui: &Ui) -> Option<ListBoxToken> {
let should_render = unsafe {
match self.size {
Size::Vec { size } => sys::igListBoxHeaderVec2(self.label.as_ptr(), size),
Size::Items {
items_count,
height_in_items,
} => sys::igListBoxHeaderInt(self.label.as_ptr(), items_count, height_in_items),
}
};
if should_render {
Some(ListBoxToken { ctx: ui.ctx })
} else {
None
}
}
/// Creates a list box and runs a closure to construct the list contents.
///
/// Note: the closure is not called if the list box is not open.
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(list) = self.begin(ui) {
f();
list.end(ui);
}
}
}
/// Tracks a list box that must be ended by calling `.end()`
#[must_use]
pub struct ListBoxToken {
ctx: *const Context,
}
impl ListBoxToken {
/// Ends a list box
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igListBoxFooter() };
}
}
impl Drop for ListBoxToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A ListBoxToken was leaked. Did you call .end()?");
}
}
}
/// # Convenience functions
impl<'a> ListBox<'a> {
/// Builds a simple list 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>,
{
use crate::widget::selectable::Selectable;
let mut result = false;
let lb = self;
if let Some(_cb) = lb.begin(ui) {
for (idx, item) in items.iter().enumerate() {
let text = label_fn(item);
let selected = idx == *current_item;
if Selectable::new(&text).selected(selected).build(ui) {
*current_item = idx;
result = true;
}
}
_cb.end(ui);
}
result
}
}

View File

@ -2,6 +2,7 @@ pub mod color_editors;
pub mod combo_box;
pub mod drag;
pub mod image;
pub mod list_box;
pub mod menu;
pub mod misc;
pub mod progress_bar;