Add indeterminate state to checkbox (#3605)
<!-- Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md) before opening a Pull Request! * Keep your PR:s small and focused. * If applicable, add a screenshot or gif. * If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example. * Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to add commits to your PR. * Remember to run `cargo fmt` and `cargo cranky`. * Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`. * When you have addressed a PR comment, mark it as resolved. Please be patient! I will review your PR, but my time is limited! -->
This commit is contained in:
parent
932fdae9e6
commit
797406de39
|
|
@ -655,6 +655,9 @@ impl Response {
|
||||||
} else {
|
} else {
|
||||||
Checked::False
|
Checked::False
|
||||||
});
|
});
|
||||||
|
} else if matches!(info.typ, WidgetType::Checkbox) {
|
||||||
|
// Indeterminate state
|
||||||
|
builder.set_checked(Checked::Mixed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,7 @@ impl Widget for Button<'_> {
|
||||||
pub struct Checkbox<'a> {
|
pub struct Checkbox<'a> {
|
||||||
checked: &'a mut bool,
|
checked: &'a mut bool,
|
||||||
text: WidgetText,
|
text: WidgetText,
|
||||||
|
indeterminate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Checkbox<'a> {
|
impl<'a> Checkbox<'a> {
|
||||||
|
|
@ -363,17 +364,32 @@ impl<'a> Checkbox<'a> {
|
||||||
Checkbox {
|
Checkbox {
|
||||||
checked,
|
checked,
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
|
indeterminate: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn without_text(checked: &'a mut bool) -> Self {
|
pub fn without_text(checked: &'a mut bool) -> Self {
|
||||||
Self::new(checked, WidgetText::default())
|
Self::new(checked, WidgetText::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Display an indeterminate state (neither checked nor unchecked)
|
||||||
|
///
|
||||||
|
/// This only affects the checkbox's appearance. It will still toggle its boolean value when
|
||||||
|
/// clicked.
|
||||||
|
#[inline]
|
||||||
|
pub fn indeterminate(mut self, indeterminate: bool) -> Self {
|
||||||
|
self.indeterminate = indeterminate;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Widget for Checkbox<'a> {
|
impl<'a> Widget for Checkbox<'a> {
|
||||||
fn ui(self, ui: &mut Ui) -> Response {
|
fn ui(self, ui: &mut Ui) -> Response {
|
||||||
let Checkbox { checked, text } = self;
|
let Checkbox {
|
||||||
|
checked,
|
||||||
|
text,
|
||||||
|
indeterminate,
|
||||||
|
} = self;
|
||||||
|
|
||||||
let spacing = &ui.spacing();
|
let spacing = &ui.spacing();
|
||||||
let icon_width = spacing.icon_width;
|
let icon_width = spacing.icon_width;
|
||||||
|
|
@ -402,11 +418,18 @@ impl<'a> Widget for Checkbox<'a> {
|
||||||
response.mark_changed();
|
response.mark_changed();
|
||||||
}
|
}
|
||||||
response.widget_info(|| {
|
response.widget_info(|| {
|
||||||
|
if indeterminate {
|
||||||
|
WidgetInfo::labeled(
|
||||||
|
WidgetType::Checkbox,
|
||||||
|
galley.as_ref().map_or("", |x| x.text()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
WidgetInfo::selected(
|
WidgetInfo::selected(
|
||||||
WidgetType::Checkbox,
|
WidgetType::Checkbox,
|
||||||
*checked,
|
*checked,
|
||||||
galley.as_ref().map_or("", |x| x.text()),
|
galley.as_ref().map_or("", |x| x.text()),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if ui.is_rect_visible(rect) {
|
if ui.is_rect_visible(rect) {
|
||||||
|
|
@ -420,7 +443,14 @@ impl<'a> Widget for Checkbox<'a> {
|
||||||
visuals.bg_stroke,
|
visuals.bg_stroke,
|
||||||
));
|
));
|
||||||
|
|
||||||
if *checked {
|
if indeterminate {
|
||||||
|
// Horizontal line:
|
||||||
|
ui.painter().add(Shape::hline(
|
||||||
|
small_icon_rect.x_range(),
|
||||||
|
small_icon_rect.center().y,
|
||||||
|
visuals.fg_stroke,
|
||||||
|
));
|
||||||
|
} else if *checked {
|
||||||
// Check mark:
|
// Check mark:
|
||||||
ui.painter().add(Shape::line(
|
ui.painter().add(Shape::line(
|
||||||
vec![
|
vec![
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ pub struct MiscDemoWindow {
|
||||||
|
|
||||||
dummy_bool: bool,
|
dummy_bool: bool,
|
||||||
dummy_usize: usize,
|
dummy_usize: usize,
|
||||||
|
checklist: [bool; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MiscDemoWindow {
|
impl Default for MiscDemoWindow {
|
||||||
|
|
@ -30,6 +31,7 @@ impl Default for MiscDemoWindow {
|
||||||
|
|
||||||
dummy_bool: false,
|
dummy_bool: false,
|
||||||
dummy_usize: 0,
|
dummy_usize: 0,
|
||||||
|
checklist: std::array::from_fn(|i| i == 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +109,24 @@ impl View for MiscDemoWindow {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.radio_value(&mut self.dummy_usize, 64, "radio_value");
|
ui.radio_value(&mut self.dummy_usize, 64, "radio_value");
|
||||||
|
ui.label("Checkboxes can be in an indeterminate state:");
|
||||||
|
let mut all_checked = self.checklist.iter().all(|item| *item);
|
||||||
|
let any_checked = self.checklist.iter().any(|item| *item);
|
||||||
|
let indeterminate = any_checked && !all_checked;
|
||||||
|
if ui
|
||||||
|
.add(
|
||||||
|
Checkbox::new(&mut all_checked, "Check/uncheck all")
|
||||||
|
.indeterminate(indeterminate),
|
||||||
|
)
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
self.checklist
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|checked| *checked = all_checked);
|
||||||
|
}
|
||||||
|
for (i, checked) in self.checklist.iter_mut().enumerate() {
|
||||||
|
ui.checkbox(checked, format!("Item {}", i + 1));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.collapsing("Columns", |ui| {
|
ui.collapsing("Columns", |ui| {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue