Refactor panels into proper containers
This commit is contained in:
parent
8b51ae5dea
commit
35b949a2d8
9 changed files with 152 additions and 96 deletions
|
@ -7,7 +7,7 @@
|
||||||
* Refactored the interface for `egui::app::App`
|
* Refactored the interface for `egui::app::App`
|
||||||
* Demo App: Add slider to scale all of Egui
|
* Demo App: Add slider to scale all of Egui
|
||||||
* Windows are now constrained to the screen
|
* Windows are now constrained to the screen
|
||||||
* Panels: you can now add side panels using `Context::panel_left` and `Context::panel_top`.
|
* Panels: you can now create panels using `SidePanel` and `TopPanel`.
|
||||||
* Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`).
|
* Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`).
|
||||||
|
|
||||||
## 0.2.0 - 2020-10-10
|
## 0.2.0 - 2020-10-10
|
||||||
|
|
|
@ -31,9 +31,9 @@ impl Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn menu_bar(style: &Style) -> Self {
|
pub(crate) fn panel(style: &Style) -> Self {
|
||||||
Self {
|
Self {
|
||||||
margin: Vec2::splat(1.0),
|
margin: Vec2::new(8.0, 2.0),
|
||||||
corner_radius: 0.0,
|
corner_radius: 0.0,
|
||||||
fill: style.visuals.widgets.noninteractive.bg_fill,
|
fill: style.visuals.widgets.noninteractive.bg_fill,
|
||||||
stroke: style.visuals.widgets.noninteractive.bg_stroke,
|
stroke: style.visuals.widgets.noninteractive.bg_stroke,
|
||||||
|
@ -58,10 +58,6 @@ impl Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panel(style: &Style) -> Self {
|
|
||||||
Self::popup(style)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill(mut self, fill: Srgba) -> Self {
|
pub fn fill(mut self, fill: Srgba) -> Self {
|
||||||
self.fill = fill;
|
self.fill = fill;
|
||||||
self
|
self
|
||||||
|
|
|
@ -6,12 +6,20 @@ pub(crate) mod area;
|
||||||
pub(crate) mod collapsing_header;
|
pub(crate) mod collapsing_header;
|
||||||
mod combo_box;
|
mod combo_box;
|
||||||
pub(crate) mod frame;
|
pub(crate) mod frame;
|
||||||
|
pub(crate) mod panel;
|
||||||
pub(crate) mod popup;
|
pub(crate) mod popup;
|
||||||
pub(crate) mod resize;
|
pub(crate) mod resize;
|
||||||
pub(crate) mod scroll_area;
|
pub(crate) mod scroll_area;
|
||||||
pub(crate) mod window;
|
pub(crate) mod window;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
area::Area, collapsing_header::*, combo_box::*, frame::Frame, popup::*, resize::Resize,
|
area::Area,
|
||||||
scroll_area::ScrollArea, window::Window,
|
collapsing_header::*,
|
||||||
|
combo_box::*,
|
||||||
|
frame::Frame,
|
||||||
|
panel::{SidePanel, TopPanel},
|
||||||
|
popup::*,
|
||||||
|
resize::Resize,
|
||||||
|
scroll_area::ScrollArea,
|
||||||
|
window::Window,
|
||||||
};
|
};
|
||||||
|
|
105
egui/src/containers/panel.rs
Normal file
105
egui/src/containers/panel.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
//! Panels
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// A panel that covers the entire left side of the screen.
|
||||||
|
///
|
||||||
|
/// Panels should be added before adding any `Window`s.
|
||||||
|
pub struct SidePanel {
|
||||||
|
id: Id,
|
||||||
|
max_width: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SidePanel {
|
||||||
|
/// The given `max_width` is a soft maximum (as always), and the actual panel may be smaller or larger.
|
||||||
|
pub fn left(id: Id, max_width: f32) -> Self {
|
||||||
|
Self { id, max_width }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SidePanel {
|
||||||
|
pub fn show<R>(
|
||||||
|
self,
|
||||||
|
ctx: &Arc<Context>,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> (R, Response) {
|
||||||
|
let Self { id, max_width } = self;
|
||||||
|
|
||||||
|
let mut panel_rect = ctx.available_rect();
|
||||||
|
panel_rect.max.x = panel_rect.max.x.at_most(panel_rect.min.x + max_width);
|
||||||
|
|
||||||
|
let layer_id = LayerId::background();
|
||||||
|
|
||||||
|
let clip_rect = ctx.input().screen_rect();
|
||||||
|
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_rect);
|
||||||
|
|
||||||
|
let frame = Frame::panel(&ctx.style());
|
||||||
|
let r = frame.show(&mut panel_ui, |ui| {
|
||||||
|
ui.set_min_height(ui.max_rect_finite().height()); // fill full height
|
||||||
|
add_contents(ui)
|
||||||
|
});
|
||||||
|
|
||||||
|
let panel_rect = panel_ui.min_rect();
|
||||||
|
let response = panel_ui.interact_hover(panel_rect);
|
||||||
|
|
||||||
|
ctx.allocate_left_panel(panel_rect);
|
||||||
|
|
||||||
|
(r, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// A panel that covers the entire top side of the screen.
|
||||||
|
///
|
||||||
|
/// Panels should be added before adding any `Window`s.
|
||||||
|
pub struct TopPanel {
|
||||||
|
id: Id,
|
||||||
|
max_height: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TopPanel {
|
||||||
|
/// Default height is that of `interact_size.y` (i.e. a button),
|
||||||
|
/// but the panel will expand as needed.
|
||||||
|
pub fn top(id: Id) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
max_height: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TopPanel {
|
||||||
|
pub fn show<R>(
|
||||||
|
self,
|
||||||
|
ctx: &Arc<Context>,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> (R, Response) {
|
||||||
|
let Self { id, max_height } = self;
|
||||||
|
let max_height = max_height.unwrap_or_else(|| ctx.style().spacing.interact_size.y);
|
||||||
|
|
||||||
|
let mut panel_rect = ctx.available_rect();
|
||||||
|
panel_rect.max.y = panel_rect.max.y.at_most(panel_rect.min.y + max_height);
|
||||||
|
|
||||||
|
let layer_id = LayerId::background();
|
||||||
|
|
||||||
|
let clip_rect = ctx.input().screen_rect();
|
||||||
|
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_rect);
|
||||||
|
|
||||||
|
let frame = Frame::panel(&ctx.style());
|
||||||
|
let r = frame.show(&mut panel_ui, |ui| {
|
||||||
|
ui.set_min_width(ui.max_rect_finite().width()); // fill full width
|
||||||
|
add_contents(ui)
|
||||||
|
});
|
||||||
|
|
||||||
|
let panel_rect = panel_ui.min_rect();
|
||||||
|
let response = panel_ui.interact_hover(panel_rect);
|
||||||
|
|
||||||
|
ctx.allocate_top_panel(panel_rect);
|
||||||
|
|
||||||
|
(r, response)
|
||||||
|
}
|
||||||
|
}
|
|
@ -287,77 +287,18 @@ impl Context {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
/// Create a panel that covers the entire left side of the screen.
|
/// Shrink `available_rect()`.
|
||||||
/// The given `max_width` is a soft maximum (as always), and the actual panel may be smaller or larger.
|
pub(crate) fn allocate_left_panel(&self, panel_rect: Rect) {
|
||||||
/// You should call this *before* adding windows to Egui.
|
|
||||||
pub fn panel_left<R>(
|
|
||||||
self: &Arc<Context>,
|
|
||||||
max_width: f32,
|
|
||||||
frame: Frame,
|
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
|
||||||
) -> (R, Response) {
|
|
||||||
let mut panel_rect = self.available_rect();
|
|
||||||
panel_rect.max.x = panel_rect.max.x.at_most(panel_rect.min.x + max_width);
|
|
||||||
|
|
||||||
let id = Id::background();
|
|
||||||
let layer_id = LayerId {
|
|
||||||
order: Order::Background,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
|
|
||||||
let clip_rect = self.input.screen_rect();
|
|
||||||
let mut panel_ui = Ui::new(self.clone(), layer_id, id, panel_rect, clip_rect);
|
|
||||||
let r = frame.show(&mut panel_ui, |ui| {
|
|
||||||
ui.set_min_height(ui.max_rect_finite().height()); // fill full height
|
|
||||||
add_contents(ui)
|
|
||||||
});
|
|
||||||
|
|
||||||
let panel_rect = panel_ui.min_rect();
|
|
||||||
let response = panel_ui.interact_hover(panel_rect);
|
|
||||||
|
|
||||||
// Shrink out `available_rect`:
|
|
||||||
let mut remainder = self.available_rect();
|
let mut remainder = self.available_rect();
|
||||||
remainder.min.x = panel_rect.max.x;
|
remainder.min.x = panel_rect.max.x;
|
||||||
*self.available_rect.lock() = Some(remainder);
|
*self.available_rect.lock() = Some(remainder);
|
||||||
|
|
||||||
(r, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a panel that covers the entire top side of the screen.
|
/// Shrink `available_rect()`.
|
||||||
/// This can be useful to add a menu bar to the whole window.
|
pub(crate) fn allocate_top_panel(&self, panel_rect: Rect) {
|
||||||
/// The given `max_height` is a soft maximum (as always), and the actual panel may be smaller or larger.
|
|
||||||
/// You should call this *before* adding windows to Egui.
|
|
||||||
pub fn panel_top<R>(
|
|
||||||
self: &Arc<Context>,
|
|
||||||
max_height: f32,
|
|
||||||
frame: Frame,
|
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
|
||||||
) -> (R, Response) {
|
|
||||||
let mut panel_rect = self.available_rect();
|
|
||||||
panel_rect.max.y = panel_rect.max.y.at_most(panel_rect.min.y + max_height);
|
|
||||||
|
|
||||||
let id = Id::background();
|
|
||||||
let layer_id = LayerId {
|
|
||||||
order: Order::Background,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
|
|
||||||
let clip_rect = self.input.screen_rect();
|
|
||||||
let mut panel_ui = Ui::new(self.clone(), layer_id, id, panel_rect, clip_rect);
|
|
||||||
let r = frame.show(&mut panel_ui, |ui| {
|
|
||||||
ui.set_min_width(ui.max_rect_finite().width()); // fill full width
|
|
||||||
add_contents(ui)
|
|
||||||
});
|
|
||||||
|
|
||||||
let panel_rect = panel_ui.min_rect();
|
|
||||||
let response = panel_ui.interact_hover(panel_rect);
|
|
||||||
|
|
||||||
// Shrink out `available_rect`:
|
|
||||||
let mut remainder = self.available_rect();
|
let mut remainder = self.available_rect();
|
||||||
remainder.min.y = panel_rect.max.y;
|
remainder.min.y = panel_rect.max.y;
|
||||||
*self.available_rect.lock() = Some(remainder);
|
*self.available_rect.lock() = Some(remainder);
|
||||||
|
|
||||||
(r, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{app, demos, Context, Resize, ScrollArea, Ui, Window};
|
use crate::{app, demos, Context, Id, Resize, ScrollArea, Ui, Window};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -63,8 +63,7 @@ impl DemoWindows {
|
||||||
self.previous_link = env.link;
|
self.previous_link = env.link;
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = crate::Frame::panel(ui.style());
|
crate::SidePanel::left(Id::new("side_panel"), 200.0).show(ctx, |ui| {
|
||||||
ctx.panel_left(240.0, frame, |ui| {
|
|
||||||
ui.heading("Egui Demo");
|
ui.heading("Egui Demo");
|
||||||
ui.label("Egui is an immediate mode GUI library written in Rust.");
|
ui.label("Egui is an immediate mode GUI library written in Rust.");
|
||||||
ui.add(crate::Hyperlink::new("https://github.com/emilk/egui").text("Egui home page"));
|
ui.add(crate::Hyperlink::new("https://github.com/emilk/egui").text("Egui home page"));
|
||||||
|
@ -83,7 +82,8 @@ impl DemoWindows {
|
||||||
self.open_windows.ui(ui);
|
self.open_windows.ui(ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ctx.panel_top(0.0, crate::Frame::none(), |ui| {
|
|
||||||
|
crate::TopPanel::top(Id::new("menu_bar")).show(ctx, |ui| {
|
||||||
show_menu_bar(ui, &mut self.open_windows, env.seconds_since_midnight);
|
show_menu_bar(ui, &mut self.open_windows, env.seconds_since_midnight);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,13 @@ impl LayerId {
|
||||||
id: Id::new("debug"),
|
id: Id::new("debug"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn background() -> Self {
|
||||||
|
Self {
|
||||||
|
order: Order::Background,
|
||||||
|
id: Id::background(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A unique identifier of a specific `PaintCmd` in a `PaintList`.
|
/// A unique identifier of a specific `PaintCmd` in a `PaintList`.
|
||||||
|
|
|
@ -37,8 +37,10 @@ impl BarState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The menu bar goes well in `TopPanel`,
|
||||||
|
/// but can also be placed in a `Window`.
|
||||||
|
/// In the latter case you may want to wrap it in `Frame`.
|
||||||
pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Response) {
|
pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Response) {
|
||||||
Frame::menu_bar(ui.style()).show(ui, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let mut style = ui.style().clone();
|
let mut style = ui.style().clone();
|
||||||
style.spacing.button_padding = vec2(2.0, 0.0);
|
style.spacing.button_padding = vec2(2.0, 0.0);
|
||||||
|
@ -56,7 +58,6 @@ pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Respo
|
||||||
|
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a top level menu in a menu bar. This would be e.g. "File", "Edit" etc.
|
/// Construct a top level menu in a menu bar. This would be e.g. "File", "Edit" etc.
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
|
|
||||||
use egui::{Slider, Window};
|
|
||||||
|
|
||||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||||
struct MyApp {
|
struct MyApp {
|
||||||
|
@ -24,13 +22,13 @@ impl egui::app::App for MyApp {
|
||||||
let MyApp { my_string, value } = self;
|
let MyApp { my_string, value } = self;
|
||||||
|
|
||||||
// Example used in `README.md`.
|
// Example used in `README.md`.
|
||||||
Window::new("Debug").show(ui.ctx(), |ui| {
|
egui::Window::new("Debug").show(ui.ctx(), |ui| {
|
||||||
ui.label(format!("Hello, world {}", 123));
|
ui.label(format!("Hello, world {}", 123));
|
||||||
if ui.button("Save").clicked {
|
if ui.button("Save").clicked {
|
||||||
my_save_function();
|
my_save_function();
|
||||||
}
|
}
|
||||||
ui.text_edit(my_string);
|
ui.text_edit(my_string);
|
||||||
ui.add(Slider::f32(value, 0.0..=1.0).text("float"));
|
ui.add(egui::Slider::f32(value, 0.0..=1.0).text("float"));
|
||||||
});
|
});
|
||||||
|
|
||||||
Default::default()
|
Default::default()
|
||||||
|
|
Loading…
Reference in a new issue