From 623dc149f05f8077757cccbb073068cbb9f44122 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 18 Jun 2025 19:39:29 -0700 Subject: [PATCH] Change arguments to animating panel functions to take &mut bool --- crates/egui/src/containers/panel.rs | 130 +++++++++++++++++---------- crates/egui_demo_app/src/wrap_app.rs | 9 +- 2 files changed, 90 insertions(+), 49 deletions(-) diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 21d8935f1..4dae2cd57 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -223,14 +223,14 @@ impl SidePanel { ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.show_inside_dyn(ui, false, Box::new(add_contents)) + self.show_inside_dyn(ui, None, Box::new(add_contents)) } /// Show the panel inside a [`Ui`]. fn show_inside_dyn<'c, R>( self, ui: &mut Ui, - closable: bool, + is_expanded: Option<&mut bool>, add_contents: Box R + 'c>, ) -> InnerResponse { let Self { @@ -273,7 +273,7 @@ impl SidePanel { width = clamp_to_range(width, width_range).at_most(available_rect.width()); side.set_rect_width(&mut panel_rect, width); - if closable && width <= width_range.min && width < old_width { + if is_expanded.is_some() && width <= width_range.min && width < old_width { drag_to_close = true; } } @@ -292,7 +292,7 @@ impl SidePanel { })) .max_rect(panel_rect) .layout(Layout::top_down(Align::Min)) - .with_closable(closable), + .with_closable(is_expanded.is_some()), ); panel_ui.expand_to_include_rect(panel_rect); panel_ui.set_clip_rect(panel_rect); // If we overflow, don't do so visibly (#4475) @@ -370,8 +370,11 @@ impl SidePanel { ui.painter().vline(resize_x, panel_rect.y_range(), stroke); } - if panel_ui.should_close() || drag_to_close { - inner_response.response.set_close(); + if let Some(is_expanded) = is_expanded { + if panel_ui.should_close() || drag_to_close { + inner_response.response.set_close(); + *is_expanded = false; + } } inner_response @@ -383,14 +386,14 @@ impl SidePanel { ctx: &Context, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.show_dyn(ctx, false, Box::new(add_contents)) + self.show_dyn(ctx, None, Box::new(add_contents)) } /// Show the panel at the top level. fn show_dyn<'c, R>( self, ctx: &Context, - closable: bool, + is_expanded: Option<&mut bool>, add_contents: Box R + 'c>, ) -> InnerResponse { let side = self.side; @@ -404,7 +407,7 @@ impl SidePanel { ); panel_ui.set_clip_rect(ctx.screen_rect()); - let inner_response = self.show_inside_dyn(&mut panel_ui, closable, add_contents); + let inner_response = self.show_inside_dyn(&mut panel_ui, is_expanded, add_contents); let rect = inner_response.response.rect; match side { @@ -420,13 +423,15 @@ impl SidePanel { /// Show the panel if `is_expanded` is `true`, /// otherwise don't show it, but with a nice animation between collapsed and expanded. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated( self, ctx: &Context, - is_expanded: bool, + is_expanded: &mut bool, add_contents: impl FnOnce(&mut Ui) -> R, ) -> Option> { - let how_expanded = animate_expansion(ctx, self.id.with("animation"), is_expanded); + let how_expanded = animate_expansion(ctx, self.id.with("animation"), *is_expanded); if 0.0 == how_expanded { None @@ -447,20 +452,21 @@ impl SidePanel { None } else { // Show the real panel: - let closable = true; - Some(self.show_dyn(ctx, closable, Box::new(add_contents))) + Some(self.show_dyn(ctx, Some(is_expanded), Box::new(add_contents))) } } /// Show the panel if `is_expanded` is `true`, /// otherwise don't show it, but with a nice animation between collapsed and expanded. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated_inside( self, ui: &mut Ui, - is_expanded: bool, + is_expanded: &mut bool, add_contents: impl FnOnce(&mut Ui) -> R, ) -> Option> { - let how_expanded = animate_expansion(ui.ctx(), self.id.with("animation"), is_expanded); + let how_expanded = animate_expansion(ui.ctx(), self.id.with("animation"), *is_expanded); if 0.0 == how_expanded { None @@ -481,20 +487,22 @@ impl SidePanel { None } else { // Show the real panel: - let closable = true; - Some(self.show_inside_dyn(ui, closable, Box::new(add_contents))) + Some(self.show_inside_dyn(ui, Some(is_expanded), Box::new(add_contents))) } } /// Show either a collapsed or a expanded panel, with a nice animation between. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated_between( ctx: &Context, - is_expanded: bool, + is_expanded: &mut bool, collapsed_panel: Self, expanded_panel: Self, add_contents: impl FnOnce(&mut Ui, f32) -> R, ) -> Option> { - let how_expanded = animate_expansion(ctx, expanded_panel.id.with("animation"), is_expanded); + let how_expanded = + animate_expansion(ctx, expanded_panel.id.with("animation"), *is_expanded); if 0.0 == how_expanded { Some(collapsed_panel.show(ctx, |ui| add_contents(ui, how_expanded))) @@ -514,20 +522,26 @@ impl SidePanel { .show(ctx, |ui| add_contents(ui, how_expanded)); None } else { - Some(expanded_panel.show(ctx, |ui| add_contents(ui, how_expanded))) + Some(expanded_panel.show_dyn( + ctx, + Some(is_expanded), + Box::new(|ui| add_contents(ui, how_expanded)), + )) } } /// Show either a collapsed or a expanded panel, with a nice animation between. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated_between_inside( ui: &mut Ui, - is_expanded: bool, + is_expanded: &mut bool, collapsed_panel: Self, expanded_panel: Self, add_contents: impl FnOnce(&mut Ui, f32) -> R, ) -> InnerResponse { let how_expanded = - animate_expansion(ui.ctx(), expanded_panel.id.with("animation"), is_expanded); + animate_expansion(ui.ctx(), expanded_panel.id.with("animation"), *is_expanded); if 0.0 == how_expanded { collapsed_panel.show_inside(ui, |ui| add_contents(ui, how_expanded)) @@ -546,7 +560,11 @@ impl SidePanel { .exact_width(fake_width) .show_inside(ui, |ui| add_contents(ui, how_expanded)) } else { - expanded_panel.show_inside(ui, |ui| add_contents(ui, how_expanded)) + expanded_panel.show_inside_dyn( + ui, + Some(is_expanded), + Box::new(|ui| add_contents(ui, how_expanded)), + ) } } } @@ -729,14 +747,14 @@ impl TopBottomPanel { ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.show_inside_dyn(ui, false, Box::new(add_contents)) + self.show_inside_dyn(ui, None, Box::new(add_contents)) } /// Show the panel inside a [`Ui`]. fn show_inside_dyn<'c, R>( self, ui: &mut Ui, - closable: bool, + is_expanded: Option<&mut bool>, add_contents: Box R + 'c>, ) -> InnerResponse { let Self { @@ -786,7 +804,10 @@ impl TopBottomPanel { clamp_to_range(height, height_range).at_most(available_rect.height()); side.set_rect_height(&mut panel_rect, height); - if closable && height <= height_range.min && height < old_height { + if is_expanded.is_some() + && height <= height_range.min + && height < old_height + { drag_to_close = true; } } @@ -805,7 +826,7 @@ impl TopBottomPanel { })) .max_rect(panel_rect) .layout(Layout::top_down(Align::Min)) - .with_closable(closable), + .with_closable(is_expanded.is_some()), ); panel_ui.expand_to_include_rect(panel_rect); panel_ui.set_clip_rect(panel_rect); // If we overflow, don't do so visibly (#4475) @@ -883,8 +904,11 @@ impl TopBottomPanel { ui.painter().hline(panel_rect.x_range(), resize_y, stroke); } - if panel_ui.should_close() || drag_to_close { - inner_response.response.set_close(); + if let Some(is_expanded) = is_expanded { + if panel_ui.should_close() || drag_to_close { + inner_response.response.set_close(); + *is_expanded = false; + } } inner_response @@ -896,14 +920,14 @@ impl TopBottomPanel { ctx: &Context, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.show_dyn(ctx, false, Box::new(add_contents)) + self.show_dyn(ctx, None, Box::new(add_contents)) } /// Show the panel at the top level. fn show_dyn<'c, R>( self, ctx: &Context, - closable: bool, + is_expanded: Option<&mut bool>, add_contents: Box R + 'c>, ) -> InnerResponse { let available_rect = ctx.available_rect(); @@ -918,7 +942,7 @@ impl TopBottomPanel { ); panel_ui.set_clip_rect(ctx.screen_rect()); - let inner_response = self.show_inside_dyn(&mut panel_ui, closable, add_contents); + let inner_response = self.show_inside_dyn(&mut panel_ui, is_expanded, add_contents); let rect = inner_response.response.rect; match side { @@ -939,13 +963,15 @@ impl TopBottomPanel { /// Show the panel if `is_expanded` is `true`, /// otherwise don't show it, but with a nice animation between collapsed and expanded. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated( self, ctx: &Context, - is_expanded: bool, + is_expanded: &mut bool, add_contents: impl FnOnce(&mut Ui) -> R, ) -> Option> { - let how_expanded = animate_expansion(ctx, self.id.with("animation"), is_expanded); + let how_expanded = animate_expansion(ctx, self.id.with("animation"), *is_expanded); if 0.0 == how_expanded { None @@ -968,19 +994,21 @@ impl TopBottomPanel { None } else { // Show the real panel: - Some(self.show(ctx, add_contents)) + Some(self.show_dyn(ctx, Some(is_expanded), Box::new(add_contents))) } } /// Show the panel if `is_expanded` is `true`, /// otherwise don't show it, but with a nice animation between collapsed and expanded. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated_inside( self, ui: &mut Ui, - is_expanded: bool, + is_expanded: &mut bool, add_contents: impl FnOnce(&mut Ui) -> R, ) -> Option> { - let how_expanded = animate_expansion(ui.ctx(), self.id.with("animation"), is_expanded); + let how_expanded = animate_expansion(ui.ctx(), self.id.with("animation"), *is_expanded); if 0.0 == how_expanded { None @@ -1003,20 +1031,22 @@ impl TopBottomPanel { None } else { // Show the real panel: - let closable = true; - Some(self.show_inside_dyn(ui, closable, Box::new(add_contents))) + Some(self.show_inside_dyn(ui, Some(is_expanded), Box::new(add_contents))) } } /// Show either a collapsed or a expanded panel, with a nice animation between. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated_between( ctx: &Context, - is_expanded: bool, + is_expanded: &mut bool, collapsed_panel: Self, expanded_panel: Self, add_contents: impl FnOnce(&mut Ui, f32) -> R, ) -> Option> { - let how_expanded = animate_expansion(ctx, expanded_panel.id.with("animation"), is_expanded); + let how_expanded = + animate_expansion(ctx, expanded_panel.id.with("animation"), *is_expanded); if 0.0 == how_expanded { Some(collapsed_panel.show(ctx, |ui| add_contents(ui, how_expanded))) @@ -1042,20 +1072,26 @@ impl TopBottomPanel { .show(ctx, |ui| add_contents(ui, how_expanded)); None } else { - Some(expanded_panel.show(ctx, |ui| add_contents(ui, how_expanded))) + Some(expanded_panel.show_dyn( + ctx, + Some(is_expanded), + Box::new(|ui| add_contents(ui, how_expanded)), + )) } } /// Show either a collapsed or a expanded panel, with a nice animation between. + /// + /// The panel supports drag-to-close, and you can also call [`Ui::close`] from within the panel to close it. pub fn show_animated_between_inside( ui: &mut Ui, - is_expanded: bool, + is_expanded: &mut bool, collapsed_panel: Self, expanded_panel: Self, add_contents: impl FnOnce(&mut Ui, f32) -> R, ) -> InnerResponse { let how_expanded = - animate_expansion(ui.ctx(), expanded_panel.id.with("animation"), is_expanded); + animate_expansion(ui.ctx(), expanded_panel.id.with("animation"), *is_expanded); if 0.0 == how_expanded { collapsed_panel.show_inside(ui, |ui| add_contents(ui, how_expanded)) @@ -1080,7 +1116,11 @@ impl TopBottomPanel { .exact_height(fake_height) .show_inside(ui, |ui| add_contents(ui, how_expanded)) } else { - expanded_panel.show_inside(ui, |ui| add_contents(ui, how_expanded)) + expanded_panel.show_inside_dyn( + ui, + Some(is_expanded), + Box::new(|ui| add_contents(ui, how_expanded)), + ) } } } diff --git a/crates/egui_demo_app/src/wrap_app.rs b/crates/egui_demo_app/src/wrap_app.rs index 3775d93d2..09859f6fc 100644 --- a/crates/egui_demo_app/src/wrap_app.rs +++ b/crates/egui_demo_app/src/wrap_app.rs @@ -337,14 +337,13 @@ impl WrapApp { fn backend_panel(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) -> Command { // The backend-panel can be toggled on/off. // We show a little animation when the user switches it. - let is_open = - self.state.backend_panel.open || ctx.memory(|mem| mem.everything_is_visible()); - let mut cmd = Command::Nothing; + let mut open = self.state.backend_panel.open; + egui::SidePanel::left("backend_panel") .resizable(false) - .show_animated(ctx, is_open, |ui| { + .show_animated(ctx, &mut open, |ui| { ui.add_space(4.0); ui.vertical_centered(|ui| { ui.heading("💻 Backend"); @@ -354,6 +353,8 @@ impl WrapApp { self.backend_panel_contents(ui, frame, &mut cmd); }); + self.state.backend_panel.open = open; + cmd }