Per-side margins with new struct Margin (#1219)

This commit is contained in:
Erlend Walstad 2022-02-07 11:29:16 +01:00 committed by GitHub
parent 603ec82a5e
commit 2802e03526
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 102 additions and 22 deletions

View file

@ -39,6 +39,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
* `TextStyle` is no longer `Copy` ([#1154](https://github.com/emilk/egui/pull/1154)). * `TextStyle` is no longer `Copy` ([#1154](https://github.com/emilk/egui/pull/1154)).
* Replaced `TextEdit::text_style` with `TextEdit::font` ([#1154](https://github.com/emilk/egui/pull/1154)). * Replaced `TextEdit::text_style` with `TextEdit::font` ([#1154](https://github.com/emilk/egui/pull/1154)).
* Replaced `corner_radius: f32` with `rounding: Rounding`, allowing per-corner rounding settings ([#1206](https://github.com/emilk/egui/pull/1206)). * Replaced `corner_radius: f32` with `rounding: Rounding`, allowing per-corner rounding settings ([#1206](https://github.com/emilk/egui/pull/1206)).
* Replaced Frame's `margin: Vec2` with `margin: Margin`, allowing for different margins on opposing sides ([#1219](https://github.com/emilk/egui/pull/1219)).
* `Plot::highlight` now takes a `bool` argument ([#1159](https://github.com/emilk/egui/pull/1159)). * `Plot::highlight` now takes a `bool` argument ([#1159](https://github.com/emilk/egui/pull/1159)).
* `ScrollArea::show` now returns a `ScrollAreaOutput`, so you might need to add `.inner` after the call to it ([#1166](https://github.com/emilk/egui/pull/1166)). * `ScrollArea::show` now returns a `ScrollAreaOutput`, so you might need to add `.inner` after the call to it ([#1166](https://github.com/emilk/egui/pull/1166)).

View file

@ -3,12 +3,55 @@
use crate::{layers::ShapeIdx, *}; use crate::{layers::ShapeIdx, *};
use epaint::*; use epaint::*;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Margin {
pub left: f32,
pub right: f32,
pub top: f32,
pub bottom: f32,
}
impl Margin {
#[inline]
pub fn same(margin: f32) -> Self {
Self {
left: margin,
right: margin,
top: margin,
bottom: margin,
}
}
/// Margins with the same size on opposing sides
#[inline]
pub fn symmetric(x: f32, y: f32) -> Self {
Self {
left: x,
right: x,
top: y,
bottom: y,
}
}
/// Total margins on both sides
pub fn sum(&self) -> Vec2 {
Vec2::new(self.left + self.right, self.top + self.bottom)
}
}
impl From<Vec2> for Margin {
fn from(v: Vec2) -> Self {
Self::symmetric(v.x, v.y)
}
}
/// Color and margin of a rectangular background of a [`Ui`]. /// Color and margin of a rectangular background of a [`Ui`].
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
#[must_use = "You should call .show()"] #[must_use = "You should call .show()"]
pub struct Frame { pub struct Frame {
/// On each side /// On each side
pub margin: Vec2, pub margin: Margin,
pub rounding: Rounding, pub rounding: Rounding,
pub shadow: Shadow, pub shadow: Shadow,
pub fill: Color32, pub fill: Color32,
@ -23,7 +66,7 @@ impl Frame {
/// For when you want to group a few widgets together within a frame. /// For when you want to group a few widgets together within a frame.
pub fn group(style: &Style) -> Self { pub fn group(style: &Style) -> Self {
Self { Self {
margin: Vec2::splat(6.0), // symmetric looks best in corners when nesting margin: Margin::same(6.0), // symmetric looks best in corners when nesting
rounding: style.visuals.widgets.noninteractive.rounding, rounding: style.visuals.widgets.noninteractive.rounding,
stroke: style.visuals.widgets.noninteractive.bg_stroke, stroke: style.visuals.widgets.noninteractive.bg_stroke,
..Default::default() ..Default::default()
@ -32,7 +75,7 @@ impl Frame {
pub(crate) fn side_top_panel(style: &Style) -> Self { pub(crate) fn side_top_panel(style: &Style) -> Self {
Self { Self {
margin: Vec2::new(8.0, 2.0), margin: Margin::symmetric(8.0, 2.0),
rounding: Rounding::none(), rounding: Rounding::none(),
fill: style.visuals.window_fill(), fill: style.visuals.window_fill(),
stroke: style.visuals.window_stroke(), stroke: style.visuals.window_stroke(),
@ -42,7 +85,7 @@ impl Frame {
pub(crate) fn central_panel(style: &Style) -> Self { pub(crate) fn central_panel(style: &Style) -> Self {
Self { Self {
margin: Vec2::new(8.0, 8.0), margin: Margin::symmetric(8.0, 8.0),
rounding: Rounding::none(), rounding: Rounding::none(),
fill: style.visuals.window_fill(), fill: style.visuals.window_fill(),
stroke: Default::default(), stroke: Default::default(),
@ -52,7 +95,7 @@ impl Frame {
pub fn window(style: &Style) -> Self { pub fn window(style: &Style) -> Self {
Self { Self {
margin: style.spacing.window_padding, margin: style.spacing.window_margin,
rounding: style.visuals.window_rounding, rounding: style.visuals.window_rounding,
shadow: style.visuals.window_shadow, shadow: style.visuals.window_shadow,
fill: style.visuals.window_fill(), fill: style.visuals.window_fill(),
@ -62,7 +105,7 @@ impl Frame {
pub fn menu(style: &Style) -> Self { pub fn menu(style: &Style) -> Self {
Self { Self {
margin: Vec2::splat(1.0), margin: Margin::same(1.0),
rounding: style.visuals.widgets.noninteractive.rounding, rounding: style.visuals.widgets.noninteractive.rounding,
shadow: style.visuals.popup_shadow, shadow: style.visuals.popup_shadow,
fill: style.visuals.window_fill(), fill: style.visuals.window_fill(),
@ -72,7 +115,7 @@ impl Frame {
pub fn popup(style: &Style) -> Self { pub fn popup(style: &Style) -> Self {
Self { Self {
margin: style.spacing.window_padding, margin: style.spacing.window_margin,
rounding: style.visuals.widgets.noninteractive.rounding, rounding: style.visuals.widgets.noninteractive.rounding,
shadow: style.visuals.popup_shadow, shadow: style.visuals.popup_shadow,
fill: style.visuals.window_fill(), fill: style.visuals.window_fill(),
@ -83,7 +126,7 @@ impl Frame {
/// dark canvas to draw on /// dark canvas to draw on
pub fn dark_canvas(style: &Style) -> Self { pub fn dark_canvas(style: &Style) -> Self {
Self { Self {
margin: Vec2::new(10.0, 10.0), margin: Margin::symmetric(10.0, 10.0),
rounding: style.visuals.widgets.noninteractive.rounding, rounding: style.visuals.widgets.noninteractive.rounding,
fill: Color32::from_black_alpha(250), fill: Color32::from_black_alpha(250),
stroke: style.visuals.window_stroke(), stroke: style.visuals.window_stroke(),
@ -109,7 +152,7 @@ impl Frame {
} }
/// Margin on each side of the frame. /// Margin on each side of the frame.
pub fn margin(mut self, margin: impl Into<Vec2>) -> Self { pub fn margin(mut self, margin: impl Into<Margin>) -> Self {
self.margin = margin.into(); self.margin = margin.into();
self self
} }
@ -137,7 +180,10 @@ impl Frame {
pub fn begin(self, ui: &mut Ui) -> Prepared { pub fn begin(self, ui: &mut Ui) -> Prepared {
let where_to_put_background = ui.painter().add(Shape::Noop); let where_to_put_background = ui.painter().add(Shape::Noop);
let outer_rect_bounds = ui.available_rect_before_wrap(); let outer_rect_bounds = ui.available_rect_before_wrap();
let mut inner_rect = outer_rect_bounds.shrink2(self.margin);
let mut inner_rect = outer_rect_bounds;
inner_rect.min += Vec2::new(self.margin.left, self.margin.top);
inner_rect.max -= Vec2::new(self.margin.right, self.margin.bottom);
// Make sure we don't shrink to the negative: // Make sure we don't shrink to the negative:
inner_rect.max.x = inner_rect.max.x.max(inner_rect.min.x); inner_rect.max.x = inner_rect.max.x.max(inner_rect.min.x);
@ -197,7 +243,10 @@ impl Frame {
impl Prepared { impl Prepared {
pub fn outer_rect(&self) -> Rect { pub fn outer_rect(&self) -> Rect {
self.content_ui.min_rect().expand2(self.frame.margin) let mut rect = self.content_ui.min_rect();
rect.min -= Vec2::new(self.frame.margin.left, self.frame.margin.top);
rect.max += Vec2::new(self.frame.margin.right, self.frame.margin.bottom);
rect
} }
pub fn end(self, ui: &mut Ui) -> Response { pub fn end(self, ui: &mut Ui) -> Response {

View file

@ -16,7 +16,7 @@ pub use {
area::Area, area::Area,
collapsing_header::{CollapsingHeader, CollapsingResponse}, collapsing_header::{CollapsingHeader, CollapsingResponse},
combo_box::*, combo_box::*,
frame::Frame, frame::{Frame, Margin},
panel::{CentralPanel, SidePanel, TopBottomPanel}, panel::{CentralPanel, SidePanel, TopBottomPanel},
popup::*, popup::*,
resize::Resize, resize::Resize,

View file

@ -302,7 +302,7 @@ pub fn popup_below_widget<R>(
frame frame
.show(ui, |ui| { .show(ui, |ui| {
ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| { ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| {
ui.set_width(widget_response.rect.width() - 2.0 * frame_margin.x); ui.set_width(widget_response.rect.width() - frame_margin.sum().x);
add_contents(ui) add_contents(ui)
}) })
.inner .inner

View file

@ -178,7 +178,7 @@ impl Resize {
.at_least(self.min_size) .at_least(self.min_size)
.at_most(self.max_size) .at_most(self.max_size)
.at_most( .at_most(
ui.input().screen_rect().size() - 2.0 * ui.spacing().window_padding, // hack for windows ui.input().screen_rect().size() - ui.spacing().window_margin.sum(), // hack for windows
); );
State { State {

View file

@ -301,7 +301,7 @@ impl<'open> Window<'open> {
} else { } else {
0.0 0.0
}; };
let margins = 2.0 * frame.margin + vec2(0.0, title_bar_height); let margins = frame.margin.sum() + vec2(0.0, title_bar_height);
interact( interact(
window_interaction, window_interaction,

View file

@ -2,7 +2,7 @@
#![allow(clippy::if_same_then_else)] #![allow(clippy::if_same_then_else)]
use crate::{color::*, emath::*, FontFamily, FontId, Response, RichText, WidgetText}; use crate::{color::*, emath::*, FontFamily, FontId, Margin, Response, RichText, WidgetText};
use epaint::{mutex::Arc, Rounding, Shadow, Stroke}; use epaint::{mutex::Arc, Rounding, Shadow, Stroke};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -220,8 +220,8 @@ pub struct Spacing {
/// widgets `A` and `B` you need to change `item_spacing` before adding `A`. /// widgets `A` and `B` you need to change `item_spacing` before adding `A`.
pub item_spacing: Vec2, pub item_spacing: Vec2,
/// Horizontal and vertical padding within a window frame. /// Horizontal and vertical margins within a window frame.
pub window_padding: Vec2, pub window_margin: Margin,
/// Button size is text size plus this on each side /// Button size is text size plus this on each side
pub button_padding: Vec2, pub button_padding: Vec2,
@ -528,7 +528,7 @@ impl Default for Spacing {
fn default() -> Self { fn default() -> Self {
Self { Self {
item_spacing: vec2(8.0, 3.0), item_spacing: vec2(8.0, 3.0),
window_padding: Vec2::splat(6.0), window_margin: Margin::same(6.0),
button_padding: vec2(4.0, 1.0), button_padding: vec2(4.0, 1.0),
indent: 18.0, // match checkbox/radio-button with `button_padding.x + icon_width + icon_spacing` indent: 18.0, // match checkbox/radio-button with `button_padding.x + icon_width + icon_spacing`
interact_size: vec2(40.0, 18.0), interact_size: vec2(40.0, 18.0),
@ -803,7 +803,7 @@ impl Spacing {
pub fn ui(&mut self, ui: &mut crate::Ui) { pub fn ui(&mut self, ui: &mut crate::Ui) {
let Self { let Self {
item_spacing, item_spacing,
window_padding, window_margin,
button_padding, button_padding,
indent, indent,
interact_size, interact_size,
@ -818,7 +818,37 @@ impl Spacing {
} = self; } = self;
ui.add(slider_vec2(item_spacing, 0.0..=20.0, "Item spacing")); ui.add(slider_vec2(item_spacing, 0.0..=20.0, "Item spacing"));
ui.add(slider_vec2(window_padding, 0.0..=20.0, "Window padding"));
let margin_range = 0.0..=20.0;
ui.horizontal(|ui| {
ui.add(
DragValue::new(&mut window_margin.left)
.clamp_range(margin_range.clone())
.prefix("left: "),
);
ui.add(
DragValue::new(&mut window_margin.right)
.clamp_range(margin_range.clone())
.prefix("right: "),
);
ui.label("Window margins x");
});
ui.horizontal(|ui| {
ui.add(
DragValue::new(&mut window_margin.top)
.clamp_range(margin_range.clone())
.prefix("top: "),
);
ui.add(
DragValue::new(&mut window_margin.bottom)
.clamp_range(margin_range)
.prefix("bottom: "),
);
ui.label("Window margins y");
});
ui.add(slider_vec2(button_padding, 0.0..=20.0, "Button padding")); ui.add(slider_vec2(button_padding, 0.0..=20.0, "Button padding"));
ui.add(slider_vec2(interact_size, 4.0..=60.0, "Interact size")) ui.add(slider_vec2(interact_size, 4.0..=60.0, "Interact size"))
.on_hover_text("Minimum size of an interactive widget"); .on_hover_text("Minimum size of an interactive widget");

View file

@ -239,7 +239,7 @@ impl Widget for &mut LegendWidget {
legend_ui legend_ui
.scope(|ui| { .scope(|ui| {
let background_frame = Frame { let background_frame = Frame {
margin: vec2(8.0, 4.0), margin: vec2(8.0, 4.0).into(),
rounding: ui.style().visuals.window_rounding, rounding: ui.style().visuals.window_rounding,
shadow: epaint::Shadow::default(), shadow: epaint::Shadow::default(),
fill: ui.style().visuals.extreme_bg_color, fill: ui.style().visuals.extreme_bg_color,