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`
|
||||
* Demo App: Add slider to scale all of Egui
|
||||
* 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`).
|
||||
|
||||
## 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 {
|
||||
margin: Vec2::splat(1.0),
|
||||
margin: Vec2::new(8.0, 2.0),
|
||||
corner_radius: 0.0,
|
||||
fill: style.visuals.widgets.noninteractive.bg_fill,
|
||||
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 {
|
||||
self.fill = fill;
|
||||
self
|
||||
|
|
|
@ -6,12 +6,20 @@ pub(crate) mod area;
|
|||
pub(crate) mod collapsing_header;
|
||||
mod combo_box;
|
||||
pub(crate) mod frame;
|
||||
pub(crate) mod panel;
|
||||
pub(crate) mod popup;
|
||||
pub(crate) mod resize;
|
||||
pub(crate) mod scroll_area;
|
||||
pub(crate) mod window;
|
||||
|
||||
pub use {
|
||||
area::Area, collapsing_header::*, combo_box::*, frame::Frame, popup::*, resize::Resize,
|
||||
scroll_area::ScrollArea, window::Window,
|
||||
area::Area,
|
||||
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.
|
||||
/// The given `max_width` 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_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`:
|
||||
/// Shrink `available_rect()`.
|
||||
pub(crate) fn allocate_left_panel(&self, panel_rect: Rect) {
|
||||
let mut remainder = self.available_rect();
|
||||
remainder.min.x = panel_rect.max.x;
|
||||
*self.available_rect.lock() = Some(remainder);
|
||||
|
||||
(r, response)
|
||||
}
|
||||
|
||||
/// Create a panel that covers the entire top side of the screen.
|
||||
/// This can be useful to add a menu bar to the whole window.
|
||||
/// 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`:
|
||||
/// Shrink `available_rect()`.
|
||||
pub(crate) fn allocate_top_panel(&self, panel_rect: Rect) {
|
||||
let mut remainder = self.available_rect();
|
||||
remainder.min.y = panel_rect.max.y;
|
||||
*self.available_rect.lock() = Some(remainder);
|
||||
|
||||
(r, response)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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;
|
||||
}
|
||||
|
||||
let frame = crate::Frame::panel(ui.style());
|
||||
ctx.panel_left(240.0, frame, |ui| {
|
||||
crate::SidePanel::left(Id::new("side_panel"), 200.0).show(ctx, |ui| {
|
||||
ui.heading("Egui Demo");
|
||||
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"));
|
||||
|
@ -83,7 +82,8 @@ impl DemoWindows {
|
|||
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);
|
||||
});
|
||||
|
||||
|
|
|
@ -34,6 +34,13 @@ impl LayerId {
|
|||
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`.
|
||||
|
|
|
@ -37,25 +37,26 @@ 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) {
|
||||
Frame::menu_bar(ui.style()).show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let mut style = ui.style().clone();
|
||||
style.spacing.button_padding = vec2(2.0, 0.0);
|
||||
// style.visuals.widgets.active.bg_fill = TRANSPARENT;
|
||||
style.visuals.widgets.active.bg_stroke = Stroke::none();
|
||||
// style.visuals.widgets.hovered.bg_fill = TRANSPARENT;
|
||||
style.visuals.widgets.hovered.bg_stroke = Stroke::none();
|
||||
style.visuals.widgets.inactive.bg_fill = TRANSPARENT;
|
||||
style.visuals.widgets.inactive.bg_stroke = Stroke::none();
|
||||
ui.set_style(style);
|
||||
ui.horizontal(|ui| {
|
||||
let mut style = ui.style().clone();
|
||||
style.spacing.button_padding = vec2(2.0, 0.0);
|
||||
// style.visuals.widgets.active.bg_fill = TRANSPARENT;
|
||||
style.visuals.widgets.active.bg_stroke = Stroke::none();
|
||||
// style.visuals.widgets.hovered.bg_fill = TRANSPARENT;
|
||||
style.visuals.widgets.hovered.bg_stroke = Stroke::none();
|
||||
style.visuals.widgets.inactive.bg_fill = TRANSPARENT;
|
||||
style.visuals.widgets.inactive.bg_stroke = Stroke::none();
|
||||
ui.set_style(style);
|
||||
|
||||
// Take full width and fixed height:
|
||||
let height = ui.style().spacing.interact_size.y;
|
||||
ui.set_min_size(vec2(ui.available().width(), height));
|
||||
// Take full width and fixed height:
|
||||
let height = ui.style().spacing.interact_size.y;
|
||||
ui.set_min_size(vec2(ui.available().width(), height));
|
||||
|
||||
add_contents(ui)
|
||||
})
|
||||
add_contents(ui)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#![deny(warnings)]
|
||||
#![warn(clippy::all)]
|
||||
|
||||
use egui::{Slider, Window};
|
||||
|
||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||
struct MyApp {
|
||||
|
@ -24,13 +22,13 @@ impl egui::app::App for MyApp {
|
|||
let MyApp { my_string, value } = self;
|
||||
|
||||
// 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));
|
||||
if ui.button("Save").clicked {
|
||||
my_save_function();
|
||||
}
|
||||
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()
|
||||
|
|
Loading…
Reference in a new issue