Add option to select collapsing headers (#623)

* Add collapsing header select as selectable label

* Modified Tree demo adding selectable example

* Update egui/src/containers/collapsing_header.rs

Selected is not linked to selectable

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* Update egui/src/containers/collapsing_header.rs

Description example

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* Changing example without name clashing

* Fixing merge issue (ah I miss P4 sometimes)

* Fixing doctest example

* Add possibility to show background to a single one

* Fixing clippy test

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
gents83 2021-08-20 19:04:13 +02:00 committed by GitHub
parent 04b3921923
commit 68ed22ab6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 111 additions and 17 deletions

View file

@ -140,6 +140,9 @@ pub struct CollapsingHeader {
default_open: bool, default_open: bool,
id_source: Id, id_source: Id,
enabled: bool, enabled: bool,
selectable: bool,
selected: bool,
show_background: bool,
} }
impl CollapsingHeader { impl CollapsingHeader {
@ -157,6 +160,9 @@ impl CollapsingHeader {
default_open: false, default_open: false,
id_source, id_source,
enabled: true, enabled: true,
selectable: false,
selected: false,
show_background: false,
} }
} }
@ -188,6 +194,44 @@ impl CollapsingHeader {
self.enabled = enabled; self.enabled = enabled;
self self
} }
/// Can the `CollapsingHeader` be selected by clicking it? Default: `false`.
///
pub fn selectable(mut self, selectable: bool) -> Self {
self.selectable = selectable;
self
}
/// If you set this to 'true', the `CollapsingHeader` will be shown as selected.
///
/// Example:
/// ```
/// # let ui = &mut egui::Ui::__test();
/// let mut selected = false;
/// let response = egui::CollapsingHeader::new("Select and open me")
/// .selectable(true)
/// .selected(selected)
/// .show(ui, |ui| ui.label("Content"));
/// if response.header_response.clicked() {
/// selected = true;
/// }
/// ```
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
/// Should the `CollapsingHeader` show a background behind it? Default: `false`.
///
/// To show it behind all `CollapsingHeader` you can just use:
/// ```
/// # let ui = &mut egui::Ui::__test();
/// ui.visuals_mut().collapsing_header_frame = true;
/// ```
pub fn show_background(mut self, show_background: bool) -> Self {
self.show_background = show_background;
self
}
} }
struct Prepared { struct Prepared {
@ -207,6 +251,9 @@ impl CollapsingHeader {
default_open, default_open,
id_source, id_source,
enabled: _, enabled: _,
selectable: _,
selected: _,
show_background: _,
} = self; } = self;
label.text_style = label label.text_style = label
@ -247,10 +294,16 @@ impl CollapsingHeader {
header_response header_response
.widget_info(|| WidgetInfo::labeled(WidgetType::CollapsingHeader, &galley.text)); .widget_info(|| WidgetInfo::labeled(WidgetType::CollapsingHeader, &galley.text));
let visuals = ui.style().interact(&header_response); let visuals = ui
let text_color = visuals.text_color(); .style()
.interact_selectable(&header_response, self.selected);
let text_color = ui
.style()
.visuals
.override_text_color
.unwrap_or_else(|| visuals.text_color());
if ui.visuals().collapsing_header_frame { if ui.visuals().collapsing_header_frame || self.show_background {
ui.painter().add(Shape::Rect { ui.painter().add(Shape::Rect {
rect: header_response.rect.expand(visuals.expansion), rect: header_response.rect.expand(visuals.expansion),
corner_radius: visuals.corner_radius, corner_radius: visuals.corner_radius,
@ -260,6 +313,16 @@ impl CollapsingHeader {
}); });
} }
if self.selected
|| self.selectable && (header_response.hovered() || header_response.has_focus())
{
let rect = rect.expand(visuals.expansion);
let corner_radius = 2.0;
ui.painter()
.rect(rect, corner_radius, visuals.bg_fill, visuals.bg_stroke);
}
{ {
let (mut icon_rect, _) = ui.spacing().icon_rectangles(header_response.rect); let (mut icon_rect, _) = ui.spacing().icon_rectangles(header_response.rect);
icon_rect.set_center(pos2( icon_rect.set_center(pos2(

View file

@ -339,28 +339,53 @@ enum Action {
#[derive(Clone, Default)] #[derive(Clone, Default)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
struct Tree(Vec<Tree>); struct Tree(String, SubTree);
impl Tree { impl Tree {
pub fn demo() -> Self { pub fn demo() -> Self {
Self(vec![ Self(
Tree(vec![Tree::default(); 4]), String::from("root"),
Tree(vec![Tree(vec![Tree::default(); 2]); 3]), SubTree(vec![
]) SubTree(vec![SubTree::default(); 4]),
SubTree(vec![SubTree(vec![SubTree::default(); 2]); 3]),
]),
)
} }
pub fn ui(&mut self, ui: &mut Ui) -> Action { pub fn ui(&mut self, ui: &mut Ui) -> Action {
self.ui_impl(ui, 0, "root") self.1.ui(ui, 0, "root", &mut self.0)
}
} }
fn ui_impl(&mut self, ui: &mut Ui, depth: usize, name: &str) -> Action { #[derive(Clone, Default)]
CollapsingHeader::new(name) #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
struct SubTree(Vec<SubTree>);
impl SubTree {
pub fn ui(
&mut self,
ui: &mut Ui,
depth: usize,
name: &str,
selected_name: &mut String,
) -> Action {
let response = CollapsingHeader::new(name)
.default_open(depth < 1) .default_open(depth < 1)
.show(ui, |ui| self.children_ui(ui, depth)) .selectable(true)
.body_returned .selected(selected_name.as_str() == name)
.unwrap_or(Action::Keep) .show(ui, |ui| self.children_ui(ui, name, depth, selected_name));
if response.header_response.clicked() {
*selected_name = name.to_string();
}
response.body_returned.unwrap_or(Action::Keep)
} }
fn children_ui(&mut self, ui: &mut Ui, depth: usize) -> Action { fn children_ui(
&mut self,
ui: &mut Ui,
parent_name: &str,
depth: usize,
selected_name: &mut String,
) -> Action {
if depth > 0 if depth > 0
&& ui && ui
.add(Button::new("delete").text_color(Color32::RED)) .add(Button::new("delete").text_color(Color32::RED))
@ -374,7 +399,13 @@ impl Tree {
.into_iter() .into_iter()
.enumerate() .enumerate()
.filter_map(|(i, mut tree)| { .filter_map(|(i, mut tree)| {
if tree.ui_impl(ui, depth + 1, &format!("child #{}", i)) == Action::Keep { if tree.ui(
ui,
depth + 1,
&format!("{}/{}", parent_name, i),
selected_name,
) == Action::Keep
{
Some(tree) Some(tree)
} else { } else {
None None
@ -383,7 +414,7 @@ impl Tree {
.collect(); .collect();
if ui.button("+").clicked() { if ui.button("+").clicked() {
self.0.push(Tree::default()); self.0.push(SubTree::default());
} }
Action::Keep Action::Keep