Add possibility of panels inside UI (i.e. windows) (#624)
* Adding possibility to have panels inside UI * Adding window with panels demo
This commit is contained in:
parent
d6299bcd91
commit
ff8c4c0d38
4 changed files with 193 additions and 62 deletions
|
@ -137,9 +137,9 @@ impl SidePanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SidePanel {
|
impl SidePanel {
|
||||||
pub fn show<R>(
|
pub fn show_inside<R>(
|
||||||
self,
|
self,
|
||||||
ctx: &CtxRef,
|
ui: &mut Ui,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> InnerResponse<R> {
|
) -> InnerResponse<R> {
|
||||||
let Self {
|
let Self {
|
||||||
|
@ -151,13 +151,11 @@ impl SidePanel {
|
||||||
width_range,
|
width_range,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let layer_id = LayerId::background();
|
let available_rect = ui.max_rect();
|
||||||
|
|
||||||
let available_rect = ctx.available_rect();
|
|
||||||
let mut panel_rect = available_rect;
|
let mut panel_rect = available_rect;
|
||||||
{
|
{
|
||||||
let mut width = default_width;
|
let mut width = default_width;
|
||||||
if let Some(state) = ctx.memory().id_data.get::<PanelState>(&id) {
|
if let Some(state) = ui.memory().id_data.get::<PanelState>(&id) {
|
||||||
width = state.rect.width();
|
width = state.rect.width();
|
||||||
}
|
}
|
||||||
width = clamp_to_range(width, width_range.clone()).at_most(available_rect.width());
|
width = clamp_to_range(width, width_range.clone()).at_most(available_rect.width());
|
||||||
|
@ -168,24 +166,25 @@ impl SidePanel {
|
||||||
let mut is_resizing = false;
|
let mut is_resizing = false;
|
||||||
if resizable {
|
if resizable {
|
||||||
let resize_id = id.with("__resize");
|
let resize_id = id.with("__resize");
|
||||||
if let Some(pointer) = ctx.input().pointer.latest_pos() {
|
if let Some(pointer) = ui.input().pointer.latest_pos() {
|
||||||
let we_are_on_top = ctx
|
let we_are_on_top = ui
|
||||||
|
.ctx()
|
||||||
.layer_id_at(pointer)
|
.layer_id_at(pointer)
|
||||||
.map_or(true, |top_layer_id| top_layer_id == layer_id);
|
.map_or(true, |top_layer_id| top_layer_id == ui.layer_id());
|
||||||
|
|
||||||
let resize_x = side.opposite().side_x(panel_rect);
|
let resize_x = side.opposite().side_x(panel_rect);
|
||||||
let mouse_over_resize_line = we_are_on_top
|
let mouse_over_resize_line = we_are_on_top
|
||||||
&& panel_rect.y_range().contains(&pointer.y)
|
&& panel_rect.y_range().contains(&pointer.y)
|
||||||
&& (resize_x - pointer.x).abs()
|
&& (resize_x - pointer.x).abs()
|
||||||
<= ctx.style().interaction.resize_grab_radius_side;
|
<= ui.style().interaction.resize_grab_radius_side;
|
||||||
|
|
||||||
if ctx.input().pointer.any_pressed()
|
if ui.input().pointer.any_pressed()
|
||||||
&& ctx.input().pointer.any_down()
|
&& ui.input().pointer.any_down()
|
||||||
&& mouse_over_resize_line
|
&& mouse_over_resize_line
|
||||||
{
|
{
|
||||||
ctx.memory().interaction.drag_id = Some(resize_id);
|
ui.memory().interaction.drag_id = Some(resize_id);
|
||||||
}
|
}
|
||||||
is_resizing = ctx.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).at_most(available_rect.width());
|
||||||
|
@ -193,42 +192,58 @@ impl SidePanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
let dragging_something_else =
|
let dragging_something_else =
|
||||||
ctx.input().pointer.any_down() || ctx.input().pointer.any_pressed();
|
ui.input().pointer.any_down() || ui.input().pointer.any_pressed();
|
||||||
resize_hover = mouse_over_resize_line && !dragging_something_else;
|
resize_hover = mouse_over_resize_line && !dragging_something_else;
|
||||||
|
|
||||||
if resize_hover || is_resizing {
|
if resize_hover || is_resizing {
|
||||||
ctx.output().cursor_icon = CursorIcon::ResizeHorizontal;
|
ui.output().cursor_icon = CursorIcon::ResizeHorizontal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let clip_rect = ctx.input().screen_rect();
|
let mut panel_ui = ui.child_ui(panel_rect, Layout::top_down(Align::Min));
|
||||||
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_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(&ctx.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
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
});
|
});
|
||||||
|
|
||||||
let rect = inner_response.response.rect;
|
let rect = inner_response.response.rect;
|
||||||
ctx.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 {
|
||||||
let stroke = if is_resizing {
|
let stroke = if is_resizing {
|
||||||
ctx.style().visuals.widgets.active.bg_stroke
|
ui.style().visuals.widgets.active.bg_stroke
|
||||||
} else {
|
} else {
|
||||||
ctx.style().visuals.widgets.hovered.bg_stroke
|
ui.style().visuals.widgets.hovered.bg_stroke
|
||||||
};
|
};
|
||||||
// draw on top of ALL panels so that the resize line won't be covered by subsequent panels
|
// draw on top of ALL panels so that the resize line won't be covered by subsequent panels
|
||||||
let resize_layer = LayerId::new(Order::PanelResizeLine, Id::new("panel_resize"));
|
let resize_layer = LayerId::new(Order::Foreground, Id::new("panel_resize"));
|
||||||
let resize_x = side.opposite().side_x(rect);
|
let resize_x = side.opposite().side_x(rect);
|
||||||
let top = pos2(resize_x, rect.top());
|
let top = pos2(resize_x, rect.top());
|
||||||
let bottom = pos2(resize_x, rect.bottom());
|
let bottom = pos2(resize_x, rect.bottom());
|
||||||
ctx.layer_painter(resize_layer)
|
ui.ctx()
|
||||||
|
.layer_painter(resize_layer)
|
||||||
.line_segment([top, bottom], stroke);
|
.line_segment([top, bottom], stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner_response
|
||||||
|
}
|
||||||
|
pub fn show<R>(
|
||||||
|
self,
|
||||||
|
ctx: &CtxRef,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> InnerResponse<R> {
|
||||||
|
let layer_id = LayerId::background();
|
||||||
|
let side = self.side;
|
||||||
|
let available_rect = ctx.available_rect();
|
||||||
|
let clip_rect = ctx.input().screen_rect();
|
||||||
|
let mut panel_ui = Ui::new(ctx.clone(), layer_id, self.id, available_rect, clip_rect);
|
||||||
|
|
||||||
|
let inner_response = self.show_inside(&mut panel_ui, add_contents);
|
||||||
|
let rect = inner_response.response.rect;
|
||||||
|
|
||||||
match side {
|
match side {
|
||||||
Side::Left => ctx
|
Side::Left => ctx
|
||||||
.frame_state()
|
.frame_state()
|
||||||
|
@ -237,7 +252,6 @@ impl SidePanel {
|
||||||
.frame_state()
|
.frame_state()
|
||||||
.allocate_right_panel(Rect::from_min_max(rect.min, available_rect.max)),
|
.allocate_right_panel(Rect::from_min_max(rect.min, available_rect.max)),
|
||||||
}
|
}
|
||||||
|
|
||||||
inner_response
|
inner_response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,9 +376,9 @@ impl TopBottomPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TopBottomPanel {
|
impl TopBottomPanel {
|
||||||
pub fn show<R>(
|
pub fn show_inside<R>(
|
||||||
self,
|
self,
|
||||||
ctx: &CtxRef,
|
ui: &mut Ui,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> InnerResponse<R> {
|
) -> InnerResponse<R> {
|
||||||
let Self {
|
let Self {
|
||||||
|
@ -376,16 +390,14 @@ impl TopBottomPanel {
|
||||||
height_range,
|
height_range,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let layer_id = LayerId::background();
|
let available_rect = ui.max_rect();
|
||||||
|
|
||||||
let available_rect = ctx.available_rect();
|
|
||||||
let mut panel_rect = available_rect;
|
let mut panel_rect = available_rect;
|
||||||
{
|
{
|
||||||
let state = ctx.memory().id_data.get::<PanelState>(&id).copied();
|
let state = ui.memory().id_data.get::<PanelState>(&id).copied();
|
||||||
let mut height = if let Some(state) = state {
|
let mut height = if let Some(state) = state {
|
||||||
state.rect.height()
|
state.rect.height()
|
||||||
} else {
|
} else {
|
||||||
default_height.unwrap_or_else(|| ctx.style().spacing.interact_size.y)
|
default_height.unwrap_or_else(|| ui.style().spacing.interact_size.y)
|
||||||
};
|
};
|
||||||
height = clamp_to_range(height, height_range.clone()).at_most(available_rect.height());
|
height = clamp_to_range(height, height_range.clone()).at_most(available_rect.height());
|
||||||
side.set_rect_height(&mut panel_rect, height);
|
side.set_rect_height(&mut panel_rect, height);
|
||||||
|
@ -395,24 +407,25 @@ impl TopBottomPanel {
|
||||||
let mut is_resizing = false;
|
let mut is_resizing = false;
|
||||||
if resizable {
|
if resizable {
|
||||||
let resize_id = id.with("__resize");
|
let resize_id = id.with("__resize");
|
||||||
if let Some(pointer) = ctx.input().pointer.latest_pos() {
|
if let Some(pointer) = ui.input().pointer.latest_pos() {
|
||||||
let we_are_on_top = ctx
|
let we_are_on_top = ui
|
||||||
|
.ctx()
|
||||||
.layer_id_at(pointer)
|
.layer_id_at(pointer)
|
||||||
.map_or(true, |top_layer_id| top_layer_id == layer_id);
|
.map_or(true, |top_layer_id| top_layer_id == ui.layer_id());
|
||||||
|
|
||||||
let resize_y = side.opposite().side_y(panel_rect);
|
let resize_y = side.opposite().side_y(panel_rect);
|
||||||
let mouse_over_resize_line = we_are_on_top
|
let mouse_over_resize_line = we_are_on_top
|
||||||
&& panel_rect.x_range().contains(&pointer.x)
|
&& panel_rect.x_range().contains(&pointer.x)
|
||||||
&& (resize_y - pointer.y).abs()
|
&& (resize_y - pointer.y).abs()
|
||||||
<= ctx.style().interaction.resize_grab_radius_side;
|
<= ui.style().interaction.resize_grab_radius_side;
|
||||||
|
|
||||||
if ctx.input().pointer.any_pressed()
|
if ui.input().pointer.any_pressed()
|
||||||
&& ctx.input().pointer.any_down()
|
&& ui.input().pointer.any_down()
|
||||||
&& mouse_over_resize_line
|
&& mouse_over_resize_line
|
||||||
{
|
{
|
||||||
ctx.memory().interaction.drag_id = Some(resize_id);
|
ui.memory().interaction.drag_id = Some(resize_id);
|
||||||
}
|
}
|
||||||
is_resizing = ctx.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 =
|
||||||
|
@ -421,42 +434,60 @@ impl TopBottomPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
let dragging_something_else =
|
let dragging_something_else =
|
||||||
ctx.input().pointer.any_down() || ctx.input().pointer.any_pressed();
|
ui.input().pointer.any_down() || ui.input().pointer.any_pressed();
|
||||||
resize_hover = mouse_over_resize_line && !dragging_something_else;
|
resize_hover = mouse_over_resize_line && !dragging_something_else;
|
||||||
|
|
||||||
if resize_hover || is_resizing {
|
if resize_hover || is_resizing {
|
||||||
ctx.output().cursor_icon = CursorIcon::ResizeVertical;
|
ui.output().cursor_icon = CursorIcon::ResizeVertical;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let clip_rect = ctx.input().screen_rect();
|
let mut panel_ui = ui.child_ui(panel_rect, Layout::top_down(Align::Min));
|
||||||
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_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(&ctx.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
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
});
|
});
|
||||||
|
|
||||||
let rect = inner_response.response.rect;
|
let rect = inner_response.response.rect;
|
||||||
ctx.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 {
|
||||||
let stroke = if is_resizing {
|
let stroke = if is_resizing {
|
||||||
ctx.style().visuals.widgets.active.bg_stroke
|
ui.style().visuals.widgets.active.bg_stroke
|
||||||
} else {
|
} else {
|
||||||
ctx.style().visuals.widgets.hovered.bg_stroke
|
ui.style().visuals.widgets.hovered.bg_stroke
|
||||||
};
|
};
|
||||||
// draw on top of ALL panels so that the resize line won't be covered by subsequent panels
|
// draw on top of ALL panels so that the resize line won't be covered by subsequent panels
|
||||||
let resize_layer = LayerId::new(Order::PanelResizeLine, Id::new("panel_resize"));
|
let resize_layer = LayerId::new(Order::Foreground, Id::new("panel_resize"));
|
||||||
let resize_y = side.opposite().side_y(rect);
|
let resize_y = side.opposite().side_y(rect);
|
||||||
let left = pos2(rect.left(), resize_y);
|
let left = pos2(rect.left(), resize_y);
|
||||||
let right = pos2(rect.right(), resize_y);
|
let right = pos2(rect.right(), resize_y);
|
||||||
ctx.layer_painter(resize_layer)
|
ui.ctx()
|
||||||
|
.layer_painter(resize_layer)
|
||||||
.line_segment([left, right], stroke);
|
.line_segment([left, right], stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner_response
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show<R>(
|
||||||
|
self,
|
||||||
|
ctx: &CtxRef,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> InnerResponse<R> {
|
||||||
|
let layer_id = LayerId::background();
|
||||||
|
let available_rect = ctx.available_rect();
|
||||||
|
let side = self.side;
|
||||||
|
|
||||||
|
let clip_rect = ctx.input().screen_rect();
|
||||||
|
let mut panel_ui = Ui::new(ctx.clone(), layer_id, self.id, available_rect, clip_rect);
|
||||||
|
|
||||||
|
let inner_response = self.show_inside(&mut panel_ui, add_contents);
|
||||||
|
let rect = inner_response.response.rect;
|
||||||
|
|
||||||
match side {
|
match side {
|
||||||
TopBottomSide::Top => {
|
TopBottomSide::Top => {
|
||||||
ctx.frame_state()
|
ctx.frame_state()
|
||||||
|
@ -516,26 +547,35 @@ impl CentralPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CentralPanel {
|
impl CentralPanel {
|
||||||
|
pub fn show_inside<R>(
|
||||||
|
self,
|
||||||
|
ui: &mut Ui,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> InnerResponse<R> {
|
||||||
|
let Self { frame } = self;
|
||||||
|
|
||||||
|
let panel_rect = ui.available_rect_before_wrap_finite();
|
||||||
|
let mut panel_ui = ui.child_ui(panel_rect, Layout::top_down(Align::Min));
|
||||||
|
|
||||||
|
let frame = frame.unwrap_or_else(|| Frame::central_panel(ui.style()));
|
||||||
|
frame.show(&mut panel_ui, |ui| {
|
||||||
|
ui.expand_to_include_rect(ui.max_rect()); // Expand frame to include it all
|
||||||
|
add_contents(ui)
|
||||||
|
})
|
||||||
|
}
|
||||||
pub fn show<R>(
|
pub fn show<R>(
|
||||||
self,
|
self,
|
||||||
ctx: &CtxRef,
|
ctx: &CtxRef,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> InnerResponse<R> {
|
) -> InnerResponse<R> {
|
||||||
let Self { frame } = self;
|
let available_rect = ctx.available_rect();
|
||||||
|
|
||||||
let panel_rect = ctx.available_rect();
|
|
||||||
|
|
||||||
let layer_id = LayerId::background();
|
let layer_id = LayerId::background();
|
||||||
let id = Id::new("central_panel");
|
let id = Id::new("central_panel");
|
||||||
|
|
||||||
let clip_rect = ctx.input().screen_rect();
|
let clip_rect = ctx.input().screen_rect();
|
||||||
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_rect);
|
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, available_rect, clip_rect);
|
||||||
|
|
||||||
let frame = frame.unwrap_or_else(|| Frame::central_panel(&ctx.style()));
|
let inner_response = self.show_inside(&mut panel_ui, add_contents);
|
||||||
let inner_response = frame.show(&mut panel_ui, |ui| {
|
|
||||||
ui.expand_to_include_rect(ui.max_rect()); // Expand frame to include it all
|
|
||||||
add_contents(ui)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only inform ctx about what we actually used, so we can shrink the native window to fit.
|
// Only inform ctx about what we actually used, so we can shrink the native window to fit.
|
||||||
ctx.frame_state()
|
ctx.frame_state()
|
||||||
|
|
|
@ -28,6 +28,7 @@ impl Default for Demos {
|
||||||
Box::new(super::widget_gallery::WidgetGallery::default()),
|
Box::new(super::widget_gallery::WidgetGallery::default()),
|
||||||
Box::new(super::window_options::WindowOptions::default()),
|
Box::new(super::window_options::WindowOptions::default()),
|
||||||
Box::new(super::tests::WindowResizeTest::default()),
|
Box::new(super::tests::WindowResizeTest::default()),
|
||||||
|
Box::new(super::window_with_panels::WindowWithPanels::default()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub mod tests;
|
||||||
pub mod toggle_switch;
|
pub mod toggle_switch;
|
||||||
pub mod widget_gallery;
|
pub mod widget_gallery;
|
||||||
pub mod window_options;
|
pub mod window_options;
|
||||||
|
pub mod window_with_panels;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
app::DemoApp, demo_app_windows::DemoWindows, misc_demo_window::MiscDemoWindow,
|
app::DemoApp, demo_app_windows::DemoWindows, misc_demo_window::MiscDemoWindow,
|
||||||
|
|
89
egui_demo_lib/src/apps/demo/window_with_panels.rs
Normal file
89
egui_demo_lib/src/apps/demo/window_with_panels.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use egui::{menu, Align, CentralPanel, Layout, ScrollArea, SidePanel, TopBottomPanel};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Default)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct WindowWithPanels {}
|
||||||
|
|
||||||
|
impl super::Demo for WindowWithPanels {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"🗖 Window With Panels"
|
||||||
|
}
|
||||||
|
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||||
|
use super::View;
|
||||||
|
let window = egui::Window::new("Window with Panels")
|
||||||
|
.scroll(false)
|
||||||
|
.title_bar(true)
|
||||||
|
.resizable(true)
|
||||||
|
.collapsible(false)
|
||||||
|
.enabled(true)
|
||||||
|
.open(open);
|
||||||
|
window.show(ctx, |ui| self.ui(ui));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::View for WindowWithPanels {
|
||||||
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
|
let left_panel_min_width = 100.;
|
||||||
|
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)
|
||||||
|
.show_inside(&mut top_ui, |ui| {
|
||||||
|
menu::bar(ui, |ui| {
|
||||||
|
menu::menu(ui, "Menu", |ui| {
|
||||||
|
if ui.button("Option 1").clicked() {}
|
||||||
|
if ui.button("Option 2").clicked() {}
|
||||||
|
if ui.button("Option 3").clicked() {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut left_rect = ui.available_rect_before_wrap_finite();
|
||||||
|
left_rect.min.y = top_response.response.rect.max.y + ui.spacing().item_spacing.y;
|
||||||
|
let mut left_ui = ui.child_ui(left_rect, Layout::top_down(Align::Max));
|
||||||
|
|
||||||
|
let left_response = SidePanel::left("Folders")
|
||||||
|
.resizable(true)
|
||||||
|
.min_width(left_panel_min_width)
|
||||||
|
.max_width(left_panel_max_width)
|
||||||
|
.show_inside(&mut left_ui, |ui| {
|
||||||
|
ScrollArea::auto_sized().show(ui, |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.label("Left Panel");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut right_rect = ui.available_rect_before_wrap_finite();
|
||||||
|
right_rect.min.x = left_response.response.rect.max.x;
|
||||||
|
right_rect.min.y = top_response.response.rect.max.y + ui.spacing().item_spacing.y;
|
||||||
|
let mut right_ui = ui.child_ui(right_rect, Layout::top_down(Align::Max));
|
||||||
|
|
||||||
|
CentralPanel::default().show_inside(&mut right_ui, |ui| {
|
||||||
|
let mut rect = ui.min_rect();
|
||||||
|
let mut bottom_rect = rect;
|
||||||
|
bottom_rect.min.y = ui.max_rect_finite().max.y - bottom_height;
|
||||||
|
rect.max.y = bottom_rect.min.y - ui.spacing().indent;
|
||||||
|
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| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.label("Central Panel");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
bottom_ui.vertical(|ui| {
|
||||||
|
ui.separator();
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Bottom Content");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue