Skip to main content

dear_imgui_rs/widget/
selectable.rs

1//! Selectable items
2//!
3//! Clickable items that can be selected, typically used in lists. Supports
4//! span-full-width behavior and selection flags.
5//!
6#![allow(
7    clippy::cast_possible_truncation,
8    clippy::cast_sign_loss,
9    clippy::as_conversions
10)]
11use crate::Ui;
12use crate::sys;
13
14fn assert_non_negative_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
15    assert!(
16        value[0].is_finite() && value[1].is_finite(),
17        "{caller} {name} must contain finite values"
18    );
19    assert!(
20        value[0] >= 0.0 && value[1] >= 0.0,
21        "{caller} {name} must contain non-negative values"
22    );
23}
24
25fn validate_selectable_flags(caller: &str, flags: SelectableFlags) {
26    let unsupported = flags.bits() & !SelectableFlags::all().bits();
27    assert!(
28        unsupported == 0,
29        "{caller} received unsupported ImGuiSelectableFlags bits: 0x{unsupported:X}"
30    );
31}
32
33bitflags::bitflags! {
34    /// Flags for selectables
35    #[repr(transparent)]
36    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
37    pub struct SelectableFlags: i32 {
38        /// Clicking this don't close parent popup window
39        const NO_AUTO_CLOSE_POPUPS = sys::ImGuiSelectableFlags_NoAutoClosePopups as i32;
40        /// Selectable frame can span all columns (text will still fit in current column)
41        const SPAN_ALL_COLUMNS = sys::ImGuiSelectableFlags_SpanAllColumns as i32;
42        /// Generate press events on double clicks too
43        const ALLOW_DOUBLE_CLICK = sys::ImGuiSelectableFlags_AllowDoubleClick as i32;
44        /// Cannot be selected, display greyed out text
45        const DISABLED = sys::ImGuiSelectableFlags_Disabled as i32;
46        /// Hit testing to allow subsequent widgets to overlap this one
47        const ALLOW_OVERLAP = sys::ImGuiSelectableFlags_AllowOverlap as i32;
48        /// Display the selectable as highlighted, as if hovered.
49        const HIGHLIGHT = sys::ImGuiSelectableFlags_Highlight as i32;
50        /// Auto-select when moved into by navigation, unless Ctrl is held.
51        const SELECT_ON_NAV = sys::ImGuiSelectableFlags_SelectOnNav as i32;
52    }
53}
54
55impl Ui {
56    /// Constructs a new simple selectable.
57    ///
58    /// Use [selectable_config] for a builder with additional options.
59    ///
60    /// [selectable_config]: Self::selectable_config
61    #[doc(alias = "Selectable")]
62    pub fn selectable<T: AsRef<str>>(&self, label: T) -> bool {
63        self.selectable_config(label).build()
64    }
65
66    /// Constructs a new selectable builder.
67    #[doc(alias = "Selectable")]
68    pub fn selectable_config<T: AsRef<str>>(&self, label: T) -> Selectable<'_, T> {
69        Selectable {
70            label,
71            selected: false,
72            flags: SelectableFlags::empty(),
73            size: [0.0, 0.0],
74            ui: self,
75        }
76    }
77}
78
79/// Builder for a selectable widget.
80#[derive(Clone, Debug)]
81#[must_use]
82pub struct Selectable<'ui, T> {
83    label: T,
84    selected: bool,
85    flags: SelectableFlags,
86    size: [f32; 2],
87    ui: &'ui Ui,
88}
89
90impl<'ui, T: AsRef<str>> Selectable<'ui, T> {
91    /// Constructs a new selectable builder.
92    #[doc(alias = "Selectable")]
93    #[deprecated(
94        since = "0.9.0",
95        note = "use `ui.selectable` or `ui.selectable_config`"
96    )]
97    pub fn new(label: T, ui: &'ui Ui) -> Self {
98        Selectable {
99            label,
100            selected: false,
101            flags: SelectableFlags::empty(),
102            size: [0.0, 0.0],
103            ui,
104        }
105    }
106    /// Replaces all current settings with the given flags
107    pub fn flags(mut self, flags: SelectableFlags) -> Self {
108        self.flags = flags;
109        self
110    }
111    /// Sets the selected state of the selectable
112    pub fn selected(mut self, selected: bool) -> Self {
113        self.selected = selected;
114        self
115    }
116    /// Enables/disables closing parent popup window on click.
117    ///
118    /// Default: enabled
119    pub fn close_popups(mut self, value: bool) -> Self {
120        self.flags
121            .set(SelectableFlags::NO_AUTO_CLOSE_POPUPS, !value);
122        self
123    }
124    /// Enables/disables full column span (text will still fit in the current column).
125    ///
126    /// Default: disabled
127    pub fn span_all_columns(mut self, value: bool) -> Self {
128        self.flags.set(SelectableFlags::SPAN_ALL_COLUMNS, value);
129        self
130    }
131    /// Enables/disables click event generation on double clicks.
132    ///
133    /// Default: disabled
134    pub fn allow_double_click(mut self, value: bool) -> Self {
135        self.flags.set(SelectableFlags::ALLOW_DOUBLE_CLICK, value);
136        self
137    }
138    /// Enables/disables the selectable.
139    ///
140    /// When disabled, it cannot be selected and the text uses the disabled text color.
141    ///
142    /// Default: disabled
143    pub fn disabled(mut self, value: bool) -> Self {
144        self.flags.set(SelectableFlags::DISABLED, value);
145        self
146    }
147    /// Sets the size of the selectable.
148    ///
149    /// For the X axis:
150    ///
151    /// - `> 0.0`: use given width
152    /// - `= 0.0`: use remaining width
153    ///
154    /// For the Y axis:
155    ///
156    /// - `> 0.0`: use given height
157    /// - `= 0.0`: use label height
158    pub fn size(mut self, size: impl Into<[f32; 2]>) -> Self {
159        self.size = size.into();
160        self
161    }
162
163    /// Builds the selectable.
164    ///
165    /// Returns true if the selectable was clicked.
166    pub fn build(self) -> bool {
167        validate_selectable_flags("Selectable::build()", self.flags);
168        assert_non_negative_finite_vec2("Selectable::build()", "size", self.size);
169        let size_vec = sys::ImVec2 {
170            x: self.size[0],
171            y: self.size[1],
172        };
173        unsafe {
174            sys::igSelectable_Bool(
175                self.ui.scratch_txt(self.label),
176                self.selected,
177                self.flags.bits(),
178                size_vec,
179            )
180        }
181    }
182
183    /// Builds the selectable using a mutable reference to selected state.
184    pub fn build_with_ref(self, selected: &mut bool) -> bool {
185        validate_selectable_flags("Selectable::build_with_ref()", self.flags);
186        assert_non_negative_finite_vec2("Selectable::build_with_ref()", "size", self.size);
187        let size_vec = sys::ImVec2 {
188            x: self.size[0],
189            y: self.size[1],
190        };
191        unsafe {
192            sys::igSelectable_BoolPtr(
193                self.ui.scratch_txt(self.label),
194                selected as *mut bool,
195                self.flags.bits(),
196                size_vec,
197            )
198        }
199    }
200}