Make it easy to panels inside of Ui:s (#629)
* Allow using the layout cursor to restrict available area * Avoid id clashes when putting panels inside a Ui * Panels: Propagate height/width range to inner Ui * Allow easy placement of panels inside of Ui:s * demo: simplify Windows with Panels demo
This commit is contained in:
parent
ee50cca696
commit
3e2746a288
6 changed files with 164 additions and 83 deletions
|
@ -151,7 +151,7 @@ impl SidePanel {
|
||||||
width_range,
|
width_range,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let available_rect = ui.max_rect();
|
let available_rect = ui.available_rect_before_wrap();
|
||||||
let mut panel_rect = available_rect;
|
let mut panel_rect = available_rect;
|
||||||
{
|
{
|
||||||
let mut width = default_width;
|
let mut width = default_width;
|
||||||
|
@ -187,7 +187,8 @@ impl SidePanel {
|
||||||
is_resizing = ui.memory().interaction.drag_id == Some(resize_id);
|
is_resizing = ui.memory().interaction.drag_id == Some(resize_id);
|
||||||
if is_resizing {
|
if is_resizing {
|
||||||
let width = (pointer.x - side.side_x(panel_rect)).abs();
|
let width = (pointer.x - side.side_x(panel_rect)).abs();
|
||||||
let width = clamp_to_range(width, width_range).at_most(available_rect.width());
|
let width =
|
||||||
|
clamp_to_range(width, width_range.clone()).at_most(available_rect.width());
|
||||||
side.set_rect_width(&mut panel_rect, width);
|
side.set_rect_width(&mut panel_rect, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,15 +202,31 @@ impl SidePanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut panel_ui = ui.child_ui(panel_rect, Layout::top_down(Align::Min));
|
let mut panel_ui = ui.child_ui_with_id_source(panel_rect, Layout::top_down(Align::Min), id);
|
||||||
panel_ui.expand_to_include_rect(panel_rect);
|
panel_ui.expand_to_include_rect(panel_rect);
|
||||||
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
||||||
let inner_response = frame.show(&mut panel_ui, |ui| {
|
let inner_response = frame.show(&mut panel_ui, |ui| {
|
||||||
ui.set_min_height(ui.max_rect_finite().height()); // Make sure the frame fills the full height
|
ui.set_min_height(ui.max_rect_finite().height()); // Make sure the frame fills the full height
|
||||||
|
ui.set_width_range(width_range);
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
});
|
});
|
||||||
|
|
||||||
let rect = inner_response.response.rect;
|
let rect = inner_response.response.rect;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor = ui.cursor();
|
||||||
|
match side {
|
||||||
|
Side::Left => {
|
||||||
|
cursor.min.x = rect.max.x + ui.spacing().item_spacing.x;
|
||||||
|
}
|
||||||
|
Side::Right => {
|
||||||
|
cursor.max.x = rect.min.x - ui.spacing().item_spacing.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.set_cursor(cursor);
|
||||||
|
}
|
||||||
|
ui.expand_to_include_rect(rect);
|
||||||
|
|
||||||
ui.memory().id_data.insert(id, PanelState { rect });
|
ui.memory().id_data.insert(id, PanelState { rect });
|
||||||
|
|
||||||
if resize_hover || is_resizing {
|
if resize_hover || is_resizing {
|
||||||
|
@ -230,6 +247,7 @@ impl SidePanel {
|
||||||
|
|
||||||
inner_response
|
inner_response
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show<R>(
|
pub fn show<R>(
|
||||||
self,
|
self,
|
||||||
ctx: &CtxRef,
|
ctx: &CtxRef,
|
||||||
|
@ -390,7 +408,7 @@ impl TopBottomPanel {
|
||||||
height_range,
|
height_range,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let available_rect = ui.max_rect();
|
let available_rect = ui.available_rect_before_wrap();
|
||||||
let mut panel_rect = available_rect;
|
let mut panel_rect = available_rect;
|
||||||
{
|
{
|
||||||
let state = ui.memory().id_data.get::<PanelState>(&id).copied();
|
let state = ui.memory().id_data.get::<PanelState>(&id).copied();
|
||||||
|
@ -428,8 +446,8 @@ impl TopBottomPanel {
|
||||||
is_resizing = ui.memory().interaction.drag_id == Some(resize_id);
|
is_resizing = ui.memory().interaction.drag_id == Some(resize_id);
|
||||||
if is_resizing {
|
if is_resizing {
|
||||||
let height = (pointer.y - side.side_y(panel_rect)).abs();
|
let height = (pointer.y - side.side_y(panel_rect)).abs();
|
||||||
let height =
|
let height = clamp_to_range(height, height_range.clone())
|
||||||
clamp_to_range(height, height_range).at_most(available_rect.height());
|
.at_most(available_rect.height());
|
||||||
side.set_rect_height(&mut panel_rect, height);
|
side.set_rect_height(&mut panel_rect, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,15 +461,31 @@ impl TopBottomPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut panel_ui = ui.child_ui(panel_rect, Layout::top_down(Align::Min));
|
let mut panel_ui = ui.child_ui_with_id_source(panel_rect, Layout::top_down(Align::Min), id);
|
||||||
panel_ui.expand_to_include_rect(panel_rect);
|
panel_ui.expand_to_include_rect(panel_rect);
|
||||||
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
|
||||||
let inner_response = frame.show(&mut panel_ui, |ui| {
|
let inner_response = frame.show(&mut panel_ui, |ui| {
|
||||||
ui.set_min_width(ui.max_rect_finite().width()); // Make the frame fill full width
|
ui.set_min_width(ui.max_rect_finite().width()); // Make the frame fill full width
|
||||||
|
ui.set_height_range(height_range);
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
});
|
});
|
||||||
|
|
||||||
let rect = inner_response.response.rect;
|
let rect = inner_response.response.rect;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cursor = ui.cursor();
|
||||||
|
match side {
|
||||||
|
TopBottomSide::Top => {
|
||||||
|
cursor.min.y = rect.max.y + ui.spacing().item_spacing.y;
|
||||||
|
}
|
||||||
|
TopBottomSide::Bottom => {
|
||||||
|
cursor.max.y = rect.min.y - ui.spacing().item_spacing.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.set_cursor(cursor);
|
||||||
|
}
|
||||||
|
ui.expand_to_include_rect(rect);
|
||||||
|
|
||||||
ui.memory().id_data.insert(id, PanelState { rect });
|
ui.memory().id_data.insert(id, PanelState { rect });
|
||||||
|
|
||||||
if resize_hover || is_resizing {
|
if resize_hover || is_resizing {
|
||||||
|
@ -563,6 +597,7 @@ impl CentralPanel {
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show<R>(
|
pub fn show<R>(
|
||||||
self,
|
self,
|
||||||
ctx: &CtxRef,
|
ctx: &CtxRef,
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub(crate) struct Region {
|
||||||
///
|
///
|
||||||
/// So one can think of `cursor` as a constraint on the available region.
|
/// So one can think of `cursor` as a constraint on the available region.
|
||||||
///
|
///
|
||||||
/// If something has already been added, this will point ot `style.spacing.item_spacing` beyond the latest child.
|
/// If something has already been added, this will point to `style.spacing.item_spacing` beyond the latest child.
|
||||||
/// The cursor can thus be `style.spacing.item_spacing` pixels outside of the min_rect.
|
/// The cursor can thus be `style.spacing.item_spacing` pixels outside of the min_rect.
|
||||||
pub(crate) cursor: Rect,
|
pub(crate) cursor: Rect,
|
||||||
}
|
}
|
||||||
|
@ -409,7 +409,7 @@ impl Layout {
|
||||||
// NOTE: in normal top-down layout the cursor has moved below the current max_rect,
|
// NOTE: in normal top-down layout the cursor has moved below the current max_rect,
|
||||||
// but the available shouldn't be negative.
|
// but the available shouldn't be negative.
|
||||||
|
|
||||||
// ALSO: with wrapping layouts, cursor jumps to new row before expanding max_rect
|
// ALSO: with wrapping layouts, cursor jumps to new row before expanding max_rect.
|
||||||
|
|
||||||
let mut avail = max_rect;
|
let mut avail = max_rect;
|
||||||
|
|
||||||
|
@ -417,45 +417,47 @@ impl Layout {
|
||||||
Direction::LeftToRight => {
|
Direction::LeftToRight => {
|
||||||
avail.min.x = cursor.min.x;
|
avail.min.x = cursor.min.x;
|
||||||
avail.max.x = avail.max.x.max(cursor.min.x);
|
avail.max.x = avail.max.x.max(cursor.min.x);
|
||||||
if self.main_wrap {
|
|
||||||
avail.min.y = cursor.min.y;
|
|
||||||
avail.max.y = cursor.max.y;
|
|
||||||
}
|
|
||||||
avail.max.x = avail.max.x.max(avail.min.x);
|
avail.max.x = avail.max.x.max(avail.min.x);
|
||||||
avail.max.y = avail.max.y.max(avail.min.y);
|
avail.max.y = avail.max.y.max(avail.min.y);
|
||||||
}
|
}
|
||||||
Direction::RightToLeft => {
|
Direction::RightToLeft => {
|
||||||
avail.max.x = cursor.max.x;
|
avail.max.x = cursor.max.x;
|
||||||
avail.min.x = avail.min.x.min(cursor.max.x);
|
avail.min.x = avail.min.x.min(cursor.max.x);
|
||||||
if self.main_wrap {
|
|
||||||
avail.min.y = cursor.min.y;
|
|
||||||
avail.max.y = cursor.max.y;
|
|
||||||
}
|
|
||||||
avail.min.x = avail.min.x.min(avail.max.x);
|
avail.min.x = avail.min.x.min(avail.max.x);
|
||||||
avail.max.y = avail.max.y.max(avail.min.y);
|
avail.max.y = avail.max.y.max(avail.min.y);
|
||||||
}
|
}
|
||||||
Direction::TopDown => {
|
Direction::TopDown => {
|
||||||
avail.min.y = cursor.min.y;
|
avail.min.y = cursor.min.y;
|
||||||
avail.max.y = avail.max.y.max(cursor.min.y);
|
avail.max.y = avail.max.y.max(cursor.min.y);
|
||||||
if self.main_wrap {
|
|
||||||
avail.min.x = cursor.min.x;
|
|
||||||
avail.max.x = cursor.max.x;
|
|
||||||
}
|
|
||||||
avail.max.x = avail.max.x.max(avail.min.x);
|
avail.max.x = avail.max.x.max(avail.min.x);
|
||||||
avail.max.y = avail.max.y.max(avail.min.y);
|
avail.max.y = avail.max.y.max(avail.min.y);
|
||||||
}
|
}
|
||||||
Direction::BottomUp => {
|
Direction::BottomUp => {
|
||||||
avail.max.y = cursor.max.y;
|
avail.max.y = cursor.max.y;
|
||||||
avail.min.y = avail.min.y.min(cursor.max.y);
|
avail.min.y = avail.min.y.min(cursor.max.y);
|
||||||
if self.main_wrap {
|
|
||||||
avail.min.x = cursor.min.x;
|
|
||||||
avail.max.x = cursor.max.x;
|
|
||||||
}
|
|
||||||
avail.max.x = avail.max.x.max(avail.min.x);
|
avail.max.x = avail.max.x.max(avail.min.x);
|
||||||
avail.min.y = avail.min.y.min(avail.max.y);
|
avail.min.y = avail.min.y.min(avail.max.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can use the cursor to restrict the available region.
|
||||||
|
// For instance, we use this to restrict the available space of a parent Ui
|
||||||
|
// after adding a panel to it.
|
||||||
|
// We also use it for wrapping layouts.
|
||||||
|
avail = avail.intersect(cursor);
|
||||||
|
|
||||||
|
// Make sure it isn't negative:
|
||||||
|
if avail.max.x < avail.min.x {
|
||||||
|
let x = 0.5 * (avail.min.x + avail.max.x);
|
||||||
|
avail.min.x = x;
|
||||||
|
avail.max.x = x;
|
||||||
|
}
|
||||||
|
if avail.max.y < avail.min.y {
|
||||||
|
let y = 0.5 * (avail.min.y + avail.max.y);
|
||||||
|
avail.min.y = y;
|
||||||
|
avail.max.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
avail
|
avail
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,7 +602,9 @@ impl Layout {
|
||||||
) -> Rect {
|
) -> Rect {
|
||||||
let frame = self.next_frame_ignore_wrap(region, size);
|
let frame = self.next_frame_ignore_wrap(region, size);
|
||||||
let rect = self.align_size_within_rect(size, frame);
|
let rect = self.align_size_within_rect(size, frame);
|
||||||
crate::egui_assert!((rect.size() - size).length() < 1.0);
|
crate::egui_assert!(!rect.any_nan());
|
||||||
|
crate::egui_assert!((rect.width() - size.x).abs() < 1.0 || size.x == f32::INFINITY);
|
||||||
|
crate::egui_assert!((rect.height() - size.y).abs() < 1.0 || size.y == f32::INFINITY);
|
||||||
rect
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,11 @@ impl Placer {
|
||||||
pub(crate) fn cursor(&self) -> Rect {
|
pub(crate) fn cursor(&self) -> Rect {
|
||||||
self.region.cursor
|
self.region.cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn set_cursor(&mut self, cursor: Rect) {
|
||||||
|
self.region.cursor = cursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Placer {
|
impl Placer {
|
||||||
|
|
|
@ -78,12 +78,22 @@ impl Ui {
|
||||||
|
|
||||||
/// Create a new `Ui` at a specific region.
|
/// Create a new `Ui` at a specific region.
|
||||||
pub fn child_ui(&mut self, max_rect: Rect, layout: Layout) -> Self {
|
pub fn child_ui(&mut self, max_rect: Rect, layout: Layout) -> Self {
|
||||||
|
self.child_ui_with_id_source(max_rect, layout, "child")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `Ui` at a specific region with a specific id.
|
||||||
|
pub fn child_ui_with_id_source(
|
||||||
|
&mut self,
|
||||||
|
max_rect: Rect,
|
||||||
|
layout: Layout,
|
||||||
|
id_source: impl Hash,
|
||||||
|
) -> Self {
|
||||||
crate::egui_assert!(!max_rect.any_nan());
|
crate::egui_assert!(!max_rect.any_nan());
|
||||||
let next_auto_id_source = Id::new(self.next_auto_id_source).with("child").value();
|
let next_auto_id_source = Id::new(self.next_auto_id_source).with("child").value();
|
||||||
self.next_auto_id_source = self.next_auto_id_source.wrapping_add(1);
|
self.next_auto_id_source = self.next_auto_id_source.wrapping_add(1);
|
||||||
|
|
||||||
Ui {
|
Ui {
|
||||||
id: self.id.with("child"),
|
id: self.id.with(id_source),
|
||||||
next_auto_id_source,
|
next_auto_id_source,
|
||||||
painter: self.painter.clone(),
|
painter: self.painter.clone(),
|
||||||
style: self.style.clone(),
|
style: self.style.clone(),
|
||||||
|
@ -436,6 +446,12 @@ impl Ui {
|
||||||
self.set_max_width(*width.end());
|
self.set_max_width(*width.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `ui.set_height_range(min..=max);` is equivalent to `ui.set_min_height(min); ui.set_max_height(max);`.
|
||||||
|
pub fn set_height_range(&mut self, height: std::ops::RangeInclusive<f32>) {
|
||||||
|
self.set_min_height(*height.start());
|
||||||
|
self.set_max_height(*height.end());
|
||||||
|
}
|
||||||
|
|
||||||
/// Set both the minimum and maximum width.
|
/// Set both the minimum and maximum width.
|
||||||
pub fn set_width(&mut self, width: f32) {
|
pub fn set_width(&mut self, width: f32) {
|
||||||
self.set_min_width(width);
|
self.set_min_width(width);
|
||||||
|
@ -734,6 +750,10 @@ impl Ui {
|
||||||
self.placer.cursor()
|
self.placer.cursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_cursor(&mut self, cursor: Rect) {
|
||||||
|
self.placer.set_cursor(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
/// Where do we expect a zero-sized widget to be placed?
|
/// Where do we expect a zero-sized widget to be placed?
|
||||||
pub(crate) fn next_widget_position(&self) -> Pos2 {
|
pub(crate) fn next_widget_position(&self) -> Pos2 {
|
||||||
self.placer.next_widget_position()
|
self.placer.next_widget_position()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use egui::{menu, Align, CentralPanel, Layout, ScrollArea, SidePanel, TopBottomPanel};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Default)]
|
#[derive(Clone, PartialEq, Default)]
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct WindowWithPanels {}
|
pub struct WindowWithPanels {}
|
||||||
|
@ -8,6 +6,7 @@ impl super::Demo for WindowWithPanels {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"🗖 Window With Panels"
|
"🗖 Window With Panels"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||||
use super::View;
|
use super::View;
|
||||||
let window = egui::Window::new("Window with Panels")
|
let window = egui::Window::new("Window with Panels")
|
||||||
|
@ -23,21 +22,12 @@ impl super::Demo for WindowWithPanels {
|
||||||
|
|
||||||
impl super::View for WindowWithPanels {
|
impl super::View for WindowWithPanels {
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
let left_panel_min_width = 100.;
|
egui::TopBottomPanel::top("top_panel")
|
||||||
let left_panel_max_width = left_panel_min_width * 4.;
|
|
||||||
let bottom_height = 25.;
|
|
||||||
|
|
||||||
ui.expand_to_include_rect(ui.max_rect()); // Expand frame to include it all
|
|
||||||
|
|
||||||
let mut top_rect = ui.available_rect_before_wrap_finite();
|
|
||||||
top_rect.min.y += ui.spacing().item_spacing.y;
|
|
||||||
let mut top_ui = ui.child_ui(top_rect, Layout::top_down(Align::Max));
|
|
||||||
|
|
||||||
let top_response = TopBottomPanel::top("window_menu")
|
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.show_inside(&mut top_ui, |ui| {
|
.min_height(0.0)
|
||||||
menu::bar(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
menu::menu(ui, "Menu", |ui| {
|
egui::menu::bar(ui, |ui| {
|
||||||
|
egui::menu::menu(ui, "Menu", |ui| {
|
||||||
if ui.button("Option 1").clicked() {}
|
if ui.button("Option 1").clicked() {}
|
||||||
if ui.button("Option 2").clicked() {}
|
if ui.button("Option 2").clicked() {}
|
||||||
if ui.button("Option 3").clicked() {}
|
if ui.button("Option 3").clicked() {}
|
||||||
|
@ -45,43 +35,49 @@ impl super::View for WindowWithPanels {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut left_rect = ui.available_rect_before_wrap_finite();
|
egui::TopBottomPanel::bottom("bottom_panel_A")
|
||||||
left_rect.min.y = top_response.response.rect.max.y + ui.spacing().item_spacing.y;
|
.resizable(false)
|
||||||
let mut left_ui = ui.child_ui(left_rect, Layout::top_down(Align::Max));
|
.min_height(0.0)
|
||||||
|
.show_inside(ui, |ui| {
|
||||||
|
ui.label("Bottom Panel A");
|
||||||
|
});
|
||||||
|
|
||||||
let left_response = SidePanel::left("Folders")
|
egui::SidePanel::left("left_panel")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.min_width(left_panel_min_width)
|
.width_range(60.0..=200.0)
|
||||||
.max_width(left_panel_max_width)
|
.show_inside(ui, |ui| {
|
||||||
.show_inside(&mut left_ui, |ui| {
|
egui::ScrollArea::auto_sized().show(ui, |ui| {
|
||||||
ScrollArea::auto_sized().show(ui, |ui| {
|
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.label("Left Panel");
|
ui.label("Left Panel");
|
||||||
})
|
ui.small(crate::LOREM_IPSUM_LONG);
|
||||||
})
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut right_rect = ui.available_rect_before_wrap_finite();
|
egui::SidePanel::right("right_panel")
|
||||||
right_rect.min.x = left_response.response.rect.max.x;
|
.resizable(true)
|
||||||
right_rect.min.y = top_response.response.rect.max.y + ui.spacing().item_spacing.y;
|
.width_range(60.0..=200.0)
|
||||||
let mut right_ui = ui.child_ui(right_rect, Layout::top_down(Align::Max));
|
.show_inside(ui, |ui| {
|
||||||
|
egui::ScrollArea::auto_sized().show(ui, |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.label("Right Panel");
|
||||||
|
ui.small(crate::LOREM_IPSUM_LONG);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
CentralPanel::default().show_inside(&mut right_ui, |ui| {
|
egui::TopBottomPanel::bottom("bottom_panel_B")
|
||||||
let mut rect = ui.min_rect();
|
.resizable(false)
|
||||||
let mut bottom_rect = rect;
|
.min_height(0.0)
|
||||||
bottom_rect.min.y = ui.max_rect_finite().max.y - bottom_height;
|
.show_inside(ui, |ui| {
|
||||||
rect.max.y = bottom_rect.min.y - ui.spacing().indent;
|
ui.label("Bottom Panel B");
|
||||||
let mut child_ui = ui.child_ui(rect, Layout::top_down(Align::Min));
|
});
|
||||||
let mut bottom_ui = ui.child_ui(bottom_rect, Layout::bottom_up(Align::Max));
|
|
||||||
ScrollArea::auto_sized().show(&mut child_ui, |ui| {
|
egui::CentralPanel::default().show_inside(ui, |ui| {
|
||||||
|
egui::ScrollArea::auto_sized().show(ui, |ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.label("Central Panel");
|
ui.label("Central Panel");
|
||||||
})
|
ui.small(crate::LOREM_IPSUM_LONG);
|
||||||
});
|
|
||||||
bottom_ui.vertical(|ui| {
|
|
||||||
ui.separator();
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Bottom Content");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,6 +63,36 @@ impl Align {
|
||||||
Self::Max => 1.0,
|
Self::Max => 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ``` rust
|
||||||
|
/// assert_eq!(emath::Align::Min.align_size_within_range(2.0, 10.0..=20.0), 10.0..=12.0);
|
||||||
|
/// assert_eq!(emath::Align::Center.align_size_within_range(2.0, 10.0..=20.0), 14.0..=16.0);
|
||||||
|
/// assert_eq!(emath::Align::Max.align_size_within_range(2.0, 10.0..=20.0), 18.0..=20.0);
|
||||||
|
/// assert_eq!(emath::Align::Min.align_size_within_range(f32::INFINITY, 10.0..=20.0), 10.0..=f32::INFINITY);
|
||||||
|
/// assert_eq!(emath::Align::Center.align_size_within_range(f32::INFINITY, 10.0..=20.0), f32::NEG_INFINITY..=f32::INFINITY);
|
||||||
|
/// assert_eq!(emath::Align::Max.align_size_within_range(f32::INFINITY, 10.0..=20.0), f32::NEG_INFINITY..=20.0);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn align_size_within_range(
|
||||||
|
self,
|
||||||
|
size: f32,
|
||||||
|
range: RangeInclusive<f32>,
|
||||||
|
) -> RangeInclusive<f32> {
|
||||||
|
let min = *range.start();
|
||||||
|
let max = *range.end();
|
||||||
|
match self {
|
||||||
|
Self::Min => min..=min + size,
|
||||||
|
Self::Center => {
|
||||||
|
if size == f32::INFINITY {
|
||||||
|
f32::NEG_INFINITY..=f32::INFINITY
|
||||||
|
} else {
|
||||||
|
let left = (min + max) / 2.0 - size / 2.0;
|
||||||
|
left..=left + size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Max => max - size..=max,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Align {
|
impl Default for Align {
|
||||||
|
@ -126,18 +156,9 @@ impl Align2 {
|
||||||
|
|
||||||
/// e.g. center a size within a given frame
|
/// e.g. center a size within a given frame
|
||||||
pub fn align_size_within_rect(self, size: Vec2, frame: Rect) -> Rect {
|
pub fn align_size_within_rect(self, size: Vec2, frame: Rect) -> Rect {
|
||||||
let x = match self.x() {
|
let x_range = self.x().align_size_within_range(size.x, frame.x_range());
|
||||||
Align::Min => frame.left(),
|
let y_range = self.y().align_size_within_range(size.y, frame.y_range());
|
||||||
Align::Center => frame.center().x - size.x / 2.0,
|
Rect::from_x_y_ranges(x_range, y_range)
|
||||||
Align::Max => frame.right() - size.x,
|
|
||||||
};
|
|
||||||
let y = match self.y() {
|
|
||||||
Align::Min => frame.top(),
|
|
||||||
Align::Center => frame.center().y - size.y / 2.0,
|
|
||||||
Align::Max => frame.bottom() - size.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
Rect::from_min_size(Pos2::new(x, y), size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pos_in_rect(self, frame: &Rect) -> Pos2 {
|
pub fn pos_in_rect(self, frame: &Rect) -> Pos2 {
|
||||||
|
|
Loading…
Reference in a new issue