use bitflags::bitflags;
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, PartialEq, Eq)]
pub enum ComboBoxHeight {
Small,
Regular,
Large,
Largest,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ComboBoxPreviewMode {
Label,
ArrowButton,
Full,
}
bitflags!(
#[repr(transparent)]
pub struct ComboBoxFlags: u32 {
const POPUP_ALIGN_LEFT = sys::ImGuiComboFlags_PopupAlignLeft;
const HEIGHT_SMALL = sys::ImGuiComboFlags_HeightSmall;
const HEIGHT_REGULAR = sys::ImGuiComboFlags_HeightRegular;
const HEIGHT_LARGE = sys::ImGuiComboFlags_HeightLarge;
const HEIGHT_LARGEST = sys::ImGuiComboFlags_HeightLargest;
const NO_ARROW_BUTTON = sys::ImGuiComboFlags_NoArrowButton;
const NO_PREVIEW = sys::ImGuiComboFlags_NoPreview;
}
);
#[derive(Copy, Clone, Debug)]
#[must_use]
pub struct ComboBox<'a> {
label: &'a ImStr,
preview_value: Option<&'a ImStr>,
flags: ComboBoxFlags,
}
impl<'a> ComboBox<'a> {
pub fn new(label: &'a ImStr) -> ComboBox<'a> {
ComboBox {
label,
preview_value: None,
flags: ComboBoxFlags::empty(),
}
}
#[inline]
pub fn preview_value(mut self, preview_value: &'a ImStr) -> Self {
self.preview_value = Some(preview_value);
self
}
#[inline]
pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
self.flags = flags;
self
}
#[inline]
pub fn popup_align_left(mut self, popup_align_left: bool) -> Self {
self.flags
.set(ComboBoxFlags::POPUP_ALIGN_LEFT, popup_align_left);
self
}
#[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
}
#[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
}
#[must_use]
pub fn begin(self, ui: &Ui) -> Option<ComboBoxToken> {
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 { ctx: ui.ctx })
} else {
None
}
}
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(combo) = self.begin(ui) {
f();
combo.end(ui);
}
}
}
#[must_use]
pub struct ComboBoxToken {
ctx: *const Context,
}
impl ComboBoxToken {
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndCombo() };
}
}
impl Drop for ComboBoxToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A ComboBoxToken was leaked. Did you call .end()?");
}
}
}
impl<'a> ComboBox<'a> {
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 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;
if Selectable::new(&text).selected(selected).build(ui) {
*current_item = idx;
result = true;
}
}
_cb.end(ui);
}
result
}
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())
}
}