mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 22:00:03 -04:00
Slide panels when animating them (#8175)
This looks A LOT nicer https://github.com/user-attachments/assets/6f208e6c-6b6d-46d2-a40d-832be1256ca7
This commit is contained in:
@@ -87,6 +87,13 @@ impl PanelSide {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Outward unit vector from the fixed edge:
|
||||||
|
/// `(-1, 0)` for [`Left`](Self::Left), `(+1, 0)` for [`Right`](Self::Right),
|
||||||
|
/// `(0, -1)` for [`Top`](Self::Top), `(0, +1)` for [`Bottom`](Self::Bottom).
|
||||||
|
fn dir_vec2(self) -> Vec2 {
|
||||||
|
self.sign() * self.axis_unit()
|
||||||
|
}
|
||||||
|
|
||||||
/// `-1` for sides at the near edge ([`Left`](Self::Left), [`Top`](Self::Top)),
|
/// `-1` for sides at the near edge ([`Left`](Self::Left), [`Top`](Self::Top)),
|
||||||
/// `+1` for sides at the far edge ([`Right`](Self::Right), [`Bottom`](Self::Bottom)).
|
/// `+1` for sides at the far edge ([`Right`](Self::Right), [`Bottom`](Self::Bottom)).
|
||||||
fn sign(self) -> f32 {
|
fn sign(self) -> f32 {
|
||||||
@@ -172,6 +179,13 @@ pub struct Panel {
|
|||||||
/// _Outer_ size range (including [`Frame`] margin & border):
|
/// _Outer_ size range (including [`Frame`] margin & border):
|
||||||
/// the width for a vertical panel, or the height for a horizontal panel.
|
/// the width for a vertical panel, or the height for a horizontal panel.
|
||||||
outer_size_range: Rangef,
|
outer_size_range: Rangef,
|
||||||
|
|
||||||
|
/// `1.0` = panel fully visible (the normal case),
|
||||||
|
/// `0.0` = panel fully slid off-screen toward its fixed edge.
|
||||||
|
///
|
||||||
|
/// Used by [`Self::show_animated_inside`] to animate a panel sliding in/out.
|
||||||
|
/// While `slide_fraction != 1.0` the panel does _not_ persist its [`PanelState`].
|
||||||
|
slide_fraction: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Panel {
|
impl Panel {
|
||||||
@@ -229,6 +243,7 @@ impl Panel {
|
|||||||
show_separator_line: true,
|
show_separator_line: true,
|
||||||
default_outer_size,
|
default_outer_size,
|
||||||
outer_size_range,
|
outer_size_range,
|
||||||
|
slide_fraction: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +340,11 @@ impl Panel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Show the panel if `is_expanded` is `true`,
|
/// Show the panel if `is_expanded` is `true`,
|
||||||
/// otherwise don't show it, but with a nice animation between collapsed and expanded.
|
/// otherwise hide it, with a slide animation in between.
|
||||||
|
///
|
||||||
|
/// During the animation `add_contents` runs against the real panel, and the
|
||||||
|
/// panel slides off-screen toward its fixed edge (clipped against the parent).
|
||||||
|
/// The parent only reserves the _visible_ portion, so neighboring widgets follow.
|
||||||
pub fn show_animated_inside<R>(
|
pub fn show_animated_inside<R>(
|
||||||
self,
|
self,
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
@@ -340,41 +359,92 @@ impl Panel {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if how_expanded < 1.0 {
|
let panel = if how_expanded < 1.0 {
|
||||||
// Show a fake panel in this in-between animation state:
|
// Mid-animation: slide the panel toward its fixed edge.
|
||||||
// TODO(emilk): move the panel out-of-screen instead of changing its width.
|
// Resizing a moving boundary is too awkward, so disable it during the slide.
|
||||||
// Then we can actually paint it as it animates.
|
self.with_slide_fraction(how_expanded).resizable(false)
|
||||||
let fake_size = how_expanded * self.outer_size(ui);
|
} else {
|
||||||
self.into_fake_animating(fake_size)
|
self
|
||||||
.show_inside(ui, |_ui| {});
|
};
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(self.show_inside(ui, add_contents))
|
Some(panel.show_inside(ui, add_contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show either a collapsed or a expanded panel, with a nice animation between.
|
/// Show either a collapsed or expanded panel, with a nice slide animation between.
|
||||||
|
///
|
||||||
|
/// The `collapsed_panel` is shown only when fully collapsed; during the
|
||||||
|
/// animation, the `expanded_panel` slides in/out toward its fixed edge,
|
||||||
|
/// interpolating its visible size between the two panels' sizes.
|
||||||
|
/// `add_contents` receives `expanded = true` whenever the expanded panel is
|
||||||
|
/// rendered (including mid-animation), and `false` for the collapsed view.
|
||||||
|
///
|
||||||
|
/// Give the two panels distinct ids so their persisted sizes don't
|
||||||
|
/// overwrite each other.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # egui::__run_test_ui(|ui| {
|
||||||
|
/// let mut is_expanded = true;
|
||||||
|
/// let collapsed = egui::Panel::top("top_collapsed").exact_size(28.0);
|
||||||
|
/// let expanded = egui::Panel::top("top_expanded")
|
||||||
|
/// .resizable(true)
|
||||||
|
/// .default_size(120.0);
|
||||||
|
/// egui::Panel::show_animated_between_inside(
|
||||||
|
/// ui,
|
||||||
|
/// is_expanded,
|
||||||
|
/// collapsed,
|
||||||
|
/// expanded,
|
||||||
|
/// |ui, expanded| {
|
||||||
|
/// if expanded {
|
||||||
|
/// ui.heading("Expanded");
|
||||||
|
/// ui.label("More content here…");
|
||||||
|
/// } else {
|
||||||
|
/// ui.label("Collapsed toolbar");
|
||||||
|
/// }
|
||||||
|
/// },
|
||||||
|
/// );
|
||||||
|
/// ui.toggle_value(&mut is_expanded, "Expand");
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
pub fn show_animated_between_inside<R>(
|
pub fn show_animated_between_inside<R>(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
is_expanded: bool,
|
is_expanded: bool,
|
||||||
collapsed_panel: Self,
|
collapsed_panel: Self,
|
||||||
expanded_panel: Self,
|
expanded_panel: Self,
|
||||||
add_contents: impl FnOnce(&mut Ui, f32) -> R,
|
add_contents: impl FnOnce(&mut Ui, bool) -> R,
|
||||||
) -> InnerResponse<R> {
|
) -> InnerResponse<R> {
|
||||||
let how_expanded = animate_expansion(ui, expanded_panel.id.with("animation"), is_expanded);
|
let how_expanded = animate_expansion(ui, expanded_panel.id.with("animation"), is_expanded);
|
||||||
|
|
||||||
let panel = if how_expanded == 0.0 {
|
// When expanding, the user sees the expanded content the moment animation starts.
|
||||||
collapsed_panel
|
// When collapsing, keep showing the expanded content until past the midpoint,
|
||||||
} else if how_expanded < 1.0 {
|
// then swap to the collapsed content for the rest of the slide-out.
|
||||||
let collapsed_size = collapsed_panel.outer_size(ui);
|
let show_expanded_contents = if is_expanded {
|
||||||
let expanded_size = expanded_panel.outer_size(ui);
|
true
|
||||||
let fake_size = lerp(collapsed_size..=expanded_size, how_expanded);
|
|
||||||
expanded_panel.into_fake_animating(fake_size)
|
|
||||||
} else {
|
} else {
|
||||||
expanded_panel
|
0.5 < how_expanded
|
||||||
};
|
};
|
||||||
|
|
||||||
panel.show_inside(ui, |ui| add_contents(ui, how_expanded))
|
if how_expanded == 0.0 {
|
||||||
|
collapsed_panel.show_inside(ui, |ui| add_contents(ui, false))
|
||||||
|
} else {
|
||||||
|
let panel = if how_expanded < 1.0 {
|
||||||
|
// Animate the visible size from collapsed_size to expanded_size,
|
||||||
|
// so the slide picks up where the collapsed panel left off.
|
||||||
|
let collapsed_size = collapsed_panel.outer_size(ui);
|
||||||
|
let expanded_size = expanded_panel.outer_size(ui);
|
||||||
|
let visible_size = lerp(collapsed_size..=expanded_size, how_expanded);
|
||||||
|
let slide_fraction = if 0.0 < expanded_size {
|
||||||
|
visible_size / expanded_size
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
expanded_panel
|
||||||
|
.with_slide_fraction(slide_fraction)
|
||||||
|
.resizable(false)
|
||||||
|
} else {
|
||||||
|
expanded_panel
|
||||||
|
};
|
||||||
|
panel.show_inside(ui, |ui| add_contents(ui, show_expanded_contents))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,11 +462,9 @@ impl Panel {
|
|||||||
let show_separator_line = self.show_separator_line;
|
let show_separator_line = self.show_separator_line;
|
||||||
let outer_size_range = self.outer_size_range;
|
let outer_size_range = self.outer_size_range;
|
||||||
|
|
||||||
let frame = self
|
let frame = self.resolve_frame(parent_ui);
|
||||||
.frame
|
|
||||||
.unwrap_or_else(|| Frame::side_top_panel(parent_ui.style()));
|
|
||||||
let available_rect = parent_ui.available_rect_before_wrap();
|
let available_rect = parent_ui.available_rect_before_wrap();
|
||||||
let mut outer_size = self.initial_outer_size(parent_ui, frame);
|
let mut outer_size = self.outer_size(parent_ui);
|
||||||
let mut outer_rect = self.compute_outer_rect(available_rect, outer_size);
|
let mut outer_rect = self.compute_outer_rect(available_rect, outer_size);
|
||||||
|
|
||||||
// Check for duplicate id
|
// Check for duplicate id
|
||||||
@@ -421,15 +489,29 @@ impl Panel {
|
|||||||
// may change and round_ui() uses the size.
|
// may change and round_ui() uses the size.
|
||||||
outer_rect = outer_rect.round_ui();
|
outer_rect = outer_rect.round_ui();
|
||||||
|
|
||||||
|
// Slide animation: translate the panel off-screen toward its fixed edge.
|
||||||
|
// When `slide_fraction == 1.0` this is a no-op.
|
||||||
|
let slide_distance = (1.0 - self.slide_fraction) * outer_size;
|
||||||
|
let shifted_outer_rect = if slide_distance == 0.0 {
|
||||||
|
outer_rect
|
||||||
|
} else {
|
||||||
|
outer_rect
|
||||||
|
.translate(slide_distance * side.dir_vec2())
|
||||||
|
.round_ui()
|
||||||
|
};
|
||||||
|
// The portion of the panel actually visible inside the parent's available area.
|
||||||
|
// The parent only allocates this much; neighbors follow the slide.
|
||||||
|
let visible_outer_rect = shifted_outer_rect.intersect(available_rect);
|
||||||
|
|
||||||
let mut panel_ui = parent_ui.new_child(
|
let mut panel_ui = parent_ui.new_child(
|
||||||
UiBuilder::new()
|
UiBuilder::new()
|
||||||
.id_salt(id)
|
.id_salt(id)
|
||||||
.ui_stack_info(UiStackInfo::new(side.ui_kind()))
|
.ui_stack_info(UiStackInfo::new(side.ui_kind()))
|
||||||
.max_rect(outer_rect)
|
.max_rect(shifted_outer_rect)
|
||||||
.layout(Layout::top_down(Align::Min)),
|
.layout(Layout::top_down(Align::Min)),
|
||||||
);
|
);
|
||||||
panel_ui.expand_to_include_rect(outer_rect);
|
panel_ui.expand_to_include_rect(shifted_outer_rect);
|
||||||
panel_ui.set_clip_rect(outer_rect); // If we overflow, don't do so visibly (#4475)
|
panel_ui.set_clip_rect(visible_outer_rect); // Hides the off-screen part during a slide; also prevents overflow (#4475).
|
||||||
|
|
||||||
let axis = side.axis();
|
let axis = side.axis();
|
||||||
let panel_axis_min =
|
let panel_axis_min =
|
||||||
@@ -448,29 +530,30 @@ impl Panel {
|
|||||||
add_contents(content_ui)
|
add_contents(content_ui)
|
||||||
});
|
});
|
||||||
|
|
||||||
// `Frame::show` returns the _outer_ rect (including margin & border).
|
// `Frame::show` returns the panel's (shifted) _outer_ rect, including margin & border.
|
||||||
let outer_rect = inner_response.response.rect;
|
let shifted_outer_rect = inner_response.response.rect;
|
||||||
|
let visible_outer_rect = shifted_outer_rect.intersect(available_rect);
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut cursor = parent_ui.cursor();
|
let mut cursor = parent_ui.cursor();
|
||||||
match side {
|
match side {
|
||||||
PanelSide::Left | PanelSide::Top => {
|
PanelSide::Left | PanelSide::Top => {
|
||||||
cursor.min[axis] = outer_rect.max[axis];
|
cursor.min[axis] = visible_outer_rect.max[axis];
|
||||||
}
|
}
|
||||||
PanelSide::Right | PanelSide::Bottom => {
|
PanelSide::Right | PanelSide::Bottom => {
|
||||||
cursor.max[axis] = outer_rect.min[axis];
|
cursor.max[axis] = visible_outer_rect.min[axis];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parent_ui.set_cursor(cursor);
|
parent_ui.set_cursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
parent_ui.expand_to_include_rect(outer_rect);
|
parent_ui.expand_to_include_rect(visible_outer_rect);
|
||||||
|
|
||||||
let (resize_hover, is_resizing) = if resizable {
|
let (resize_hover, is_resizing) = if resizable {
|
||||||
// Now we do the actual resize interaction, on top of all the contents,
|
// Now we do the actual resize interaction, on top of all the contents,
|
||||||
// otherwise its input could be eaten by the contents, e.g. a
|
// otherwise its input could be eaten by the contents, e.g. a
|
||||||
// `ScrollArea` on either side of the panel boundary.
|
// `ScrollArea` on either side of the panel boundary.
|
||||||
self.resize_panel(outer_rect, parent_ui)
|
self.resize_panel(shifted_outer_rect, parent_ui)
|
||||||
} else {
|
} else {
|
||||||
(false, false)
|
(false, false)
|
||||||
};
|
};
|
||||||
@@ -479,9 +562,18 @@ impl Panel {
|
|||||||
parent_ui.set_cursor_icon(self.cursor_icon(outer_size));
|
parent_ui.set_cursor_icon(self.cursor_icon(outer_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelState { outer_rect }.store(parent_ui, id);
|
if self.slide_fraction == 1.0 {
|
||||||
|
// Only persist the panel's rect when it's fully expanded —
|
||||||
|
// skip while sliding so the stored rect always reflects the real layout.
|
||||||
|
PanelState {
|
||||||
|
outer_rect: shifted_outer_rect,
|
||||||
|
}
|
||||||
|
.store(parent_ui, id);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
// Hide the separator once the panel is mostly slid off — at that point
|
||||||
|
// the line would just be a stray dash hovering near the parent edge.
|
||||||
|
if 0.01 < self.slide_fraction {
|
||||||
let stroke = if is_resizing {
|
let stroke = if is_resizing {
|
||||||
parent_ui.style().visuals.widgets.active.fg_stroke // highly visible
|
parent_ui.style().visuals.widgets.active.fg_stroke // highly visible
|
||||||
} else if resize_hover {
|
} else if resize_hover {
|
||||||
@@ -493,8 +585,8 @@ impl Panel {
|
|||||||
Stroke::NONE
|
Stroke::NONE
|
||||||
};
|
};
|
||||||
// TODO(emilk): draw line on top of all panels in this ui when https://github.com/emilk/egui/issues/1516 is done
|
// TODO(emilk): draw line on top of all panels in this ui when https://github.com/emilk/egui/issues/1516 is done
|
||||||
let line_pos = side.resize_pos(outer_rect) + 0.5 * side.sign() * stroke.width;
|
let line_pos = side.resize_pos(shifted_outer_rect) + 0.5 * side.sign() * stroke.width;
|
||||||
let cross_range = outer_rect.range_along(side.cross_axis());
|
let cross_range = shifted_outer_rect.range_along(side.cross_axis());
|
||||||
if axis == 0 {
|
if axis == 0 {
|
||||||
parent_ui.painter().vline(line_pos, cross_range, stroke);
|
parent_ui.painter().vline(line_pos, cross_range, stroke);
|
||||||
} else {
|
} else {
|
||||||
@@ -505,15 +597,23 @@ impl Panel {
|
|||||||
inner_response
|
inner_response
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Outer size to start the frame with: from persisted state, or a sensible default.
|
/// The configured [`Frame`], or the default side/top panel frame for this [`Ui`].
|
||||||
fn initial_outer_size(&self, ui: &Ui, frame: Frame) -> f32 {
|
fn resolve_frame(&self, ui: &Ui) -> Frame {
|
||||||
|
self.frame
|
||||||
|
.unwrap_or_else(|| Frame::side_top_panel(ui.style()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current _outer_ width or height of the panel (from previous frame),
|
||||||
|
/// including the [`Frame`] margin & border, or fall back to some default.
|
||||||
|
fn outer_size(&self, ui: &Ui) -> f32 {
|
||||||
let axis = self.side.axis();
|
let axis = self.side.axis();
|
||||||
if let Some(state) = PanelState::load(ui, self.id) {
|
if let Some(state) = PanelState::load(ui, self.id) {
|
||||||
state.outer_rect.size_along(axis)
|
state.outer_rect.size_along(axis)
|
||||||
|
} else if let Some(default_outer_size) = self.default_outer_size {
|
||||||
|
default_outer_size
|
||||||
} else {
|
} else {
|
||||||
self.default_outer_size.unwrap_or_else(|| {
|
let frame = self.resolve_frame(ui);
|
||||||
ui.style().spacing.interact_size[axis] + frame.total_margin().sum()[axis]
|
ui.style().spacing.interact_size[axis] + frame.total_margin().sum()[axis]
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,28 +670,11 @@ impl Panel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a non-resizable, fixed-size clone of this panel for animating between sizes.
|
/// Slide the panel toward its fixed edge. `1.0` = fully visible, `0.0` = fully off-screen.
|
||||||
///
|
#[inline]
|
||||||
/// Uses a distinct id so the resulting panel doesn't clash with the real one.
|
fn with_slide_fraction(mut self, slide_fraction: f32) -> Self {
|
||||||
fn into_fake_animating(self, outer_size: f32) -> Self {
|
self.slide_fraction = slide_fraction;
|
||||||
Self {
|
self
|
||||||
id: self.id.with("animating_panel"),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
.resizable(false)
|
|
||||||
.exact_size(outer_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current _outer_ width or height of the panel (from previous frame),
|
|
||||||
/// including the [`Frame`] margin & border,
|
|
||||||
/// or fall back to some default.
|
|
||||||
fn outer_size(&self, ctx: &Context) -> f32 {
|
|
||||||
let axis = self.side.axis();
|
|
||||||
if let Some(state) = PanelState::load(ctx, self.id) {
|
|
||||||
state.outer_rect.size_along(axis)
|
|
||||||
} else {
|
|
||||||
ctx.global_style().spacing.interact_size[axis]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,22 @@
|
|||||||
#[derive(Clone, Default, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct Panels {}
|
pub struct Panels {
|
||||||
|
left: bool,
|
||||||
|
right: bool,
|
||||||
|
top: bool,
|
||||||
|
bottom: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Panels {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
left: true,
|
||||||
|
right: true,
|
||||||
|
top: true,
|
||||||
|
bottom: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl crate::Demo for Panels {
|
impl crate::Demo for Panels {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
@@ -11,7 +27,7 @@ impl crate::Demo for Panels {
|
|||||||
use crate::View as _;
|
use crate::View as _;
|
||||||
egui::Window::new("Panels")
|
egui::Window::new("Panels")
|
||||||
.default_width(600.0)
|
.default_width(600.0)
|
||||||
.default_height(400.0)
|
.default_height(600.0)
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.open(open)
|
.open(open)
|
||||||
.constrain_to(ui.available_rect_before_wrap())
|
.constrain_to(ui.available_rect_before_wrap())
|
||||||
@@ -23,10 +39,17 @@ impl crate::View for Panels {
|
|||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
// Note that the order we add the panels is very important!
|
// Note that the order we add the panels is very important!
|
||||||
|
|
||||||
|
let Self {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
top,
|
||||||
|
bottom,
|
||||||
|
} = self;
|
||||||
|
|
||||||
egui::Panel::top("top_panel")
|
egui::Panel::top("top_panel")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.min_size(32.0)
|
.min_size(32.0)
|
||||||
.show_inside(ui, |ui| {
|
.show_animated_inside(ui, *top, |ui| {
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Expandable Upper Panel");
|
ui.heading("Expandable Upper Panel");
|
||||||
@@ -39,7 +62,7 @@ impl crate::View for Panels {
|
|||||||
.resizable(true)
|
.resizable(true)
|
||||||
.default_size(150.0)
|
.default_size(150.0)
|
||||||
.size_range(80.0..=200.0)
|
.size_range(80.0..=200.0)
|
||||||
.show_inside(ui, |ui| {
|
.show_animated_inside(ui, *left, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Left Panel");
|
ui.heading("Left Panel");
|
||||||
});
|
});
|
||||||
@@ -52,7 +75,7 @@ impl crate::View for Panels {
|
|||||||
.resizable(true)
|
.resizable(true)
|
||||||
.default_size(150.0)
|
.default_size(150.0)
|
||||||
.size_range(80.0..=200.0)
|
.size_range(80.0..=200.0)
|
||||||
.show_inside(ui, |ui| {
|
.show_animated_inside(ui, *right, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Right Panel");
|
ui.heading("Right Panel");
|
||||||
});
|
});
|
||||||
@@ -61,23 +84,45 @@ impl crate::View for Panels {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::Panel::bottom("bottom_panel")
|
egui::Panel::show_animated_between_inside(
|
||||||
.resizable(false)
|
ui,
|
||||||
.min_size(0.0)
|
*bottom,
|
||||||
.show_inside(ui, |ui| {
|
egui::Panel::bottom("bottom_panel_collapsed"),
|
||||||
ui.vertical_centered(|ui| {
|
egui::Panel::bottom("bottom_panel_expanded"),
|
||||||
ui.heading("Bottom Panel");
|
|ui, expanded| {
|
||||||
});
|
if expanded {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.add(crate::egui_github_link_file!());
|
if ui.button("Collapse bottom panel").clicked() {
|
||||||
});
|
*bottom = false;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
ui.label(egui::RichText::new(crate::LOREM_IPSUM_LONG).small().weak());
|
||||||
|
} else {
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
if ui.button("Expand bottom panel").clicked() {
|
||||||
|
*bottom = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// TODO(emilk): This extra panel is superfluous - just use what's left of `ui` instead
|
// TODO(emilk): This extra panel is superfluous - just use what's left of `ui` instead
|
||||||
egui::CentralPanel::default().show_inside(ui, |ui| {
|
egui::CentralPanel::default().show_inside(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Central Panel");
|
ui.heading("Central Panel");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Panel toggles:");
|
||||||
|
ui.toggle_value(left, "⬅");
|
||||||
|
ui.toggle_value(top, "⬆");
|
||||||
|
ui.toggle_value(bottom, "⬇");
|
||||||
|
ui.toggle_value(right, "➡");
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
lorem_ipsum(ui);
|
lorem_ipsum(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1cd58e9912d1db051c93a9b2ea340f3f6dfe64d38ed54aa1b8292bc6f1bf970c
|
oid sha256:b339d04f331a5d5f7ea17a1c68f61626e08ad5199aec9181080c711a60f07d53
|
||||||
size 215502
|
size 344102
|
||||||
|
|||||||
Reference in New Issue
Block a user