⚠️ `Frame` now includes stroke width as part of padding (#5575)
* Part of https://github.com/emilk/egui/issues/4019 `Frame` now includes the width of the stroke as part of its size. From the new docs: ### `Frame` docs The total (outer) size of a frame is `content_size + inner_margin + 2*stroke.width + outer_margin`. Everything within the stroke is filled with the fill color (if any). ```text +-----------------^-------------------------------------- -+ | | outer_margin | | +------------v----^------------------------------+ | | | | stroke width | | | | +------------v---^---------------------+ | | | | | | inner_margin | | | | | | +-----------v----------------+ | | | | | | | ^ | | | | | | | | | | | | | | | | |<------ content_size ------>| | | | | | | | | | | | | | | | | v | | | | | | | +------- content_rect -------+ | | | | | | | | | | | +-------------fill_rect ---------------+ | | | | | | | +----------------- widget_rect ------------------+ | | | +---------------------- outer_rect ------------------------+ ``` The four rectangles, from inside to outside, are: * `content_rect`: the rectangle that is made available to the inner [`Ui`] or widget. * `fill_rect`: the rectangle that is filled with the fill color (inside the stroke, if any). * `widget_rect`: is the interactive part of the widget (what sense clicks etc). * `outer_rect`: what is allocated in the outer [`Ui`], and is what is returned by [`Response::rect`]. ### Notes This required rewriting a lot of the layout code for `egui::Window`, which was a massive pain. But now the window margin and stroke width is properly accounted for everywhere.
This commit is contained in:
parent
938d8b0d2e
commit
6607cd95f9
|
|
@ -6,7 +6,43 @@ use crate::{
|
|||
};
|
||||
use epaint::{Color32, Margin, Marginf, Rect, Rounding, Shadow, Shape, Stroke};
|
||||
|
||||
/// Add a background, frame and/or margin to a rectangular background of a [`Ui`].
|
||||
/// A frame around some content, including margin, colors, etc.
|
||||
///
|
||||
/// ## Definitions
|
||||
/// The total (outer) size of a frame is
|
||||
/// `content_size + inner_margin + 2 * stroke.width + outer_margin`.
|
||||
///
|
||||
/// Everything within the stroke is filled with the fill color (if any).
|
||||
///
|
||||
/// ```text
|
||||
/// +-----------------^-------------------------------------- -+
|
||||
/// | | outer_margin |
|
||||
/// | +------------v----^------------------------------+ |
|
||||
/// | | | stroke width | |
|
||||
/// | | +------------v---^---------------------+ | |
|
||||
/// | | | | inner_margin | | |
|
||||
/// | | | +-----------v----------------+ | | |
|
||||
/// | | | | ^ | | | |
|
||||
/// | | | | | | | | |
|
||||
/// | | | |<------ content_size ------>| | | |
|
||||
/// | | | | | | | | |
|
||||
/// | | | | v | | | |
|
||||
/// | | | +------- content_rect -------+ | | |
|
||||
/// | | | | | |
|
||||
/// | | +-------------fill_rect ---------------+ | |
|
||||
/// | | | |
|
||||
/// | +----------------- widget_rect ------------------+ |
|
||||
/// | |
|
||||
/// +---------------------- outer_rect ------------------------+
|
||||
/// ```
|
||||
///
|
||||
/// The four rectangles, from inside to outside, are:
|
||||
/// * `content_rect`: the rectangle that is made available to the inner [`Ui`] or widget.
|
||||
/// * `fill_rect`: the rectangle that is filled with the fill color (inside the stroke, if any).
|
||||
/// * `widget_rect`: is the interactive part of the widget (what sense clicks etc).
|
||||
/// * `outer_rect`: what is allocated in the outer [`Ui`], and is what is returned by [`Response::rect`].
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ```
|
||||
/// # egui::__run_test_ui(|ui| {
|
||||
|
|
@ -58,19 +94,47 @@ use epaint::{Color32, Margin, Marginf, Rect, Rounding, Shadow, Shape, Stroke};
|
|||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[must_use = "You should call .show()"]
|
||||
pub struct Frame {
|
||||
// Fields are ordered inside-out.
|
||||
// TODO(emilk): add `min_content_size: Vec2`
|
||||
//
|
||||
/// Margin within the painted frame.
|
||||
///
|
||||
/// Known as `padding` in CSS.
|
||||
#[doc(alias = "padding")]
|
||||
pub inner_margin: Margin,
|
||||
|
||||
/// Margin outside the painted frame.
|
||||
pub outer_margin: Margin,
|
||||
|
||||
pub rounding: Rounding,
|
||||
|
||||
pub shadow: Shadow,
|
||||
|
||||
/// The background fill color of the frame, within the [`Self::stroke`].
|
||||
///
|
||||
/// Known as `background` in CSS.
|
||||
#[doc(alias = "background")]
|
||||
pub fill: Color32,
|
||||
|
||||
/// The width and color of the outline around the frame.
|
||||
///
|
||||
/// The width of the stroke is part of the total margin/padding of the frame.
|
||||
#[doc(alias = "border")]
|
||||
pub stroke: Stroke,
|
||||
|
||||
/// The rounding of the corners of [`Self::stroke`] and [`Self::fill`].
|
||||
pub rounding: Rounding,
|
||||
|
||||
/// Margin outside the painted frame.
|
||||
///
|
||||
/// Similar to what is called `margin` in CSS.
|
||||
/// However, egui does NOT do "Margin Collapse" like in CSS,
|
||||
/// i.e. when placing two frames next to each other,
|
||||
/// the distance between their borders is the SUM
|
||||
/// of their other margins.
|
||||
/// In CSS the distance would be the MAX of their outer margins.
|
||||
/// Supporting margin collapse is difficult, and would
|
||||
/// requires complicating the already complicated egui layout code.
|
||||
///
|
||||
/// Consider using [`crate::Spacing::item_spacing`]
|
||||
/// for adding space between widgets.
|
||||
pub outer_margin: Margin,
|
||||
|
||||
/// Optional drop-shadow behind the frame.
|
||||
pub shadow: Shadow,
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -85,68 +149,72 @@ fn frame_size() {
|
|||
);
|
||||
}
|
||||
|
||||
/// ## Constructors
|
||||
impl Frame {
|
||||
pub fn none() -> Self {
|
||||
Self::default()
|
||||
/// No colors, no margins, no border.
|
||||
///
|
||||
/// This is also the default.
|
||||
pub const NONE: Self = Self {
|
||||
inner_margin: Margin::ZERO,
|
||||
stroke: Stroke::NONE,
|
||||
fill: Color32::TRANSPARENT,
|
||||
rounding: Rounding::ZERO,
|
||||
outer_margin: Margin::ZERO,
|
||||
shadow: Shadow::NONE,
|
||||
};
|
||||
|
||||
pub const fn new() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
|
||||
#[deprecated = "Use `Frame::NONE` or `Frame::new()` instead."]
|
||||
pub const fn none() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
|
||||
/// For when you want to group a few widgets together within a frame.
|
||||
pub fn group(style: &Style) -> Self {
|
||||
Self {
|
||||
inner_margin: Margin::same(6), // same and symmetric looks best in corners when nesting groups
|
||||
rounding: style.visuals.widgets.noninteractive.rounding,
|
||||
stroke: style.visuals.widgets.noninteractive.bg_stroke,
|
||||
..Default::default()
|
||||
}
|
||||
Self::new()
|
||||
.inner_margin(6)
|
||||
.rounding(style.visuals.widgets.noninteractive.rounding)
|
||||
.stroke(style.visuals.widgets.noninteractive.bg_stroke)
|
||||
}
|
||||
|
||||
pub fn side_top_panel(style: &Style) -> Self {
|
||||
Self {
|
||||
inner_margin: Margin::symmetric(8, 2),
|
||||
fill: style.visuals.panel_fill,
|
||||
..Default::default()
|
||||
}
|
||||
Self::new()
|
||||
.inner_margin(Margin::symmetric(8, 2))
|
||||
.fill(style.visuals.panel_fill)
|
||||
}
|
||||
|
||||
pub fn central_panel(style: &Style) -> Self {
|
||||
Self {
|
||||
inner_margin: Margin::same(8),
|
||||
fill: style.visuals.panel_fill,
|
||||
..Default::default()
|
||||
}
|
||||
Self::new().inner_margin(8).fill(style.visuals.panel_fill)
|
||||
}
|
||||
|
||||
pub fn window(style: &Style) -> Self {
|
||||
Self {
|
||||
inner_margin: style.spacing.window_margin,
|
||||
rounding: style.visuals.window_rounding,
|
||||
shadow: style.visuals.window_shadow,
|
||||
fill: style.visuals.window_fill(),
|
||||
stroke: style.visuals.window_stroke(),
|
||||
..Default::default()
|
||||
}
|
||||
Self::new()
|
||||
.inner_margin(style.spacing.window_margin)
|
||||
.rounding(style.visuals.window_rounding)
|
||||
.shadow(style.visuals.window_shadow)
|
||||
.fill(style.visuals.window_fill())
|
||||
.stroke(style.visuals.window_stroke())
|
||||
}
|
||||
|
||||
pub fn menu(style: &Style) -> Self {
|
||||
Self {
|
||||
inner_margin: style.spacing.menu_margin,
|
||||
rounding: style.visuals.menu_rounding,
|
||||
shadow: style.visuals.popup_shadow,
|
||||
fill: style.visuals.window_fill(),
|
||||
stroke: style.visuals.window_stroke(),
|
||||
..Default::default()
|
||||
}
|
||||
Self::new()
|
||||
.inner_margin(style.spacing.menu_margin)
|
||||
.rounding(style.visuals.menu_rounding)
|
||||
.shadow(style.visuals.popup_shadow)
|
||||
.fill(style.visuals.window_fill())
|
||||
.stroke(style.visuals.window_stroke())
|
||||
}
|
||||
|
||||
pub fn popup(style: &Style) -> Self {
|
||||
Self {
|
||||
inner_margin: style.spacing.menu_margin,
|
||||
rounding: style.visuals.menu_rounding,
|
||||
shadow: style.visuals.popup_shadow,
|
||||
fill: style.visuals.window_fill(),
|
||||
stroke: style.visuals.window_stroke(),
|
||||
..Default::default()
|
||||
}
|
||||
Self::new()
|
||||
.inner_margin(style.spacing.menu_margin)
|
||||
.rounding(style.visuals.menu_rounding)
|
||||
.shadow(style.visuals.popup_shadow)
|
||||
.fill(style.visuals.window_fill())
|
||||
.stroke(style.visuals.window_stroke())
|
||||
}
|
||||
|
||||
/// A canvas to draw on.
|
||||
|
|
@ -154,57 +222,77 @@ impl Frame {
|
|||
/// In bright mode this will be very bright,
|
||||
/// and in dark mode this will be very dark.
|
||||
pub fn canvas(style: &Style) -> Self {
|
||||
Self {
|
||||
inner_margin: Margin::same(2),
|
||||
rounding: style.visuals.widgets.noninteractive.rounding,
|
||||
fill: style.visuals.extreme_bg_color,
|
||||
stroke: style.visuals.window_stroke(),
|
||||
..Default::default()
|
||||
}
|
||||
Self::new()
|
||||
.inner_margin(2)
|
||||
.rounding(style.visuals.widgets.noninteractive.rounding)
|
||||
.fill(style.visuals.extreme_bg_color)
|
||||
.stroke(style.visuals.window_stroke())
|
||||
}
|
||||
|
||||
/// A dark canvas to draw on.
|
||||
pub fn dark_canvas(style: &Style) -> Self {
|
||||
Self {
|
||||
fill: Color32::from_black_alpha(250),
|
||||
..Self::canvas(style)
|
||||
}
|
||||
Self::canvas(style).fill(Color32::from_black_alpha(250))
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Builders
|
||||
impl Frame {
|
||||
#[inline]
|
||||
pub fn fill(mut self, fill: Color32) -> Self {
|
||||
self.fill = fill;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn stroke(mut self, stroke: impl Into<Stroke>) -> Self {
|
||||
self.stroke = stroke.into();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rounding(mut self, rounding: impl Into<Rounding>) -> Self {
|
||||
self.rounding = rounding.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Margin within the painted frame.
|
||||
///
|
||||
/// Known as `padding` in CSS.
|
||||
#[doc(alias = "padding")]
|
||||
#[inline]
|
||||
pub fn inner_margin(mut self, inner_margin: impl Into<Margin>) -> Self {
|
||||
self.inner_margin = inner_margin.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// The background fill color of the frame, within the [`Self::stroke`].
|
||||
///
|
||||
/// Known as `background` in CSS.
|
||||
#[doc(alias = "background")]
|
||||
#[inline]
|
||||
pub fn fill(mut self, fill: Color32) -> Self {
|
||||
self.fill = fill;
|
||||
self
|
||||
}
|
||||
|
||||
/// The width and color of the outline around the frame.
|
||||
///
|
||||
/// The width of the stroke is part of the total margin/padding of the frame.
|
||||
#[inline]
|
||||
pub fn stroke(mut self, stroke: impl Into<Stroke>) -> Self {
|
||||
self.stroke = stroke.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// The rounding of the corners of [`Self::stroke`] and [`Self::fill`].
|
||||
#[inline]
|
||||
pub fn rounding(mut self, rounding: impl Into<Rounding>) -> Self {
|
||||
self.rounding = rounding.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Margin outside the painted frame.
|
||||
///
|
||||
/// Similar to what is called `margin` in CSS.
|
||||
/// However, egui does NOT do "Margin Collapse" like in CSS,
|
||||
/// i.e. when placing two frames next to each other,
|
||||
/// the distance between their borders is the SUM
|
||||
/// of their other margins.
|
||||
/// In CSS the distance would be the MAX of their outer margins.
|
||||
/// Supporting margin collapse is difficult, and would
|
||||
/// requires complicating the already complicated egui layout code.
|
||||
///
|
||||
/// Consider using [`crate::Spacing::item_spacing`]
|
||||
/// for adding space between widgets.
|
||||
#[inline]
|
||||
pub fn outer_margin(mut self, outer_margin: impl Into<Margin>) -> Self {
|
||||
self.outer_margin = outer_margin.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Optional drop-shadow behind the frame.
|
||||
#[inline]
|
||||
pub fn shadow(mut self, shadow: Shadow) -> Self {
|
||||
self.shadow = shadow;
|
||||
|
|
@ -224,11 +312,37 @@ impl Frame {
|
|||
}
|
||||
}
|
||||
|
||||
/// ## Inspectors
|
||||
impl Frame {
|
||||
/// Inner margin plus outer margin.
|
||||
/// How much extra space the frame uses up compared to the content.
|
||||
///
|
||||
/// [`Self::inner_margin`] + [`Self.stroke`]`.width` + [`Self::outer_margin`].
|
||||
#[inline]
|
||||
pub fn total_margin(&self) -> Marginf {
|
||||
Marginf::from(self.inner_margin) + Marginf::from(self.outer_margin)
|
||||
Marginf::from(self.inner_margin)
|
||||
+ Marginf::from(self.stroke.width)
|
||||
+ Marginf::from(self.outer_margin)
|
||||
}
|
||||
|
||||
/// Calculate the `fill_rect` from the `content_rect`.
|
||||
///
|
||||
/// This is the rectangle that is filled with the fill color (inside the stroke, if any).
|
||||
pub fn fill_rect(&self, content_rect: Rect) -> Rect {
|
||||
content_rect + self.inner_margin
|
||||
}
|
||||
|
||||
/// Calculate the `widget_rect` from the `content_rect`.
|
||||
///
|
||||
/// This is the visible and interactive rectangle.
|
||||
pub fn widget_rect(&self, content_rect: Rect) -> Rect {
|
||||
content_rect + self.inner_margin + Marginf::from(self.stroke.width)
|
||||
}
|
||||
|
||||
/// Calculate the `outer_rect` from the `content_rect`.
|
||||
///
|
||||
/// This is what is allocated in the outer [`Ui`], and is what is returned by [`Response::rect`].
|
||||
pub fn outer_rect(&self, content_rect: Rect) -> Rect {
|
||||
content_rect + self.inner_margin + Marginf::from(self.stroke.width) + self.outer_margin
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -259,20 +373,18 @@ impl Frame {
|
|||
let where_to_put_background = ui.painter().add(Shape::Noop);
|
||||
let outer_rect_bounds = ui.available_rect_before_wrap();
|
||||
|
||||
let mut inner_rect = outer_rect_bounds - self.outer_margin - self.inner_margin;
|
||||
let mut max_content_rect = outer_rect_bounds - self.total_margin();
|
||||
|
||||
// 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.y = inner_rect.max.y.max(inner_rect.min.y);
|
||||
max_content_rect.max.x = max_content_rect.max.x.max(max_content_rect.min.x);
|
||||
max_content_rect.max.y = max_content_rect.max.y.max(max_content_rect.min.y);
|
||||
|
||||
let content_ui = ui.new_child(
|
||||
UiBuilder::new()
|
||||
.ui_stack_info(UiStackInfo::new(UiKind::Frame).with_frame(self))
|
||||
.max_rect(inner_rect),
|
||||
.max_rect(max_content_rect),
|
||||
);
|
||||
|
||||
// content_ui.set_clip_rect(outer_rect_bounds.shrink(self.stroke.width * 0.5)); // Can't do this since we don't know final size yet
|
||||
|
||||
Prepared {
|
||||
frame: self,
|
||||
where_to_put_background,
|
||||
|
|
@ -298,32 +410,37 @@ impl Frame {
|
|||
}
|
||||
|
||||
/// Paint this frame as a shape.
|
||||
///
|
||||
/// The margin is ignored.
|
||||
pub fn paint(&self, outer_rect: Rect) -> Shape {
|
||||
pub fn paint(&self, content_rect: Rect) -> Shape {
|
||||
let Self {
|
||||
inner_margin: _,
|
||||
outer_margin: _,
|
||||
rounding,
|
||||
shadow,
|
||||
fill,
|
||||
stroke,
|
||||
rounding,
|
||||
outer_margin: _,
|
||||
shadow,
|
||||
} = *self;
|
||||
|
||||
let frame_shape = Shape::Rect(epaint::RectShape::new(outer_rect, rounding, fill, stroke));
|
||||
let fill_rect = self.fill_rect(content_rect);
|
||||
let widget_rect = self.widget_rect(content_rect);
|
||||
|
||||
let frame_shape = Shape::Rect(epaint::RectShape::new(fill_rect, rounding, fill, stroke));
|
||||
|
||||
if shadow == Default::default() {
|
||||
frame_shape
|
||||
} else {
|
||||
let shadow = shadow.as_shape(outer_rect, rounding);
|
||||
let shadow = shadow.as_shape(widget_rect, rounding);
|
||||
Shape::Vec(vec![Shape::from(shadow), frame_shape])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Prepared {
|
||||
fn content_with_margin(&self) -> Rect {
|
||||
self.content_ui.min_rect() + self.frame.inner_margin + self.frame.outer_margin
|
||||
fn outer_rect(&self) -> Rect {
|
||||
let content_rect = self.content_ui.min_rect();
|
||||
content_rect
|
||||
+ self.frame.inner_margin
|
||||
+ Marginf::from(self.frame.stroke.width)
|
||||
+ self.frame.outer_margin
|
||||
}
|
||||
|
||||
/// Allocate the space that was used by [`Self::content_ui`].
|
||||
|
|
@ -332,22 +449,25 @@ impl Prepared {
|
|||
///
|
||||
/// This can be called before or after [`Self::paint`].
|
||||
pub fn allocate_space(&self, ui: &mut Ui) -> Response {
|
||||
ui.allocate_rect(self.content_with_margin(), Sense::hover())
|
||||
ui.allocate_rect(self.outer_rect(), Sense::hover())
|
||||
}
|
||||
|
||||
/// Paint the frame.
|
||||
///
|
||||
/// This can be called before or after [`Self::allocate_space`].
|
||||
pub fn paint(&self, ui: &Ui) {
|
||||
let paint_rect = self.content_ui.min_rect() + self.frame.inner_margin;
|
||||
let content_rect = self.content_ui.min_rect();
|
||||
let widget_rect = self.frame.widget_rect(content_rect);
|
||||
|
||||
if ui.is_rect_visible(paint_rect) {
|
||||
let shape = self.frame.paint(paint_rect);
|
||||
if ui.is_rect_visible(widget_rect) {
|
||||
let shape = self.frame.paint(content_rect);
|
||||
ui.painter().set(self.where_to_put_background, shape);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience for calling [`Self::allocate_space`] and [`Self::paint`].
|
||||
///
|
||||
/// Returns the outer rect, i.e. including the outer margin.
|
||||
pub fn end(self, ui: &mut Ui) -> Response {
|
||||
self.paint(ui);
|
||||
self.allocate_space(ui)
|
||||
|
|
|
|||
|
|
@ -2,15 +2,11 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::collapsing_header::CollapsingState;
|
||||
use crate::{
|
||||
Align, Align2, Context, CursorIcon, Id, InnerResponse, LayerId, NumExt, Order, Response, Sense,
|
||||
TextStyle, Ui, UiKind, Vec2b, WidgetInfo, WidgetRect, WidgetText, WidgetType,
|
||||
};
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::{
|
||||
emath, pos2, vec2, Galley, Pos2, Rect, RectShape, Rounding, Roundingf, Shape, Stroke, Vec2,
|
||||
};
|
||||
use epaint::{RectShape, Roundingf};
|
||||
|
||||
use crate::collapsing_header::CollapsingState;
|
||||
use crate::*;
|
||||
|
||||
use super::scroll_area::ScrollBarVisibility;
|
||||
use super::{area, resize, Area, Frame, Resize, ScrollArea};
|
||||
|
|
@ -452,8 +448,6 @@ impl<'open> Window<'open> {
|
|||
let header_color =
|
||||
frame.map_or_else(|| ctx.style().visuals.widgets.open.weak_bg_fill, |f| f.fill);
|
||||
let mut window_frame = frame.unwrap_or_else(|| Frame::window(&ctx.style()));
|
||||
// Keep the original inner margin for later use
|
||||
let window_margin = window_frame.inner_margin;
|
||||
|
||||
let is_explicitly_closed = matches!(open, Some(false));
|
||||
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());
|
||||
|
|
@ -483,15 +477,23 @@ impl<'open> Window<'open> {
|
|||
|
||||
area.with_widget_info(|| WidgetInfo::labeled(WidgetType::Window, true, title.text()));
|
||||
|
||||
// Calculate roughly how much larger the window size is compared to the inner rect
|
||||
let (title_bar_height, title_content_spacing) = if with_title_bar {
|
||||
// Calculate roughly how much larger the full window inner size is compared to the content rect
|
||||
let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {
|
||||
let style = ctx.style();
|
||||
let spacing = window_margin.sum().y;
|
||||
let height = ctx.fonts(|f| title.font_height(f, &style)) + spacing;
|
||||
let half_height = (height / 2.0).round() as _;
|
||||
let title_bar_inner_height = ctx
|
||||
.fonts(|fonts| title.font_height(fonts, &style))
|
||||
.at_least(style.spacing.interact_size.y);
|
||||
let title_bar_inner_height = title_bar_inner_height + window_frame.inner_margin.sum().y;
|
||||
let half_height = (title_bar_inner_height / 2.0).round() as _;
|
||||
window_frame.rounding.ne = window_frame.rounding.ne.clamp(0, half_height);
|
||||
window_frame.rounding.nw = window_frame.rounding.nw.clamp(0, half_height);
|
||||
(height, spacing)
|
||||
|
||||
let title_content_spacing = if is_collapsed {
|
||||
0.0
|
||||
} else {
|
||||
window_frame.stroke.width
|
||||
};
|
||||
(title_bar_inner_height, title_content_spacing)
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
};
|
||||
|
|
@ -500,7 +502,8 @@ impl<'open> Window<'open> {
|
|||
// Prevent window from becoming larger than the constrain rect.
|
||||
let constrain_rect = area.constrain_rect();
|
||||
let max_width = constrain_rect.width();
|
||||
let max_height = constrain_rect.height() - title_bar_height;
|
||||
let max_height =
|
||||
constrain_rect.height() - title_bar_height_with_margin - title_content_spacing;
|
||||
resize.max_size.x = resize.max_size.x.min(max_width);
|
||||
resize.max_size.y = resize.max_size.y.min(max_height);
|
||||
}
|
||||
|
|
@ -508,21 +511,28 @@ impl<'open> Window<'open> {
|
|||
// First check for resize to avoid frame delay:
|
||||
let last_frame_outer_rect = area.state().rect();
|
||||
let resize_interaction = ctx.with_accessibility_parent(area.id(), || {
|
||||
resize_interaction(ctx, possible, area_layer_id, last_frame_outer_rect)
|
||||
resize_interaction(
|
||||
ctx,
|
||||
possible,
|
||||
area_layer_id,
|
||||
last_frame_outer_rect,
|
||||
window_frame,
|
||||
)
|
||||
});
|
||||
|
||||
let margins = window_frame.outer_margin.sum()
|
||||
+ window_frame.inner_margin.sum()
|
||||
+ vec2(0.0, title_bar_height);
|
||||
{
|
||||
let margins = window_frame.total_margin().sum()
|
||||
+ vec2(0.0, title_bar_height_with_margin + title_content_spacing);
|
||||
|
||||
resize_response(
|
||||
resize_interaction,
|
||||
ctx,
|
||||
margins,
|
||||
area_layer_id,
|
||||
&mut area,
|
||||
resize_id,
|
||||
);
|
||||
resize_response(
|
||||
resize_interaction,
|
||||
ctx,
|
||||
margins,
|
||||
area_layer_id,
|
||||
&mut area,
|
||||
resize_id,
|
||||
);
|
||||
}
|
||||
|
||||
let mut area_content_ui = area.content_ui(ctx);
|
||||
if is_open {
|
||||
|
|
@ -535,40 +545,43 @@ impl<'open> Window<'open> {
|
|||
let content_inner = {
|
||||
ctx.with_accessibility_parent(area.id(), || {
|
||||
// BEGIN FRAME --------------------------------
|
||||
let frame_stroke = window_frame.stroke;
|
||||
let mut frame = window_frame.begin(&mut area_content_ui);
|
||||
|
||||
let show_close_button = open.is_some();
|
||||
|
||||
let where_to_put_header_background = &area_content_ui.painter().add(Shape::Noop);
|
||||
|
||||
// Backup item spacing before the title bar
|
||||
let item_spacing = frame.content_ui.spacing().item_spacing;
|
||||
// Use title bar spacing as the item spacing before the content
|
||||
frame.content_ui.spacing_mut().item_spacing.y = title_content_spacing;
|
||||
|
||||
let title_bar = if with_title_bar {
|
||||
let title_bar = TitleBar::new(
|
||||
&mut frame.content_ui,
|
||||
&frame.content_ui,
|
||||
title,
|
||||
show_close_button,
|
||||
&mut collapsing,
|
||||
collapsible,
|
||||
window_frame,
|
||||
title_bar_height_with_margin,
|
||||
);
|
||||
resize.min_size.x = resize.min_size.x.at_least(title_bar.rect.width()); // Prevent making window smaller than title bar width
|
||||
resize.min_size.x = resize.min_size.x.at_least(title_bar.inner_rect.width()); // Prevent making window smaller than title bar width
|
||||
|
||||
frame.content_ui.set_min_size(title_bar.inner_rect.size());
|
||||
|
||||
// Skip the title bar (and separator):
|
||||
if is_collapsed {
|
||||
frame.content_ui.add_space(title_bar.inner_rect.height());
|
||||
} else {
|
||||
frame.content_ui.add_space(
|
||||
title_bar.inner_rect.height()
|
||||
+ title_content_spacing
|
||||
+ window_frame.inner_margin.sum().y,
|
||||
);
|
||||
}
|
||||
|
||||
Some(title_bar)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Remove item spacing after the title bar
|
||||
frame.content_ui.spacing_mut().item_spacing.y = 0.0;
|
||||
|
||||
let (content_inner, mut content_response) = collapsing
|
||||
let (content_inner, content_response) = collapsing
|
||||
.show_body_unindented(&mut frame.content_ui, |ui| {
|
||||
// Restore item spacing for the content
|
||||
ui.spacing_mut().item_spacing.y = item_spacing.y;
|
||||
|
||||
resize.show(ui, |ui| {
|
||||
if scroll.is_any_scroll_enabled() {
|
||||
scroll.show(ui, add_contents).inner
|
||||
|
|
@ -584,23 +597,18 @@ impl<'open> Window<'open> {
|
|||
&area_content_ui,
|
||||
&possible,
|
||||
outer_rect,
|
||||
frame_stroke,
|
||||
window_frame.rounding,
|
||||
&window_frame,
|
||||
resize_interaction,
|
||||
);
|
||||
|
||||
// END FRAME --------------------------------
|
||||
|
||||
if let Some(title_bar) = title_bar {
|
||||
let mut title_rect = Rect::from_min_size(
|
||||
outer_rect.min,
|
||||
Vec2 {
|
||||
x: outer_rect.size().x,
|
||||
y: title_bar_height,
|
||||
},
|
||||
);
|
||||
|
||||
title_rect = title_rect.round_to_pixels(area_content_ui.pixels_per_point());
|
||||
if let Some(mut title_bar) = title_bar {
|
||||
title_bar.inner_rect = outer_rect.shrink(window_frame.stroke.width);
|
||||
title_bar.inner_rect.max.y =
|
||||
title_bar.inner_rect.min.y + title_bar_height_with_margin;
|
||||
title_bar.inner_rect =
|
||||
title_bar.inner_rect.round_to_pixels(ctx.pixels_per_point());
|
||||
|
||||
if on_top && area_content_ui.visuals().window_highlight_topmost {
|
||||
let mut round = window_frame.rounding;
|
||||
|
|
@ -612,18 +620,20 @@ impl<'open> Window<'open> {
|
|||
|
||||
area_content_ui.painter().set(
|
||||
*where_to_put_header_background,
|
||||
RectShape::filled(title_rect, round, header_color),
|
||||
RectShape::filled(title_bar.inner_rect, round, header_color),
|
||||
);
|
||||
};
|
||||
|
||||
// Fix title bar separator line position
|
||||
if let Some(response) = &mut content_response {
|
||||
response.rect.min.y = outer_rect.min.y + title_bar_height;
|
||||
if false {
|
||||
ctx.debug_painter().debug_rect(
|
||||
title_bar.inner_rect,
|
||||
Color32::LIGHT_BLUE,
|
||||
"title_bar.rect",
|
||||
);
|
||||
}
|
||||
|
||||
title_bar.ui(
|
||||
&mut area_content_ui,
|
||||
title_rect,
|
||||
&content_response,
|
||||
open,
|
||||
&mut collapsing,
|
||||
|
|
@ -653,12 +663,11 @@ fn paint_resize_corner(
|
|||
ui: &Ui,
|
||||
possible: &PossibleInteractions,
|
||||
outer_rect: Rect,
|
||||
stroke: impl Into<Stroke>,
|
||||
rounding: impl Into<Rounding>,
|
||||
window_frame: &Frame,
|
||||
i: ResizeInteraction,
|
||||
) {
|
||||
let inactive_stroke = stroke.into();
|
||||
let rounding = rounding.into();
|
||||
let rounding = window_frame.rounding;
|
||||
|
||||
let (corner, radius, corner_response) = if possible.resize_right && possible.resize_bottom {
|
||||
(Align2::RIGHT_BOTTOM, rounding.se, i.right & i.bottom)
|
||||
} else if possible.resize_left && possible.resize_bottom {
|
||||
|
|
@ -694,11 +703,12 @@ fn paint_resize_corner(
|
|||
} else if corner_response.hover {
|
||||
ui.visuals().widgets.hovered.fg_stroke
|
||||
} else {
|
||||
inactive_stroke
|
||||
window_frame.stroke
|
||||
};
|
||||
|
||||
let fill_rect = outer_rect.shrink(window_frame.stroke.width);
|
||||
let corner_size = Vec2::splat(ui.visuals().resize_corner_size);
|
||||
let corner_rect = corner.align_size_within_rect(corner_size, outer_rect);
|
||||
let corner_rect = corner.align_size_within_rect(corner_size, fill_rect);
|
||||
let corner_rect = corner_rect.translate(-offset * corner.to_sign()); // move away from corner
|
||||
crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke.color, corner);
|
||||
}
|
||||
|
|
@ -738,7 +748,11 @@ impl PossibleInteractions {
|
|||
/// Resizing the window edges.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct ResizeInteraction {
|
||||
start_rect: Rect,
|
||||
/// Outer rect (outside the stroke)
|
||||
outer_rect: Rect,
|
||||
|
||||
window_frame: Frame,
|
||||
|
||||
left: SideResponse,
|
||||
right: SideResponse,
|
||||
top: SideResponse,
|
||||
|
|
@ -835,13 +849,17 @@ fn resize_response(
|
|||
ctx.memory_mut(|mem| mem.areas_mut().move_to_top(area_layer_id));
|
||||
}
|
||||
|
||||
/// Acts on outer rect (outside the stroke)
|
||||
fn move_and_resize_window(ctx: &Context, interaction: &ResizeInteraction) -> Option<Rect> {
|
||||
if !interaction.any_dragged() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let pointer_pos = ctx.input(|i| i.pointer.interact_pos())?;
|
||||
let mut rect = interaction.start_rect; // prevent drift
|
||||
let mut rect = interaction.outer_rect; // prevent drift
|
||||
|
||||
// Put the rect in the center of the stroke:
|
||||
rect = rect.shrink(interaction.window_frame.stroke.width / 2.0);
|
||||
|
||||
if interaction.left.drag {
|
||||
rect.min.x = pointer_pos.x;
|
||||
|
|
@ -855,6 +873,9 @@ fn move_and_resize_window(ctx: &Context, interaction: &ResizeInteraction) -> Opt
|
|||
rect.max.y = pointer_pos.y;
|
||||
}
|
||||
|
||||
// Return to having the rect outside the stroke:
|
||||
rect = rect.expand(interaction.window_frame.stroke.width / 2.0);
|
||||
|
||||
Some(rect.round_ui())
|
||||
}
|
||||
|
||||
|
|
@ -862,11 +883,13 @@ fn resize_interaction(
|
|||
ctx: &Context,
|
||||
possible: PossibleInteractions,
|
||||
layer_id: LayerId,
|
||||
rect: Rect,
|
||||
outer_rect: Rect,
|
||||
window_frame: Frame,
|
||||
) -> ResizeInteraction {
|
||||
if !possible.resizable() {
|
||||
return ResizeInteraction {
|
||||
start_rect: rect,
|
||||
outer_rect,
|
||||
window_frame,
|
||||
left: Default::default(),
|
||||
right: Default::default(),
|
||||
top: Default::default(),
|
||||
|
|
@ -874,6 +897,9 @@ fn resize_interaction(
|
|||
};
|
||||
}
|
||||
|
||||
// The rect that is in the middle of the stroke:
|
||||
let rect = outer_rect.shrink(window_frame.stroke.width / 2.0);
|
||||
|
||||
let side_response = |rect, id| {
|
||||
let response = ctx.create_widget(
|
||||
WidgetRect {
|
||||
|
|
@ -990,7 +1016,8 @@ fn resize_interaction(
|
|||
}
|
||||
|
||||
let interaction = ResizeInteraction {
|
||||
start_rect: rect,
|
||||
outer_rect,
|
||||
window_frame,
|
||||
left,
|
||||
right,
|
||||
top,
|
||||
|
|
@ -1027,6 +1054,18 @@ fn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction)
|
|||
}
|
||||
|
||||
let rounding = Roundingf::from(ui.visuals().window_rounding);
|
||||
|
||||
// Put the rect in the center of the fixed window stroke:
|
||||
let rect = rect.shrink(interaction.window_frame.stroke.width / 2.0);
|
||||
|
||||
// Make sure the inner part of the stroke is at a pixel boundary:
|
||||
let stroke = visuals.bg_stroke;
|
||||
let half_stroke = stroke.width / 2.0;
|
||||
let rect = rect
|
||||
.shrink(half_stroke)
|
||||
.round_to_pixels(ui.pixels_per_point())
|
||||
.expand(half_stroke);
|
||||
|
||||
let Rect { min, max } = rect;
|
||||
|
||||
let mut points = Vec::new();
|
||||
|
|
@ -1083,80 +1122,74 @@ fn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction)
|
|||
points.push(pos2(max.x, min.y + rounding.ne));
|
||||
points.push(pos2(max.x, max.y - rounding.se));
|
||||
}
|
||||
ui.painter().add(Shape::line(points, visuals.bg_stroke));
|
||||
|
||||
ui.painter().add(Shape::line(points, stroke));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
struct TitleBar {
|
||||
/// A title Id used for dragging windows
|
||||
id: Id,
|
||||
window_frame: Frame,
|
||||
|
||||
/// Prepared text in the title
|
||||
title_galley: Arc<Galley>,
|
||||
|
||||
/// Size of the title bar in a collapsed state (if window is collapsible),
|
||||
/// which includes all necessary space for showing the expand button, the
|
||||
/// title and the close button.
|
||||
min_rect: Rect,
|
||||
|
||||
/// Size of the title bar in an expanded state. This size become known only
|
||||
/// after expanding window and painting its content
|
||||
rect: Rect,
|
||||
/// after expanding window and painting its content.
|
||||
///
|
||||
/// Does not include the stroke, nor the separator line between the title bar and the window contents.
|
||||
inner_rect: Rect,
|
||||
}
|
||||
|
||||
impl TitleBar {
|
||||
fn new(
|
||||
ui: &mut Ui,
|
||||
ui: &Ui,
|
||||
title: WidgetText,
|
||||
show_close_button: bool,
|
||||
collapsing: &mut CollapsingState,
|
||||
collapsible: bool,
|
||||
window_frame: Frame,
|
||||
title_bar_height_with_margin: f32,
|
||||
) -> Self {
|
||||
let inner_response = ui.horizontal(|ui| {
|
||||
let height = ui
|
||||
.fonts(|fonts| title.font_height(fonts, ui.style()))
|
||||
.max(ui.spacing().interact_size.y);
|
||||
ui.set_min_height(height);
|
||||
if false {
|
||||
ui.ctx()
|
||||
.debug_painter()
|
||||
.debug_rect(ui.min_rect(), Color32::GREEN, "outer_min_rect");
|
||||
}
|
||||
|
||||
let item_spacing = ui.spacing().item_spacing;
|
||||
let button_size = Vec2::splat(ui.spacing().icon_width);
|
||||
let inner_height = title_bar_height_with_margin - window_frame.inner_margin.sum().y;
|
||||
|
||||
let pad = ((height - button_size.y) / 2.0).round_ui(); // calculated so that the icon is on the diagonal (if window padding is symmetrical)
|
||||
let item_spacing = ui.spacing().item_spacing;
|
||||
let button_size = Vec2::splat(ui.spacing().icon_width.at_most(inner_height));
|
||||
|
||||
if collapsible {
|
||||
ui.add_space(pad);
|
||||
collapsing.show_default_button_with_size(ui, button_size);
|
||||
}
|
||||
let left_pad = ((inner_height - button_size.y) / 2.0).round_ui(); // calculated so that the icon is on the diagonal (if window padding is symmetrical)
|
||||
|
||||
let title_galley = title.into_galley(
|
||||
ui,
|
||||
Some(crate::TextWrapMode::Extend),
|
||||
f32::INFINITY,
|
||||
TextStyle::Heading,
|
||||
);
|
||||
let title_galley = title.into_galley(
|
||||
ui,
|
||||
Some(crate::TextWrapMode::Extend),
|
||||
f32::INFINITY,
|
||||
TextStyle::Heading,
|
||||
);
|
||||
|
||||
let minimum_width = if collapsible || show_close_button {
|
||||
// If at least one button is shown we make room for both buttons (since title is centered):
|
||||
2.0 * (pad + button_size.x + item_spacing.x) + title_galley.size().x
|
||||
} else {
|
||||
pad + title_galley.size().x + pad
|
||||
};
|
||||
let min_rect = Rect::from_min_size(ui.min_rect().min, vec2(minimum_width, height));
|
||||
let id = ui.advance_cursor_after_rect(min_rect);
|
||||
let minimum_width = if collapsible || show_close_button {
|
||||
// If at least one button is shown we make room for both buttons (since title should be centered):
|
||||
2.0 * (left_pad + button_size.x + item_spacing.x) + title_galley.size().x
|
||||
} else {
|
||||
left_pad + title_galley.size().x + left_pad
|
||||
};
|
||||
let min_inner_size = vec2(minimum_width, inner_height);
|
||||
let min_rect = Rect::from_min_size(ui.min_rect().min, min_inner_size);
|
||||
|
||||
Self {
|
||||
id,
|
||||
title_galley,
|
||||
min_rect,
|
||||
rect: Rect::NAN, // Will be filled in later
|
||||
}
|
||||
});
|
||||
if false {
|
||||
ui.ctx()
|
||||
.debug_painter()
|
||||
.debug_rect(min_rect, Color32::LIGHT_BLUE, "min_rect");
|
||||
}
|
||||
|
||||
let title_bar = inner_response.inner;
|
||||
let rect = inner_response.response.rect;
|
||||
|
||||
Self { rect, ..title_bar }
|
||||
Self {
|
||||
window_frame,
|
||||
title_galley,
|
||||
inner_rect: min_rect, // First estimate - will be refined later
|
||||
}
|
||||
}
|
||||
|
||||
/// Finishes painting of the title bar when the window content size already known.
|
||||
|
|
@ -1174,17 +1207,34 @@ impl TitleBar {
|
|||
/// - `collapsible`: if `true`, double click on the title bar will be handled for a change
|
||||
/// of `collapsing` state
|
||||
fn ui(
|
||||
mut self,
|
||||
self,
|
||||
ui: &mut Ui,
|
||||
outer_rect: Rect,
|
||||
content_response: &Option<Response>,
|
||||
open: Option<&mut bool>,
|
||||
collapsing: &mut CollapsingState,
|
||||
collapsible: bool,
|
||||
) {
|
||||
if let Some(content_response) = &content_response {
|
||||
// Now we know how large we got to be:
|
||||
self.rect.max.x = self.rect.max.x.max(content_response.rect.max.x);
|
||||
let window_frame = self.window_frame;
|
||||
let title_inner_rect = self.inner_rect;
|
||||
|
||||
if false {
|
||||
ui.ctx()
|
||||
.debug_painter()
|
||||
.debug_rect(self.inner_rect, Color32::RED, "TitleBar");
|
||||
}
|
||||
|
||||
if collapsible {
|
||||
// Show collapse-button:
|
||||
let button_center = Align2::LEFT_CENTER
|
||||
.align_size_within_rect(Vec2::splat(self.inner_rect.height()), self.inner_rect)
|
||||
.center();
|
||||
let button_size = Vec2::splat(ui.spacing().icon_width);
|
||||
let button_rect = Rect::from_center_size(button_center, button_size);
|
||||
let button_rect = button_rect.round_to_pixels(ui.pixels_per_point());
|
||||
|
||||
ui.allocate_new_ui(UiBuilder::new().max_rect(button_rect), |ui| {
|
||||
collapsing.show_default_button_with_size(ui, button_size);
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(open) = open {
|
||||
|
|
@ -1194,9 +1244,9 @@ impl TitleBar {
|
|||
}
|
||||
}
|
||||
|
||||
let full_top_rect = Rect::from_x_y_ranges(self.rect.x_range(), self.min_rect.y_range());
|
||||
let text_pos =
|
||||
emath::align::center_size_in_rect(self.title_galley.size(), full_top_rect).left_top();
|
||||
emath::align::center_size_in_rect(self.title_galley.size(), title_inner_rect)
|
||||
.left_top();
|
||||
let text_pos = text_pos - self.title_galley.rect.min.to_vec2();
|
||||
ui.painter().galley(
|
||||
text_pos,
|
||||
|
|
@ -1205,22 +1255,35 @@ impl TitleBar {
|
|||
);
|
||||
|
||||
if let Some(content_response) = &content_response {
|
||||
// paint separator between title and content:
|
||||
let y = content_response.rect.top();
|
||||
// let y = lerp(self.rect.bottom()..=content_response.rect.top(), 0.5);
|
||||
let stroke = ui.visuals().widgets.noninteractive.bg_stroke;
|
||||
// Workaround: To prevent border infringement,
|
||||
// the 0.1 value should ideally be calculated using TessellationOptions::feathering_size_in_pixels
|
||||
// or we could support selectively disabling feathering on line caps
|
||||
let x_range = outer_rect.x_range().shrink(0.1);
|
||||
ui.painter().hline(x_range, y, stroke);
|
||||
// Paint separator between title and content:
|
||||
let content_rect = content_response.rect;
|
||||
if false {
|
||||
ui.ctx()
|
||||
.debug_painter()
|
||||
.debug_rect(content_rect, Color32::RED, "content_rect");
|
||||
}
|
||||
let y = title_inner_rect.bottom() + window_frame.stroke.width / 2.0;
|
||||
|
||||
// To verify the sanity of this, use a very wide window stroke
|
||||
ui.painter()
|
||||
.hline(title_inner_rect.x_range(), y, window_frame.stroke);
|
||||
}
|
||||
|
||||
// Don't cover the close- and collapse buttons:
|
||||
let double_click_rect = self.rect.shrink2(vec2(32.0, 0.0));
|
||||
let double_click_rect = title_inner_rect.shrink2(vec2(32.0, 0.0));
|
||||
|
||||
if false {
|
||||
ui.ctx().debug_painter().debug_rect(
|
||||
double_click_rect,
|
||||
Color32::GREEN,
|
||||
"double_click_rect",
|
||||
);
|
||||
}
|
||||
|
||||
let id = ui.unique_id().with("__window_title_bar");
|
||||
|
||||
if ui
|
||||
.interact(double_click_rect, self.id, Sense::click())
|
||||
.interact(double_click_rect, id, Sense::click())
|
||||
.double_clicked()
|
||||
&& collapsible
|
||||
{
|
||||
|
|
@ -1234,16 +1297,12 @@ impl TitleBar {
|
|||
/// The button is square and its size is determined by the
|
||||
/// [`crate::style::Spacing::icon_width`] setting.
|
||||
fn close_button_ui(&self, ui: &mut Ui) -> Response {
|
||||
let button_center = Align2::RIGHT_CENTER
|
||||
.align_size_within_rect(Vec2::splat(self.inner_rect.height()), self.inner_rect)
|
||||
.center();
|
||||
let button_size = Vec2::splat(ui.spacing().icon_width);
|
||||
let pad = (self.rect.height() - button_size.y) / 2.0; // calculated so that the icon is on the diagonal (if window padding is symmetrical)
|
||||
let button_rect = Rect::from_min_size(
|
||||
pos2(
|
||||
self.rect.right() - pad - button_size.x,
|
||||
self.rect.center().y - 0.5 * button_size.y,
|
||||
),
|
||||
button_size,
|
||||
);
|
||||
|
||||
let button_rect = Rect::from_center_size(button_center, button_size);
|
||||
let button_rect = button_rect.round_to_pixels(ui.pixels_per_point());
|
||||
close_button(ui, button_rect)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1771,12 +1771,14 @@ impl Ui {
|
|||
/// Add extra space before the next widget.
|
||||
///
|
||||
/// The direction is dependent on the layout.
|
||||
/// This will be in addition to the [`crate::style::Spacing::item_spacing`].
|
||||
///
|
||||
/// This will be in addition to the [`crate::style::Spacing::item_spacing`]
|
||||
/// that is always added, but `item_spacing` won't be added _again_ by `add_space`.
|
||||
///
|
||||
/// [`Self::min_rect`] will expand to contain the space.
|
||||
#[inline]
|
||||
pub fn add_space(&mut self, amount: f32) {
|
||||
self.placer.advance_cursor(amount);
|
||||
self.placer.advance_cursor(amount.round_ui());
|
||||
}
|
||||
|
||||
/// Show some text.
|
||||
|
|
|
|||
|
|
@ -44,7 +44,11 @@ pub struct FractalClockApp {
|
|||
impl eframe::App for FractalClockApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame::dark_canvas(&ctx.style()))
|
||||
.frame(
|
||||
egui::Frame::dark_canvas(&ctx.style())
|
||||
.stroke(egui::Stroke::NONE)
|
||||
.rounding(0),
|
||||
)
|
||||
.show(ctx, |ui| {
|
||||
self.fractal_clock
|
||||
.ui(ui, self.mock_time.or(Some(crate::seconds_since_midnight())));
|
||||
|
|
@ -293,7 +297,7 @@ impl eframe::App for WrapApp {
|
|||
|
||||
let mut cmd = Command::Nothing;
|
||||
egui::TopBottomPanel::top("wrap_app_top_bar")
|
||||
.frame(egui::Frame::none().inner_margin(4.0))
|
||||
.frame(egui::Frame::new().inner_margin(4))
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.visuals_mut().button_frame = false;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7c05cc3d48242e46a391af34cb56f72de7933bf2cead009b6cd477c21867a84e
|
||||
size 327802
|
||||
oid sha256:4aeab31841dd95b5e0f4bd0af0c0ba49a862d50836dbafdf2172fbbab950c105
|
||||
size 327741
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:61212e30fe1fecf5891ddad6ac795df510bfad76b21a7a8a13aa024fdad6d05e
|
||||
size 93118
|
||||
oid sha256:0e4a90792a9876da549f3d1da9b057a078400ad15db2cc6e35f4324851137d4e
|
||||
size 93115
|
||||
|
|
|
|||
|
|
@ -7,19 +7,18 @@ pub struct FrameDemo {
|
|||
impl Default for FrameDemo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
frame: egui::Frame {
|
||||
inner_margin: 12.0.into(),
|
||||
outer_margin: 24.0.into(),
|
||||
rounding: 14.0.into(),
|
||||
shadow: egui::Shadow {
|
||||
frame: egui::Frame::new()
|
||||
.inner_margin(12)
|
||||
.outer_margin(24)
|
||||
.rounding(14)
|
||||
.shadow(egui::Shadow {
|
||||
offset: [8, 12],
|
||||
blur: 16,
|
||||
spread: 0,
|
||||
color: egui::Color32::from_black_alpha(180),
|
||||
},
|
||||
fill: egui::Color32::from_rgba_unmultiplied(97, 0, 255, 128),
|
||||
stroke: egui::Stroke::new(1.0, egui::Color32::GRAY),
|
||||
},
|
||||
})
|
||||
.fill(egui::Color32::from_rgba_unmultiplied(97, 0, 255, 128))
|
||||
.stroke(egui::Stroke::new(1.0, egui::Color32::GRAY)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use super::{Demo, View};
|
||||
|
||||
use egui::{
|
||||
vec2, Align, Checkbox, CollapsingHeader, Color32, Context, FontId, Frame, Resize, RichText,
|
||||
Sense, Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window,
|
||||
vec2, Align, Checkbox, CollapsingHeader, Color32, Context, FontId, Resize, RichText, Sense,
|
||||
Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window,
|
||||
};
|
||||
|
||||
/// Showcase some ui code
|
||||
|
|
@ -512,54 +512,52 @@ fn ui_stack_demo(ui: &mut Ui) {
|
|||
);
|
||||
});
|
||||
let stack = ui.stack().clone();
|
||||
Frame {
|
||||
inner_margin: ui.spacing().menu_margin,
|
||||
stroke: ui.visuals().widgets.noninteractive.bg_stroke,
|
||||
..Default::default()
|
||||
}
|
||||
.show(ui, |ui| {
|
||||
egui_extras::TableBuilder::new(ui)
|
||||
.column(egui_extras::Column::auto())
|
||||
.column(egui_extras::Column::auto())
|
||||
.header(18.0, |mut header| {
|
||||
header.col(|ui| {
|
||||
ui.strong("id");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("kind");
|
||||
});
|
||||
})
|
||||
.body(|mut body| {
|
||||
for node in stack.iter() {
|
||||
body.row(18.0, |mut row| {
|
||||
row.col(|ui| {
|
||||
let response = ui.label(format!("{:?}", node.id));
|
||||
egui::Frame::new()
|
||||
.inner_margin(ui.spacing().menu_margin)
|
||||
.stroke(ui.visuals().widgets.noninteractive.bg_stroke)
|
||||
.show(ui, |ui| {
|
||||
egui_extras::TableBuilder::new(ui)
|
||||
.column(egui_extras::Column::auto())
|
||||
.column(egui_extras::Column::auto())
|
||||
.header(18.0, |mut header| {
|
||||
header.col(|ui| {
|
||||
ui.strong("id");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("kind");
|
||||
});
|
||||
})
|
||||
.body(|mut body| {
|
||||
for node in stack.iter() {
|
||||
body.row(18.0, |mut row| {
|
||||
row.col(|ui| {
|
||||
let response = ui.label(format!("{:?}", node.id));
|
||||
|
||||
if response.hovered() {
|
||||
ui.ctx().debug_painter().debug_rect(
|
||||
node.max_rect,
|
||||
Color32::GREEN,
|
||||
"max_rect",
|
||||
);
|
||||
ui.ctx().debug_painter().circle_filled(
|
||||
node.min_rect.min,
|
||||
2.0,
|
||||
Color32::RED,
|
||||
);
|
||||
}
|
||||
});
|
||||
if response.hovered() {
|
||||
ui.ctx().debug_painter().debug_rect(
|
||||
node.max_rect,
|
||||
Color32::GREEN,
|
||||
"max_rect",
|
||||
);
|
||||
ui.ctx().debug_painter().circle_filled(
|
||||
node.min_rect.min,
|
||||
2.0,
|
||||
Color32::RED,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
row.col(|ui| {
|
||||
ui.label(if let Some(kind) = node.kind() {
|
||||
format!("{kind:?}")
|
||||
} else {
|
||||
"-".to_owned()
|
||||
row.col(|ui| {
|
||||
ui.label(if let Some(kind) = node.kind() {
|
||||
format!("{kind:?}")
|
||||
} else {
|
||||
"-".to_owned()
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ui.small("Hover on UI's ids to display their origin and max rect.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cf83bead834ec8f88d74b32ae6331715e8c6df183e007e2a16004c019534a30f
|
||||
size 31810
|
||||
oid sha256:d4cfd5191dc7046a782ef2350dc8e0547d2702182badcb15b6b928ce077b76c1
|
||||
size 32154
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0a1099b85a1aaf20f3f1e091bc68259f811737feaefdfcc12acd067eca8f9117
|
||||
size 27083
|
||||
oid sha256:e89c730b462c2b60b90f2ac15fe9576e878a4906c223317c51344a0ec2b6d993
|
||||
size 27564
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6969c6da67ea6cc7ebbbd7a2cc1cb13d4720befe28126367cbf2b2679d037674
|
||||
size 82363
|
||||
oid sha256:ea2c944af8bc1be42ec7c00be58dfaa23c92bca8957eda94f2ff10f5b4242562
|
||||
size 83358
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:332c2af36873d8ccccb36c08fd2e475dc1f18454a3090a851c0889395d4f364f
|
||||
size 11518
|
||||
oid sha256:c401ff91fff4051042528d398d2b2270a4ae924570e6332cf8f2c6774c845160
|
||||
size 11826
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d0d0b1b4d2c4b624904250bc8d6870559f0179e3f7f2d6dc4a4ff256df356237
|
||||
size 20626
|
||||
oid sha256:7efc1ff3e4e5bfd4216394f94ee7486c272a9ca1c980789f4ad143f89b0a7103
|
||||
size 21073
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d5fe6166bb8cd5fae0899957e968312b9229a79e423a5d154eda483a149b264d
|
||||
size 20831
|
||||
oid sha256:d9c48cf928a17dd0980ba086aa004bde3a0040dcb82752d138c1df34f1ef3d2f
|
||||
size 21167
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b71e1d109f90e017644dd20b9d84d81e3a6d5195afbd01ba86c85fa248c8b5c5
|
||||
size 10703
|
||||
oid sha256:be0f96c700b7662aab5098f8412dae3676116eeed65e70f6b295dd3375b329d0
|
||||
size 10968
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e3c9ba9064f44a4a14405f53316c1c602184caf16cb584d7c1f1912fe59f85ab
|
||||
size 135712
|
||||
oid sha256:dc69c76eaa121e9e7782cfbbb68b5a23004d79862bae4af2e3ca3a29eff04bea
|
||||
size 136467
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:097bd512dd71c17370f6e374312c19e7ab6845f151b3c3565f2a0790b61ee7ba
|
||||
size 24413
|
||||
oid sha256:23187a9fb12a3ab7df4e2321aa25b493559923d61e82802f843ee29dcd932f7b
|
||||
size 24985
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2bdf54573a6b0d2fedd90314f91dd7de22dd13709e8dd699b37ef8935b6adda5
|
||||
size 17785
|
||||
oid sha256:7f433f3e8bff38a0aafd7e6cba5c5efe1abf484550a6f9e90008f8f5ea891497
|
||||
size 18113
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d6328c86b70f3e24aaf87db200d13dfd0baa787dd8431e47621457937f8f5021
|
||||
size 22552
|
||||
oid sha256:e5105ecf77852412c0dd904b96f0fec752f22e416df9932df4499d6d5a776f46
|
||||
size 22865
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:afb57dc0bb8ff839138e36b8780136e0c8da55ff380832538fae0394143807c0
|
||||
size 65321
|
||||
oid sha256:ebf0403bd599e5c00c2831f9c4032e8d20420212c9cd7fa875f1ae1cbbc8d3a7
|
||||
size 65902
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:403fc1d08d79168bc9f7a08ac7f363f2df916547f08311838edfda8a499a9b2d
|
||||
size 32879
|
||||
oid sha256:4e690dc73873ab75c030d3c0238e9d5b840f976dd8f4882dc1e930024d217838
|
||||
size 33323
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cbd490a15377bdd4fd3272f3cd126cbc4fb9ed2f6f84edcbba03dd30dc3f3d99
|
||||
size 36780
|
||||
oid sha256:e760210371dbf2a197f96a78d01b7480f0ae05d46bbb4e642276b2eb30847ec2
|
||||
size 37075
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c31b3998817e345b12b037d7f8bec7641f77d0c7eab7da9a746b7b51c9efc8fb
|
||||
size 17531
|
||||
oid sha256:fd02b208d0e4e306bbc9a54f25f5a3d20875a12182cef3224e6daa309b6cf453
|
||||
size 17898
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4e8963c3ecd0e74fe9641d87101742a0d45c82a582d70e30eb36bc835f5aac06
|
||||
size 25330
|
||||
oid sha256:341958da648a7db3374c4337cf057ae8e81c08c4a6de7e4f1cbe9c5b049f2e62
|
||||
size 25727
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:88b3a50b481942630b5804c60227f23b719dc7e3eb6dbe432c2448cb69332def
|
||||
size 262141
|
||||
oid sha256:8934cff7203d19b38df9d91729091ff5d1ad6c8d445fd9c1cb62b6df1bb8cb80
|
||||
size 263547
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4d5d628b54b28eccac0d9ef21bbdabace0cdf507898707956943e2259df846ca
|
||||
size 23741
|
||||
oid sha256:5d61f58138798d701bb8dda2c3240eef69eb350df3168fb3aa4148e4fef3f77a
|
||||
size 24077
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8763a8f8b4242cbe589cd3282dc5f4b32a84b4e0416fb8072dfefb7879a5d4f6
|
||||
size 187982
|
||||
oid sha256:e4006e93663d02fe0f4485d2c163ab2b6feded787bee87ea15616fc0b36136d0
|
||||
size 188875
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3d61088bf1f992467e8396ac89407fa158d6f44e2d2d196778247f3ff18cb893
|
||||
size 119759
|
||||
oid sha256:70b170ba7b8e51d9d9f766d7ce25068fa4265c4127e729af4f1adaacbb745d19
|
||||
size 120947
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:79ebaf9cccd19da2923507b5a553a23edc67382ef59e4b71f01d3bd6cc913539
|
||||
size 25829
|
||||
oid sha256:157353a8c9bcb638a8be485489e4a232f348eae3cc4ceefe227d7970c7d1f8b3
|
||||
size 26256
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:06cbf13841e6ac5fbc57bdae6f5ad9d19718193c344420dedcc0e9d7ed2b8ba9
|
||||
size 71590
|
||||
oid sha256:cf0ddd39a45519dcf9027f691e856921c736d18e2eeafd16f0e086720121b6a7
|
||||
size 72286
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:35e66f211c0b30a684371b520c46dbe4f9d5b6835e053a4eb65f492dd66a9e6c
|
||||
size 67288
|
||||
oid sha256:914d37e326087f770994bcf3867a27d88050c57887a2b42c68414d311fa96003
|
||||
size 67698
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fa9ee8631bfe433ee6fad1fb1651fd6b63e2fb3fbc5f321a5410f7266dc16d09
|
||||
size 21296
|
||||
oid sha256:b0fdf8ce329883450e071e4733c3904577999d18ac61c922c7caacbec09dfda7
|
||||
size 21661
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6475702b1bf2c65fb5196928a8934ade647a6053d6902a836e3d69cb7920091e
|
||||
size 59874
|
||||
oid sha256:375b71a8ac5b0e48f3c67a089ef0e8a4fd17f8eb21fa535422099c29c2747e27
|
||||
size 59991
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9d8daaec0c58709298a4594b7b2aa935aa2de327e6b71bd7417c2ba3a6eb060c
|
||||
size 13020
|
||||
oid sha256:aa96b1e3733e4af94a6cb6ec765c3f3581df2175e75831eb00bd42df2e7a2708
|
||||
size 13285
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:29d8d859a8cb11e4b390d45c658ed8ff191c2e542909e12f2385c0cba62baa2d
|
||||
size 35109
|
||||
oid sha256:18880dfaf5d198876c4db97ebd6313d59755a3e8298567f2b2fa91dcc21699c5
|
||||
size 35607
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:17217e600d8a85ec00ecb11f0e5fe69496b30bbf715cc86785cec6e18b8c2fa1
|
||||
size 48158
|
||||
oid sha256:d626b310439bff13487548bbba8b516886c13049824a7f5dd902f6dffb3c5ba4
|
||||
size 48234
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fac50d2327a9e5e99989dd81d5644db86031b91b9c5c68fc17b5ef53ae655048
|
||||
size 47970
|
||||
oid sha256:80a2968e211c83639b60e84b805f1327fb37b87144cada672a403c7e92ace8a8
|
||||
size 48066
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e5e829257b742194742429be0efd326be17ff7f5b9b16f9df58df21e899320bd
|
||||
size 43963
|
||||
oid sha256:86df5dc4b4ddd6f44226242b6d9b5e9f2aacd45193ae9f784fb5084a7a509e0b
|
||||
size 43987
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5da064332c669a860a92a34b101f23e28026d4f07948f7c3e9a40e611f5e284f
|
||||
size 43986
|
||||
oid sha256:1138bbc3b7e73cccd555f0fd58c27a5bda4d84484fdc1bd5223fc9802d0c5328
|
||||
size 44089
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl<'a, State> AppKind<'a, State> {
|
|||
sizing_pass: bool,
|
||||
) -> egui::Response {
|
||||
egui::CentralPanel::default()
|
||||
.frame(Frame::none())
|
||||
.frame(Frame::NONE)
|
||||
.show(ctx, |ui| {
|
||||
let mut builder = egui::UiBuilder::new();
|
||||
if sizing_pass {
|
||||
|
|
|
|||
|
|
@ -45,13 +45,11 @@ impl eframe::App for MyApp {
|
|||
fn custom_window_frame(ctx: &egui::Context, title: &str, add_contents: impl FnOnce(&mut egui::Ui)) {
|
||||
use egui::{CentralPanel, UiBuilder};
|
||||
|
||||
let panel_frame = egui::Frame {
|
||||
fill: ctx.style().visuals.window_fill(),
|
||||
rounding: 10.0.into(),
|
||||
stroke: ctx.style().visuals.widgets.noninteractive.fg_stroke,
|
||||
outer_margin: 1.0.into(), // so the stroke is within the bounds
|
||||
..Default::default()
|
||||
};
|
||||
let panel_frame = egui::Frame::new()
|
||||
.fill(ctx.style().visuals.window_fill())
|
||||
.rounding(10)
|
||||
.stroke(ctx.style().visuals.widgets.noninteractive.fg_stroke)
|
||||
.outer_margin(1); // so the stroke is within the bounds
|
||||
|
||||
CentralPanel::default().frame(panel_frame).show(ctx, |ui| {
|
||||
let app_rect = ui.max_rect();
|
||||
|
|
|
|||
|
|
@ -62,27 +62,23 @@ impl eframe::App for MyApp {
|
|||
|
||||
// nested frames test
|
||||
ui.add_space(20.0);
|
||||
egui::Frame {
|
||||
stroke: ui.visuals().noninteractive().bg_stroke,
|
||||
inner_margin: egui::Margin::same(4),
|
||||
outer_margin: egui::Margin::same(4),
|
||||
..Default::default()
|
||||
}
|
||||
.show(ui, |ui| {
|
||||
full_span_widget(ui, false);
|
||||
stack_ui(ui);
|
||||
|
||||
egui::Frame {
|
||||
stroke: ui.visuals().noninteractive().bg_stroke,
|
||||
inner_margin: egui::Margin::same(8),
|
||||
outer_margin: egui::Margin::same(6),
|
||||
..Default::default()
|
||||
}
|
||||
egui::Frame::new()
|
||||
.stroke(ui.visuals().noninteractive().bg_stroke)
|
||||
.inner_margin(4)
|
||||
.outer_margin(4)
|
||||
.show(ui, |ui| {
|
||||
full_span_widget(ui, false);
|
||||
stack_ui(ui);
|
||||
|
||||
egui::Frame::new()
|
||||
.stroke(ui.visuals().noninteractive().bg_stroke)
|
||||
.inner_margin(8)
|
||||
.outer_margin(6)
|
||||
.show(ui, |ui| {
|
||||
full_span_widget(ui, false);
|
||||
stack_ui(ui);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -126,18 +122,16 @@ impl eframe::App for MyApp {
|
|||
// Ui nesting test
|
||||
ui.add_space(20.0);
|
||||
ui.label("UI nesting test:");
|
||||
egui::Frame {
|
||||
stroke: ui.visuals().noninteractive().bg_stroke,
|
||||
inner_margin: egui::Margin::same(4),
|
||||
..Default::default()
|
||||
}
|
||||
.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.scope(stack_ui);
|
||||
egui::Frame::new()
|
||||
.stroke(ui.visuals().noninteractive().bg_stroke)
|
||||
.inner_margin(4)
|
||||
.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.scope(stack_ui);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// table test
|
||||
let mut cell_stack = None;
|
||||
|
|
@ -265,106 +259,104 @@ fn stack_ui(ui: &mut egui::Ui) {
|
|||
}
|
||||
|
||||
fn stack_ui_impl(ui: &mut egui::Ui, stack: &egui::UiStack) {
|
||||
egui::Frame {
|
||||
stroke: ui.style().noninteractive().fg_stroke,
|
||||
inner_margin: egui::Margin::same(4),
|
||||
..Default::default()
|
||||
}
|
||||
.show(ui, |ui| {
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
egui::Frame::new()
|
||||
.stroke(ui.style().noninteractive().fg_stroke)
|
||||
.inner_margin(4)
|
||||
.show(ui, |ui| {
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
egui_extras::TableBuilder::new(ui)
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.header(20.0, |mut header| {
|
||||
header.col(|ui| {
|
||||
ui.strong("id");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("kind");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("stroke");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("inner");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("outer");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("direction");
|
||||
});
|
||||
})
|
||||
.body(|mut body| {
|
||||
for node in stack.iter() {
|
||||
body.row(20.0, |mut row| {
|
||||
row.col(|ui| {
|
||||
if ui.label(format!("{:?}", node.id)).hovered() {
|
||||
ui.ctx().debug_painter().debug_rect(
|
||||
node.max_rect,
|
||||
egui::Color32::GREEN,
|
||||
"max",
|
||||
);
|
||||
ui.ctx().debug_painter().circle_filled(
|
||||
node.min_rect.min,
|
||||
2.0,
|
||||
egui::Color32::RED,
|
||||
);
|
||||
}
|
||||
});
|
||||
row.col(|ui| {
|
||||
let s = if let Some(kind) = node.kind() {
|
||||
format!("{kind:?}")
|
||||
} else {
|
||||
"-".to_owned()
|
||||
};
|
||||
|
||||
ui.label(s);
|
||||
});
|
||||
row.col(|ui| {
|
||||
let frame = node.frame();
|
||||
if frame.stroke == egui::Stroke::NONE {
|
||||
ui.label("-");
|
||||
} else {
|
||||
let mut layout_job = egui::text::LayoutJob::default();
|
||||
layout_job.append(
|
||||
"⬛ ",
|
||||
0.0,
|
||||
egui::TextFormat::simple(
|
||||
egui::TextStyle::Body.resolve(ui.style()),
|
||||
frame.stroke.color,
|
||||
),
|
||||
);
|
||||
layout_job.append(
|
||||
format!("{}px", frame.stroke.width).as_str(),
|
||||
0.0,
|
||||
egui::TextFormat::simple(
|
||||
egui::TextStyle::Body.resolve(ui.style()),
|
||||
ui.style().visuals.text_color(),
|
||||
),
|
||||
);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
ui.label(layout_job);
|
||||
}
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(print_margin(&node.frame().inner_margin));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(print_margin(&node.frame().outer_margin));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(format!("{:?}", node.layout_direction));
|
||||
});
|
||||
egui_extras::TableBuilder::new(ui)
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.header(20.0, |mut header| {
|
||||
header.col(|ui| {
|
||||
ui.strong("id");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("kind");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("stroke");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("inner");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("outer");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("direction");
|
||||
});
|
||||
})
|
||||
.body(|mut body| {
|
||||
for node in stack.iter() {
|
||||
body.row(20.0, |mut row| {
|
||||
row.col(|ui| {
|
||||
if ui.label(format!("{:?}", node.id)).hovered() {
|
||||
ui.ctx().debug_painter().debug_rect(
|
||||
node.max_rect,
|
||||
egui::Color32::GREEN,
|
||||
"max",
|
||||
);
|
||||
ui.ctx().debug_painter().circle_filled(
|
||||
node.min_rect.min,
|
||||
2.0,
|
||||
egui::Color32::RED,
|
||||
);
|
||||
}
|
||||
});
|
||||
row.col(|ui| {
|
||||
let s = if let Some(kind) = node.kind() {
|
||||
format!("{kind:?}")
|
||||
} else {
|
||||
"-".to_owned()
|
||||
};
|
||||
|
||||
ui.label(s);
|
||||
});
|
||||
row.col(|ui| {
|
||||
let frame = node.frame();
|
||||
if frame.stroke == egui::Stroke::NONE {
|
||||
ui.label("-");
|
||||
} else {
|
||||
let mut layout_job = egui::text::LayoutJob::default();
|
||||
layout_job.append(
|
||||
"⬛ ",
|
||||
0.0,
|
||||
egui::TextFormat::simple(
|
||||
egui::TextStyle::Body.resolve(ui.style()),
|
||||
frame.stroke.color,
|
||||
),
|
||||
);
|
||||
layout_job.append(
|
||||
format!("{}px", frame.stroke.width).as_str(),
|
||||
0.0,
|
||||
egui::TextFormat::simple(
|
||||
egui::TextStyle::Body.resolve(ui.style()),
|
||||
ui.style().visuals.text_color(),
|
||||
),
|
||||
);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
ui.label(layout_job);
|
||||
}
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(print_margin(&node.frame().inner_margin));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(print_margin(&node.frame().outer_margin));
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(format!("{:?}", node.layout_direction));
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn print_margin(margin: &egui::Margin) -> String {
|
||||
|
|
|
|||
Loading…
Reference in New Issue