⚠️ `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};
|
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| {
|
/// # 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))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[must_use = "You should call .show()"]
|
#[must_use = "You should call .show()"]
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
|
// Fields are ordered inside-out.
|
||||||
|
// TODO(emilk): add `min_content_size: Vec2`
|
||||||
|
//
|
||||||
/// Margin within the painted frame.
|
/// Margin within the painted frame.
|
||||||
|
///
|
||||||
|
/// Known as `padding` in CSS.
|
||||||
|
#[doc(alias = "padding")]
|
||||||
pub inner_margin: Margin,
|
pub inner_margin: Margin,
|
||||||
|
|
||||||
/// Margin outside the painted frame.
|
/// The background fill color of the frame, within the [`Self::stroke`].
|
||||||
pub outer_margin: Margin,
|
///
|
||||||
|
/// Known as `background` in CSS.
|
||||||
pub rounding: Rounding,
|
#[doc(alias = "background")]
|
||||||
|
|
||||||
pub shadow: Shadow,
|
|
||||||
|
|
||||||
pub fill: Color32,
|
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,
|
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]
|
#[test]
|
||||||
|
|
@ -85,68 +149,72 @@ fn frame_size() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ## Constructors
|
||||||
impl Frame {
|
impl Frame {
|
||||||
pub fn none() -> Self {
|
/// No colors, no margins, no border.
|
||||||
Self::default()
|
///
|
||||||
|
/// 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.
|
/// 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::new()
|
||||||
inner_margin: Margin::same(6), // same and symmetric looks best in corners when nesting groups
|
.inner_margin(6)
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn side_top_panel(style: &Style) -> Self {
|
pub fn side_top_panel(style: &Style) -> Self {
|
||||||
Self {
|
Self::new()
|
||||||
inner_margin: Margin::symmetric(8, 2),
|
.inner_margin(Margin::symmetric(8, 2))
|
||||||
fill: style.visuals.panel_fill,
|
.fill(style.visuals.panel_fill)
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn central_panel(style: &Style) -> Self {
|
pub fn central_panel(style: &Style) -> Self {
|
||||||
Self {
|
Self::new().inner_margin(8).fill(style.visuals.panel_fill)
|
||||||
inner_margin: Margin::same(8),
|
|
||||||
fill: style.visuals.panel_fill,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window(style: &Style) -> Self {
|
pub fn window(style: &Style) -> Self {
|
||||||
Self {
|
Self::new()
|
||||||
inner_margin: style.spacing.window_margin,
|
.inner_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())
|
||||||
stroke: style.visuals.window_stroke(),
|
.stroke(style.visuals.window_stroke())
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn menu(style: &Style) -> Self {
|
pub fn menu(style: &Style) -> Self {
|
||||||
Self {
|
Self::new()
|
||||||
inner_margin: style.spacing.menu_margin,
|
.inner_margin(style.spacing.menu_margin)
|
||||||
rounding: style.visuals.menu_rounding,
|
.rounding(style.visuals.menu_rounding)
|
||||||
shadow: style.visuals.popup_shadow,
|
.shadow(style.visuals.popup_shadow)
|
||||||
fill: style.visuals.window_fill(),
|
.fill(style.visuals.window_fill())
|
||||||
stroke: style.visuals.window_stroke(),
|
.stroke(style.visuals.window_stroke())
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popup(style: &Style) -> Self {
|
pub fn popup(style: &Style) -> Self {
|
||||||
Self {
|
Self::new()
|
||||||
inner_margin: style.spacing.menu_margin,
|
.inner_margin(style.spacing.menu_margin)
|
||||||
rounding: style.visuals.menu_rounding,
|
.rounding(style.visuals.menu_rounding)
|
||||||
shadow: style.visuals.popup_shadow,
|
.shadow(style.visuals.popup_shadow)
|
||||||
fill: style.visuals.window_fill(),
|
.fill(style.visuals.window_fill())
|
||||||
stroke: style.visuals.window_stroke(),
|
.stroke(style.visuals.window_stroke())
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A canvas to draw on.
|
/// A canvas to draw on.
|
||||||
|
|
@ -154,57 +222,77 @@ impl Frame {
|
||||||
/// In bright mode this will be very bright,
|
/// In bright mode this will be very bright,
|
||||||
/// and in dark mode this will be very dark.
|
/// and in dark mode this will be very dark.
|
||||||
pub fn canvas(style: &Style) -> Self {
|
pub fn canvas(style: &Style) -> Self {
|
||||||
Self {
|
Self::new()
|
||||||
inner_margin: Margin::same(2),
|
.inner_margin(2)
|
||||||
rounding: style.visuals.widgets.noninteractive.rounding,
|
.rounding(style.visuals.widgets.noninteractive.rounding)
|
||||||
fill: style.visuals.extreme_bg_color,
|
.fill(style.visuals.extreme_bg_color)
|
||||||
stroke: style.visuals.window_stroke(),
|
.stroke(style.visuals.window_stroke())
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dark canvas to draw on.
|
/// A dark canvas to draw on.
|
||||||
pub fn dark_canvas(style: &Style) -> Self {
|
pub fn dark_canvas(style: &Style) -> Self {
|
||||||
Self {
|
Self::canvas(style).fill(Color32::from_black_alpha(250))
|
||||||
fill: Color32::from_black_alpha(250),
|
|
||||||
..Self::canvas(style)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ## Builders
|
||||||
impl Frame {
|
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.
|
/// Margin within the painted frame.
|
||||||
|
///
|
||||||
|
/// Known as `padding` in CSS.
|
||||||
|
#[doc(alias = "padding")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inner_margin(mut self, inner_margin: impl Into<Margin>) -> Self {
|
pub fn inner_margin(mut self, inner_margin: impl Into<Margin>) -> Self {
|
||||||
self.inner_margin = inner_margin.into();
|
self.inner_margin = inner_margin.into();
|
||||||
self
|
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.
|
/// 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]
|
#[inline]
|
||||||
pub fn outer_margin(mut self, outer_margin: impl Into<Margin>) -> Self {
|
pub fn outer_margin(mut self, outer_margin: impl Into<Margin>) -> Self {
|
||||||
self.outer_margin = outer_margin.into();
|
self.outer_margin = outer_margin.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Optional drop-shadow behind the frame.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shadow(mut self, shadow: Shadow) -> Self {
|
pub fn shadow(mut self, shadow: Shadow) -> Self {
|
||||||
self.shadow = shadow;
|
self.shadow = shadow;
|
||||||
|
|
@ -224,11 +312,37 @@ impl Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ## Inspectors
|
||||||
impl Frame {
|
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]
|
#[inline]
|
||||||
pub fn total_margin(&self) -> Marginf {
|
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 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 - 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:
|
// Make sure we don't shrink to the negative:
|
||||||
inner_rect.max.x = inner_rect.max.x.max(inner_rect.min.x);
|
max_content_rect.max.x = max_content_rect.max.x.max(max_content_rect.min.x);
|
||||||
inner_rect.max.y = inner_rect.max.y.max(inner_rect.min.y);
|
max_content_rect.max.y = max_content_rect.max.y.max(max_content_rect.min.y);
|
||||||
|
|
||||||
let content_ui = ui.new_child(
|
let content_ui = ui.new_child(
|
||||||
UiBuilder::new()
|
UiBuilder::new()
|
||||||
.ui_stack_info(UiStackInfo::new(UiKind::Frame).with_frame(self))
|
.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 {
|
Prepared {
|
||||||
frame: self,
|
frame: self,
|
||||||
where_to_put_background,
|
where_to_put_background,
|
||||||
|
|
@ -298,32 +410,37 @@ impl Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paint this frame as a shape.
|
/// Paint this frame as a shape.
|
||||||
///
|
pub fn paint(&self, content_rect: Rect) -> Shape {
|
||||||
/// The margin is ignored.
|
|
||||||
pub fn paint(&self, outer_rect: Rect) -> Shape {
|
|
||||||
let Self {
|
let Self {
|
||||||
inner_margin: _,
|
inner_margin: _,
|
||||||
outer_margin: _,
|
|
||||||
rounding,
|
|
||||||
shadow,
|
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
|
rounding,
|
||||||
|
outer_margin: _,
|
||||||
|
shadow,
|
||||||
} = *self;
|
} = *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() {
|
if shadow == Default::default() {
|
||||||
frame_shape
|
frame_shape
|
||||||
} else {
|
} 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])
|
Shape::Vec(vec![Shape::from(shadow), frame_shape])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Prepared {
|
impl Prepared {
|
||||||
fn content_with_margin(&self) -> Rect {
|
fn outer_rect(&self) -> Rect {
|
||||||
self.content_ui.min_rect() + self.frame.inner_margin + self.frame.outer_margin
|
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`].
|
/// 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`].
|
/// This can be called before or after [`Self::paint`].
|
||||||
pub fn allocate_space(&self, ui: &mut Ui) -> Response {
|
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.
|
/// Paint the frame.
|
||||||
///
|
///
|
||||||
/// This can be called before or after [`Self::allocate_space`].
|
/// This can be called before or after [`Self::allocate_space`].
|
||||||
pub fn paint(&self, ui: &Ui) {
|
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) {
|
if ui.is_rect_visible(widget_rect) {
|
||||||
let shape = self.frame.paint(paint_rect);
|
let shape = self.frame.paint(content_rect);
|
||||||
ui.painter().set(self.where_to_put_background, shape);
|
ui.painter().set(self.where_to_put_background, shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience for calling [`Self::allocate_space`] and [`Self::paint`].
|
/// 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 {
|
pub fn end(self, ui: &mut Ui) -> Response {
|
||||||
self.paint(ui);
|
self.paint(ui);
|
||||||
self.allocate_space(ui)
|
self.allocate_space(ui)
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,11 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
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 emath::GuiRounding as _;
|
||||||
use epaint::{
|
use epaint::{RectShape, Roundingf};
|
||||||
emath, pos2, vec2, Galley, Pos2, Rect, RectShape, Rounding, Roundingf, Shape, Stroke, Vec2,
|
|
||||||
};
|
use crate::collapsing_header::CollapsingState;
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
use super::scroll_area::ScrollBarVisibility;
|
use super::scroll_area::ScrollBarVisibility;
|
||||||
use super::{area, resize, Area, Frame, Resize, ScrollArea};
|
use super::{area, resize, Area, Frame, Resize, ScrollArea};
|
||||||
|
|
@ -452,8 +448,6 @@ impl<'open> Window<'open> {
|
||||||
let header_color =
|
let header_color =
|
||||||
frame.map_or_else(|| ctx.style().visuals.widgets.open.weak_bg_fill, |f| f.fill);
|
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()));
|
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_explicitly_closed = matches!(open, Some(false));
|
||||||
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());
|
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()));
|
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
|
// Calculate roughly how much larger the full window inner size is compared to the content rect
|
||||||
let (title_bar_height, title_content_spacing) = if with_title_bar {
|
let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {
|
||||||
let style = ctx.style();
|
let style = ctx.style();
|
||||||
let spacing = window_margin.sum().y;
|
let title_bar_inner_height = ctx
|
||||||
let height = ctx.fonts(|f| title.font_height(f, &style)) + spacing;
|
.fonts(|fonts| title.font_height(fonts, &style))
|
||||||
let half_height = (height / 2.0).round() as _;
|
.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.ne = window_frame.rounding.ne.clamp(0, half_height);
|
||||||
window_frame.rounding.nw = window_frame.rounding.nw.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 {
|
} else {
|
||||||
(0.0, 0.0)
|
(0.0, 0.0)
|
||||||
};
|
};
|
||||||
|
|
@ -500,7 +502,8 @@ impl<'open> Window<'open> {
|
||||||
// Prevent window from becoming larger than the constrain rect.
|
// Prevent window from becoming larger than the constrain rect.
|
||||||
let constrain_rect = area.constrain_rect();
|
let constrain_rect = area.constrain_rect();
|
||||||
let max_width = constrain_rect.width();
|
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.x = resize.max_size.x.min(max_width);
|
||||||
resize.max_size.y = resize.max_size.y.min(max_height);
|
resize.max_size.y = resize.max_size.y.min(max_height);
|
||||||
}
|
}
|
||||||
|
|
@ -508,12 +511,18 @@ impl<'open> Window<'open> {
|
||||||
// First check for resize to avoid frame delay:
|
// First check for resize to avoid frame delay:
|
||||||
let last_frame_outer_rect = area.state().rect();
|
let last_frame_outer_rect = area.state().rect();
|
||||||
let resize_interaction = ctx.with_accessibility_parent(area.id(), || {
|
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()
|
let margins = window_frame.total_margin().sum()
|
||||||
+ vec2(0.0, title_bar_height);
|
+ vec2(0.0, title_bar_height_with_margin + title_content_spacing);
|
||||||
|
|
||||||
resize_response(
|
resize_response(
|
||||||
resize_interaction,
|
resize_interaction,
|
||||||
|
|
@ -523,6 +532,7 @@ impl<'open> Window<'open> {
|
||||||
&mut area,
|
&mut area,
|
||||||
resize_id,
|
resize_id,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut area_content_ui = area.content_ui(ctx);
|
let mut area_content_ui = area.content_ui(ctx);
|
||||||
if is_open {
|
if is_open {
|
||||||
|
|
@ -535,40 +545,43 @@ impl<'open> Window<'open> {
|
||||||
let content_inner = {
|
let content_inner = {
|
||||||
ctx.with_accessibility_parent(area.id(), || {
|
ctx.with_accessibility_parent(area.id(), || {
|
||||||
// BEGIN FRAME --------------------------------
|
// BEGIN FRAME --------------------------------
|
||||||
let frame_stroke = window_frame.stroke;
|
|
||||||
let mut frame = window_frame.begin(&mut area_content_ui);
|
let mut frame = window_frame.begin(&mut area_content_ui);
|
||||||
|
|
||||||
let show_close_button = open.is_some();
|
let show_close_button = open.is_some();
|
||||||
|
|
||||||
let where_to_put_header_background = &area_content_ui.painter().add(Shape::Noop);
|
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 = if with_title_bar {
|
||||||
let title_bar = TitleBar::new(
|
let title_bar = TitleBar::new(
|
||||||
&mut frame.content_ui,
|
&frame.content_ui,
|
||||||
title,
|
title,
|
||||||
show_close_button,
|
show_close_button,
|
||||||
&mut collapsing,
|
|
||||||
collapsible,
|
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)
|
Some(title_bar)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove item spacing after the title bar
|
let (content_inner, content_response) = collapsing
|
||||||
frame.content_ui.spacing_mut().item_spacing.y = 0.0;
|
|
||||||
|
|
||||||
let (content_inner, mut content_response) = collapsing
|
|
||||||
.show_body_unindented(&mut frame.content_ui, |ui| {
|
.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| {
|
resize.show(ui, |ui| {
|
||||||
if scroll.is_any_scroll_enabled() {
|
if scroll.is_any_scroll_enabled() {
|
||||||
scroll.show(ui, add_contents).inner
|
scroll.show(ui, add_contents).inner
|
||||||
|
|
@ -584,23 +597,18 @@ impl<'open> Window<'open> {
|
||||||
&area_content_ui,
|
&area_content_ui,
|
||||||
&possible,
|
&possible,
|
||||||
outer_rect,
|
outer_rect,
|
||||||
frame_stroke,
|
&window_frame,
|
||||||
window_frame.rounding,
|
|
||||||
resize_interaction,
|
resize_interaction,
|
||||||
);
|
);
|
||||||
|
|
||||||
// END FRAME --------------------------------
|
// END FRAME --------------------------------
|
||||||
|
|
||||||
if let Some(title_bar) = title_bar {
|
if let Some(mut title_bar) = title_bar {
|
||||||
let mut title_rect = Rect::from_min_size(
|
title_bar.inner_rect = outer_rect.shrink(window_frame.stroke.width);
|
||||||
outer_rect.min,
|
title_bar.inner_rect.max.y =
|
||||||
Vec2 {
|
title_bar.inner_rect.min.y + title_bar_height_with_margin;
|
||||||
x: outer_rect.size().x,
|
title_bar.inner_rect =
|
||||||
y: title_bar_height,
|
title_bar.inner_rect.round_to_pixels(ctx.pixels_per_point());
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
title_rect = title_rect.round_to_pixels(area_content_ui.pixels_per_point());
|
|
||||||
|
|
||||||
if on_top && area_content_ui.visuals().window_highlight_topmost {
|
if on_top && area_content_ui.visuals().window_highlight_topmost {
|
||||||
let mut round = window_frame.rounding;
|
let mut round = window_frame.rounding;
|
||||||
|
|
@ -612,18 +620,20 @@ impl<'open> Window<'open> {
|
||||||
|
|
||||||
area_content_ui.painter().set(
|
area_content_ui.painter().set(
|
||||||
*where_to_put_header_background,
|
*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 false {
|
||||||
if let Some(response) = &mut content_response {
|
ctx.debug_painter().debug_rect(
|
||||||
response.rect.min.y = outer_rect.min.y + title_bar_height;
|
title_bar.inner_rect,
|
||||||
|
Color32::LIGHT_BLUE,
|
||||||
|
"title_bar.rect",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
title_bar.ui(
|
title_bar.ui(
|
||||||
&mut area_content_ui,
|
&mut area_content_ui,
|
||||||
title_rect,
|
|
||||||
&content_response,
|
&content_response,
|
||||||
open,
|
open,
|
||||||
&mut collapsing,
|
&mut collapsing,
|
||||||
|
|
@ -653,12 +663,11 @@ fn paint_resize_corner(
|
||||||
ui: &Ui,
|
ui: &Ui,
|
||||||
possible: &PossibleInteractions,
|
possible: &PossibleInteractions,
|
||||||
outer_rect: Rect,
|
outer_rect: Rect,
|
||||||
stroke: impl Into<Stroke>,
|
window_frame: &Frame,
|
||||||
rounding: impl Into<Rounding>,
|
|
||||||
i: ResizeInteraction,
|
i: ResizeInteraction,
|
||||||
) {
|
) {
|
||||||
let inactive_stroke = stroke.into();
|
let rounding = window_frame.rounding;
|
||||||
let rounding = rounding.into();
|
|
||||||
let (corner, radius, corner_response) = if possible.resize_right && possible.resize_bottom {
|
let (corner, radius, corner_response) = if possible.resize_right && possible.resize_bottom {
|
||||||
(Align2::RIGHT_BOTTOM, rounding.se, i.right & i.bottom)
|
(Align2::RIGHT_BOTTOM, rounding.se, i.right & i.bottom)
|
||||||
} else if possible.resize_left && possible.resize_bottom {
|
} else if possible.resize_left && possible.resize_bottom {
|
||||||
|
|
@ -694,11 +703,12 @@ fn paint_resize_corner(
|
||||||
} else if corner_response.hover {
|
} else if corner_response.hover {
|
||||||
ui.visuals().widgets.hovered.fg_stroke
|
ui.visuals().widgets.hovered.fg_stroke
|
||||||
} else {
|
} 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_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
|
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);
|
crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke.color, corner);
|
||||||
}
|
}
|
||||||
|
|
@ -738,7 +748,11 @@ impl PossibleInteractions {
|
||||||
/// Resizing the window edges.
|
/// Resizing the window edges.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct ResizeInteraction {
|
struct ResizeInteraction {
|
||||||
start_rect: Rect,
|
/// Outer rect (outside the stroke)
|
||||||
|
outer_rect: Rect,
|
||||||
|
|
||||||
|
window_frame: Frame,
|
||||||
|
|
||||||
left: SideResponse,
|
left: SideResponse,
|
||||||
right: SideResponse,
|
right: SideResponse,
|
||||||
top: SideResponse,
|
top: SideResponse,
|
||||||
|
|
@ -835,13 +849,17 @@ fn resize_response(
|
||||||
ctx.memory_mut(|mem| mem.areas_mut().move_to_top(area_layer_id));
|
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> {
|
fn move_and_resize_window(ctx: &Context, interaction: &ResizeInteraction) -> Option<Rect> {
|
||||||
if !interaction.any_dragged() {
|
if !interaction.any_dragged() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointer_pos = ctx.input(|i| i.pointer.interact_pos())?;
|
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 {
|
if interaction.left.drag {
|
||||||
rect.min.x = pointer_pos.x;
|
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;
|
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())
|
Some(rect.round_ui())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -862,11 +883,13 @@ fn resize_interaction(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
possible: PossibleInteractions,
|
possible: PossibleInteractions,
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
rect: Rect,
|
outer_rect: Rect,
|
||||||
|
window_frame: Frame,
|
||||||
) -> ResizeInteraction {
|
) -> ResizeInteraction {
|
||||||
if !possible.resizable() {
|
if !possible.resizable() {
|
||||||
return ResizeInteraction {
|
return ResizeInteraction {
|
||||||
start_rect: rect,
|
outer_rect,
|
||||||
|
window_frame,
|
||||||
left: Default::default(),
|
left: Default::default(),
|
||||||
right: Default::default(),
|
right: Default::default(),
|
||||||
top: 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 side_response = |rect, id| {
|
||||||
let response = ctx.create_widget(
|
let response = ctx.create_widget(
|
||||||
WidgetRect {
|
WidgetRect {
|
||||||
|
|
@ -990,7 +1016,8 @@ fn resize_interaction(
|
||||||
}
|
}
|
||||||
|
|
||||||
let interaction = ResizeInteraction {
|
let interaction = ResizeInteraction {
|
||||||
start_rect: rect,
|
outer_rect,
|
||||||
|
window_frame,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
top,
|
top,
|
||||||
|
|
@ -1027,6 +1054,18 @@ fn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction)
|
||||||
}
|
}
|
||||||
|
|
||||||
let rounding = Roundingf::from(ui.visuals().window_rounding);
|
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 Rect { min, max } = rect;
|
||||||
|
|
||||||
let mut points = Vec::new();
|
let mut points = Vec::new();
|
||||||
|
|
@ -1083,51 +1122,46 @@ fn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction)
|
||||||
points.push(pos2(max.x, min.y + rounding.ne));
|
points.push(pos2(max.x, min.y + rounding.ne));
|
||||||
points.push(pos2(max.x, max.y - rounding.se));
|
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 {
|
struct TitleBar {
|
||||||
/// A title Id used for dragging windows
|
window_frame: Frame,
|
||||||
id: Id,
|
|
||||||
|
|
||||||
/// Prepared text in the title
|
/// Prepared text in the title
|
||||||
title_galley: Arc<Galley>,
|
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
|
/// Size of the title bar in an expanded state. This size become known only
|
||||||
/// after expanding window and painting its content
|
/// after expanding window and painting its content.
|
||||||
rect: Rect,
|
///
|
||||||
|
/// Does not include the stroke, nor the separator line between the title bar and the window contents.
|
||||||
|
inner_rect: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TitleBar {
|
impl TitleBar {
|
||||||
fn new(
|
fn new(
|
||||||
ui: &mut Ui,
|
ui: &Ui,
|
||||||
title: WidgetText,
|
title: WidgetText,
|
||||||
show_close_button: bool,
|
show_close_button: bool,
|
||||||
collapsing: &mut CollapsingState,
|
|
||||||
collapsible: bool,
|
collapsible: bool,
|
||||||
|
window_frame: Frame,
|
||||||
|
title_bar_height_with_margin: f32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let inner_response = ui.horizontal(|ui| {
|
if false {
|
||||||
let height = ui
|
ui.ctx()
|
||||||
.fonts(|fonts| title.font_height(fonts, ui.style()))
|
.debug_painter()
|
||||||
.max(ui.spacing().interact_size.y);
|
.debug_rect(ui.min_rect(), Color32::GREEN, "outer_min_rect");
|
||||||
ui.set_min_height(height);
|
}
|
||||||
|
|
||||||
|
let inner_height = title_bar_height_with_margin - window_frame.inner_margin.sum().y;
|
||||||
|
|
||||||
let item_spacing = ui.spacing().item_spacing;
|
let item_spacing = ui.spacing().item_spacing;
|
||||||
let button_size = Vec2::splat(ui.spacing().icon_width);
|
let button_size = Vec2::splat(ui.spacing().icon_width.at_most(inner_height));
|
||||||
|
|
||||||
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 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)
|
||||||
|
|
||||||
if collapsible {
|
|
||||||
ui.add_space(pad);
|
|
||||||
collapsing.show_default_button_with_size(ui, button_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
let title_galley = title.into_galley(
|
let title_galley = title.into_galley(
|
||||||
ui,
|
ui,
|
||||||
|
|
@ -1137,26 +1171,25 @@ impl TitleBar {
|
||||||
);
|
);
|
||||||
|
|
||||||
let minimum_width = if collapsible || show_close_button {
|
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):
|
// If at least one button is shown we make room for both buttons (since title should be centered):
|
||||||
2.0 * (pad + button_size.x + item_spacing.x) + title_galley.size().x
|
2.0 * (left_pad + button_size.x + item_spacing.x) + title_galley.size().x
|
||||||
} else {
|
} else {
|
||||||
pad + title_galley.size().x + pad
|
left_pad + title_galley.size().x + left_pad
|
||||||
};
|
};
|
||||||
let min_rect = Rect::from_min_size(ui.min_rect().min, vec2(minimum_width, height));
|
let min_inner_size = vec2(minimum_width, inner_height);
|
||||||
let id = ui.advance_cursor_after_rect(min_rect);
|
let min_rect = Rect::from_min_size(ui.min_rect().min, min_inner_size);
|
||||||
|
|
||||||
|
if false {
|
||||||
|
ui.ctx()
|
||||||
|
.debug_painter()
|
||||||
|
.debug_rect(min_rect, Color32::LIGHT_BLUE, "min_rect");
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
window_frame,
|
||||||
title_galley,
|
title_galley,
|
||||||
min_rect,
|
inner_rect: min_rect, // First estimate - will be refined later
|
||||||
rect: Rect::NAN, // Will be filled in later
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let title_bar = inner_response.inner;
|
|
||||||
let rect = inner_response.response.rect;
|
|
||||||
|
|
||||||
Self { rect, ..title_bar }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finishes painting of the title bar when the window content size already known.
|
/// 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
|
/// - `collapsible`: if `true`, double click on the title bar will be handled for a change
|
||||||
/// of `collapsing` state
|
/// of `collapsing` state
|
||||||
fn ui(
|
fn ui(
|
||||||
mut self,
|
self,
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
outer_rect: Rect,
|
|
||||||
content_response: &Option<Response>,
|
content_response: &Option<Response>,
|
||||||
open: Option<&mut bool>,
|
open: Option<&mut bool>,
|
||||||
collapsing: &mut CollapsingState,
|
collapsing: &mut CollapsingState,
|
||||||
collapsible: bool,
|
collapsible: bool,
|
||||||
) {
|
) {
|
||||||
if let Some(content_response) = &content_response {
|
let window_frame = self.window_frame;
|
||||||
// Now we know how large we got to be:
|
let title_inner_rect = self.inner_rect;
|
||||||
self.rect.max.x = self.rect.max.x.max(content_response.rect.max.x);
|
|
||||||
|
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 {
|
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 =
|
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();
|
let text_pos = text_pos - self.title_galley.rect.min.to_vec2();
|
||||||
ui.painter().galley(
|
ui.painter().galley(
|
||||||
text_pos,
|
text_pos,
|
||||||
|
|
@ -1205,22 +1255,35 @@ impl TitleBar {
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(content_response) = &content_response {
|
if let Some(content_response) = &content_response {
|
||||||
// paint separator between title and content:
|
// Paint separator between title and content:
|
||||||
let y = content_response.rect.top();
|
let content_rect = content_response.rect;
|
||||||
// let y = lerp(self.rect.bottom()..=content_response.rect.top(), 0.5);
|
if false {
|
||||||
let stroke = ui.visuals().widgets.noninteractive.bg_stroke;
|
ui.ctx()
|
||||||
// Workaround: To prevent border infringement,
|
.debug_painter()
|
||||||
// the 0.1 value should ideally be calculated using TessellationOptions::feathering_size_in_pixels
|
.debug_rect(content_rect, Color32::RED, "content_rect");
|
||||||
// or we could support selectively disabling feathering on line caps
|
}
|
||||||
let x_range = outer_rect.x_range().shrink(0.1);
|
let y = title_inner_rect.bottom() + window_frame.stroke.width / 2.0;
|
||||||
ui.painter().hline(x_range, y, stroke);
|
|
||||||
|
// 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:
|
// 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
|
if ui
|
||||||
.interact(double_click_rect, self.id, Sense::click())
|
.interact(double_click_rect, id, Sense::click())
|
||||||
.double_clicked()
|
.double_clicked()
|
||||||
&& collapsible
|
&& collapsible
|
||||||
{
|
{
|
||||||
|
|
@ -1234,16 +1297,12 @@ impl TitleBar {
|
||||||
/// The button is square and its size is determined by the
|
/// The button is square and its size is determined by the
|
||||||
/// [`crate::style::Spacing::icon_width`] setting.
|
/// [`crate::style::Spacing::icon_width`] setting.
|
||||||
fn close_button_ui(&self, ui: &mut Ui) -> Response {
|
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 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_center_size(button_center, button_size);
|
||||||
let button_rect = Rect::from_min_size(
|
let button_rect = button_rect.round_to_pixels(ui.pixels_per_point());
|
||||||
pos2(
|
|
||||||
self.rect.right() - pad - button_size.x,
|
|
||||||
self.rect.center().y - 0.5 * button_size.y,
|
|
||||||
),
|
|
||||||
button_size,
|
|
||||||
);
|
|
||||||
|
|
||||||
close_button(ui, button_rect)
|
close_button(ui, button_rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1771,12 +1771,14 @@ impl Ui {
|
||||||
/// Add extra space before the next widget.
|
/// Add extra space before the next widget.
|
||||||
///
|
///
|
||||||
/// The direction is dependent on the layout.
|
/// 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.
|
/// [`Self::min_rect`] will expand to contain the space.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_space(&mut self, amount: f32) {
|
pub fn add_space(&mut self, amount: f32) {
|
||||||
self.placer.advance_cursor(amount);
|
self.placer.advance_cursor(amount.round_ui());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show some text.
|
/// Show some text.
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,11 @@ pub struct FractalClockApp {
|
||||||
impl eframe::App for FractalClockApp {
|
impl eframe::App for FractalClockApp {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
egui::CentralPanel::default()
|
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| {
|
.show(ctx, |ui| {
|
||||||
self.fractal_clock
|
self.fractal_clock
|
||||||
.ui(ui, self.mock_time.or(Some(crate::seconds_since_midnight())));
|
.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;
|
let mut cmd = Command::Nothing;
|
||||||
egui::TopBottomPanel::top("wrap_app_top_bar")
|
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| {
|
.show(ctx, |ui| {
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
ui.visuals_mut().button_frame = false;
|
ui.visuals_mut().button_frame = false;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7c05cc3d48242e46a391af34cb56f72de7933bf2cead009b6cd477c21867a84e
|
oid sha256:4aeab31841dd95b5e0f4bd0af0c0ba49a862d50836dbafdf2172fbbab950c105
|
||||||
size 327802
|
size 327741
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:61212e30fe1fecf5891ddad6ac795df510bfad76b21a7a8a13aa024fdad6d05e
|
oid sha256:0e4a90792a9876da549f3d1da9b057a078400ad15db2cc6e35f4324851137d4e
|
||||||
size 93118
|
size 93115
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,18 @@ pub struct FrameDemo {
|
||||||
impl Default for FrameDemo {
|
impl Default for FrameDemo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
frame: egui::Frame {
|
frame: egui::Frame::new()
|
||||||
inner_margin: 12.0.into(),
|
.inner_margin(12)
|
||||||
outer_margin: 24.0.into(),
|
.outer_margin(24)
|
||||||
rounding: 14.0.into(),
|
.rounding(14)
|
||||||
shadow: egui::Shadow {
|
.shadow(egui::Shadow {
|
||||||
offset: [8, 12],
|
offset: [8, 12],
|
||||||
blur: 16,
|
blur: 16,
|
||||||
spread: 0,
|
spread: 0,
|
||||||
color: egui::Color32::from_black_alpha(180),
|
color: egui::Color32::from_black_alpha(180),
|
||||||
},
|
})
|
||||||
fill: egui::Color32::from_rgba_unmultiplied(97, 0, 255, 128),
|
.fill(egui::Color32::from_rgba_unmultiplied(97, 0, 255, 128))
|
||||||
stroke: egui::Stroke::new(1.0, egui::Color32::GRAY),
|
.stroke(egui::Stroke::new(1.0, egui::Color32::GRAY)),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::{Demo, View};
|
use super::{Demo, View};
|
||||||
|
|
||||||
use egui::{
|
use egui::{
|
||||||
vec2, Align, Checkbox, CollapsingHeader, Color32, Context, FontId, Frame, Resize, RichText,
|
vec2, Align, Checkbox, CollapsingHeader, Color32, Context, FontId, Resize, RichText, Sense,
|
||||||
Sense, Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window,
|
Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Showcase some ui code
|
/// Showcase some ui code
|
||||||
|
|
@ -512,11 +512,9 @@ fn ui_stack_demo(ui: &mut Ui) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
let stack = ui.stack().clone();
|
let stack = ui.stack().clone();
|
||||||
Frame {
|
egui::Frame::new()
|
||||||
inner_margin: ui.spacing().menu_margin,
|
.inner_margin(ui.spacing().menu_margin)
|
||||||
stroke: ui.visuals().widgets.noninteractive.bg_stroke,
|
.stroke(ui.visuals().widgets.noninteractive.bg_stroke)
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
egui_extras::TableBuilder::new(ui)
|
egui_extras::TableBuilder::new(ui)
|
||||||
.column(egui_extras::Column::auto())
|
.column(egui_extras::Column::auto())
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:cf83bead834ec8f88d74b32ae6331715e8c6df183e007e2a16004c019534a30f
|
oid sha256:d4cfd5191dc7046a782ef2350dc8e0547d2702182badcb15b6b928ce077b76c1
|
||||||
size 31810
|
size 32154
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0a1099b85a1aaf20f3f1e091bc68259f811737feaefdfcc12acd067eca8f9117
|
oid sha256:e89c730b462c2b60b90f2ac15fe9576e878a4906c223317c51344a0ec2b6d993
|
||||||
size 27083
|
size 27564
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6969c6da67ea6cc7ebbbd7a2cc1cb13d4720befe28126367cbf2b2679d037674
|
oid sha256:ea2c944af8bc1be42ec7c00be58dfaa23c92bca8957eda94f2ff10f5b4242562
|
||||||
size 82363
|
size 83358
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:332c2af36873d8ccccb36c08fd2e475dc1f18454a3090a851c0889395d4f364f
|
oid sha256:c401ff91fff4051042528d398d2b2270a4ae924570e6332cf8f2c6774c845160
|
||||||
size 11518
|
size 11826
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d0d0b1b4d2c4b624904250bc8d6870559f0179e3f7f2d6dc4a4ff256df356237
|
oid sha256:7efc1ff3e4e5bfd4216394f94ee7486c272a9ca1c980789f4ad143f89b0a7103
|
||||||
size 20626
|
size 21073
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d5fe6166bb8cd5fae0899957e968312b9229a79e423a5d154eda483a149b264d
|
oid sha256:d9c48cf928a17dd0980ba086aa004bde3a0040dcb82752d138c1df34f1ef3d2f
|
||||||
size 20831
|
size 21167
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b71e1d109f90e017644dd20b9d84d81e3a6d5195afbd01ba86c85fa248c8b5c5
|
oid sha256:be0f96c700b7662aab5098f8412dae3676116eeed65e70f6b295dd3375b329d0
|
||||||
size 10703
|
size 10968
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e3c9ba9064f44a4a14405f53316c1c602184caf16cb584d7c1f1912fe59f85ab
|
oid sha256:dc69c76eaa121e9e7782cfbbb68b5a23004d79862bae4af2e3ca3a29eff04bea
|
||||||
size 135712
|
size 136467
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:097bd512dd71c17370f6e374312c19e7ab6845f151b3c3565f2a0790b61ee7ba
|
oid sha256:23187a9fb12a3ab7df4e2321aa25b493559923d61e82802f843ee29dcd932f7b
|
||||||
size 24413
|
size 24985
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2bdf54573a6b0d2fedd90314f91dd7de22dd13709e8dd699b37ef8935b6adda5
|
oid sha256:7f433f3e8bff38a0aafd7e6cba5c5efe1abf484550a6f9e90008f8f5ea891497
|
||||||
size 17785
|
size 18113
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d6328c86b70f3e24aaf87db200d13dfd0baa787dd8431e47621457937f8f5021
|
oid sha256:e5105ecf77852412c0dd904b96f0fec752f22e416df9932df4499d6d5a776f46
|
||||||
size 22552
|
size 22865
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:afb57dc0bb8ff839138e36b8780136e0c8da55ff380832538fae0394143807c0
|
oid sha256:ebf0403bd599e5c00c2831f9c4032e8d20420212c9cd7fa875f1ae1cbbc8d3a7
|
||||||
size 65321
|
size 65902
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:403fc1d08d79168bc9f7a08ac7f363f2df916547f08311838edfda8a499a9b2d
|
oid sha256:4e690dc73873ab75c030d3c0238e9d5b840f976dd8f4882dc1e930024d217838
|
||||||
size 32879
|
size 33323
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:cbd490a15377bdd4fd3272f3cd126cbc4fb9ed2f6f84edcbba03dd30dc3f3d99
|
oid sha256:e760210371dbf2a197f96a78d01b7480f0ae05d46bbb4e642276b2eb30847ec2
|
||||||
size 36780
|
size 37075
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c31b3998817e345b12b037d7f8bec7641f77d0c7eab7da9a746b7b51c9efc8fb
|
oid sha256:fd02b208d0e4e306bbc9a54f25f5a3d20875a12182cef3224e6daa309b6cf453
|
||||||
size 17531
|
size 17898
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4e8963c3ecd0e74fe9641d87101742a0d45c82a582d70e30eb36bc835f5aac06
|
oid sha256:341958da648a7db3374c4337cf057ae8e81c08c4a6de7e4f1cbe9c5b049f2e62
|
||||||
size 25330
|
size 25727
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:88b3a50b481942630b5804c60227f23b719dc7e3eb6dbe432c2448cb69332def
|
oid sha256:8934cff7203d19b38df9d91729091ff5d1ad6c8d445fd9c1cb62b6df1bb8cb80
|
||||||
size 262141
|
size 263547
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4d5d628b54b28eccac0d9ef21bbdabace0cdf507898707956943e2259df846ca
|
oid sha256:5d61f58138798d701bb8dda2c3240eef69eb350df3168fb3aa4148e4fef3f77a
|
||||||
size 23741
|
size 24077
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8763a8f8b4242cbe589cd3282dc5f4b32a84b4e0416fb8072dfefb7879a5d4f6
|
oid sha256:e4006e93663d02fe0f4485d2c163ab2b6feded787bee87ea15616fc0b36136d0
|
||||||
size 187982
|
size 188875
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3d61088bf1f992467e8396ac89407fa158d6f44e2d2d196778247f3ff18cb893
|
oid sha256:70b170ba7b8e51d9d9f766d7ce25068fa4265c4127e729af4f1adaacbb745d19
|
||||||
size 119759
|
size 120947
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:79ebaf9cccd19da2923507b5a553a23edc67382ef59e4b71f01d3bd6cc913539
|
oid sha256:157353a8c9bcb638a8be485489e4a232f348eae3cc4ceefe227d7970c7d1f8b3
|
||||||
size 25829
|
size 26256
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:06cbf13841e6ac5fbc57bdae6f5ad9d19718193c344420dedcc0e9d7ed2b8ba9
|
oid sha256:cf0ddd39a45519dcf9027f691e856921c736d18e2eeafd16f0e086720121b6a7
|
||||||
size 71590
|
size 72286
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:35e66f211c0b30a684371b520c46dbe4f9d5b6835e053a4eb65f492dd66a9e6c
|
oid sha256:914d37e326087f770994bcf3867a27d88050c57887a2b42c68414d311fa96003
|
||||||
size 67288
|
size 67698
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:fa9ee8631bfe433ee6fad1fb1651fd6b63e2fb3fbc5f321a5410f7266dc16d09
|
oid sha256:b0fdf8ce329883450e071e4733c3904577999d18ac61c922c7caacbec09dfda7
|
||||||
size 21296
|
size 21661
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6475702b1bf2c65fb5196928a8934ade647a6053d6902a836e3d69cb7920091e
|
oid sha256:375b71a8ac5b0e48f3c67a089ef0e8a4fd17f8eb21fa535422099c29c2747e27
|
||||||
size 59874
|
size 59991
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9d8daaec0c58709298a4594b7b2aa935aa2de327e6b71bd7417c2ba3a6eb060c
|
oid sha256:aa96b1e3733e4af94a6cb6ec765c3f3581df2175e75831eb00bd42df2e7a2708
|
||||||
size 13020
|
size 13285
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:29d8d859a8cb11e4b390d45c658ed8ff191c2e542909e12f2385c0cba62baa2d
|
oid sha256:18880dfaf5d198876c4db97ebd6313d59755a3e8298567f2b2fa91dcc21699c5
|
||||||
size 35109
|
size 35607
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:17217e600d8a85ec00ecb11f0e5fe69496b30bbf715cc86785cec6e18b8c2fa1
|
oid sha256:d626b310439bff13487548bbba8b516886c13049824a7f5dd902f6dffb3c5ba4
|
||||||
size 48158
|
size 48234
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:fac50d2327a9e5e99989dd81d5644db86031b91b9c5c68fc17b5ef53ae655048
|
oid sha256:80a2968e211c83639b60e84b805f1327fb37b87144cada672a403c7e92ace8a8
|
||||||
size 47970
|
size 48066
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e5e829257b742194742429be0efd326be17ff7f5b9b16f9df58df21e899320bd
|
oid sha256:86df5dc4b4ddd6f44226242b6d9b5e9f2aacd45193ae9f784fb5084a7a509e0b
|
||||||
size 43963
|
size 43987
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5da064332c669a860a92a34b101f23e28026d4f07948f7c3e9a40e611f5e284f
|
oid sha256:1138bbc3b7e73cccd555f0fd58c27a5bda4d84484fdc1bd5223fc9802d0c5328
|
||||||
size 43986
|
size 44089
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ impl<'a, State> AppKind<'a, State> {
|
||||||
sizing_pass: bool,
|
sizing_pass: bool,
|
||||||
) -> egui::Response {
|
) -> egui::Response {
|
||||||
egui::CentralPanel::default()
|
egui::CentralPanel::default()
|
||||||
.frame(Frame::none())
|
.frame(Frame::NONE)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
let mut builder = egui::UiBuilder::new();
|
let mut builder = egui::UiBuilder::new();
|
||||||
if sizing_pass {
|
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)) {
|
fn custom_window_frame(ctx: &egui::Context, title: &str, add_contents: impl FnOnce(&mut egui::Ui)) {
|
||||||
use egui::{CentralPanel, UiBuilder};
|
use egui::{CentralPanel, UiBuilder};
|
||||||
|
|
||||||
let panel_frame = egui::Frame {
|
let panel_frame = egui::Frame::new()
|
||||||
fill: ctx.style().visuals.window_fill(),
|
.fill(ctx.style().visuals.window_fill())
|
||||||
rounding: 10.0.into(),
|
.rounding(10)
|
||||||
stroke: ctx.style().visuals.widgets.noninteractive.fg_stroke,
|
.stroke(ctx.style().visuals.widgets.noninteractive.fg_stroke)
|
||||||
outer_margin: 1.0.into(), // so the stroke is within the bounds
|
.outer_margin(1); // so the stroke is within the bounds
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
CentralPanel::default().frame(panel_frame).show(ctx, |ui| {
|
CentralPanel::default().frame(panel_frame).show(ctx, |ui| {
|
||||||
let app_rect = ui.max_rect();
|
let app_rect = ui.max_rect();
|
||||||
|
|
|
||||||
|
|
@ -62,22 +62,18 @@ impl eframe::App for MyApp {
|
||||||
|
|
||||||
// nested frames test
|
// nested frames test
|
||||||
ui.add_space(20.0);
|
ui.add_space(20.0);
|
||||||
egui::Frame {
|
egui::Frame::new()
|
||||||
stroke: ui.visuals().noninteractive().bg_stroke,
|
.stroke(ui.visuals().noninteractive().bg_stroke)
|
||||||
inner_margin: egui::Margin::same(4),
|
.inner_margin(4)
|
||||||
outer_margin: egui::Margin::same(4),
|
.outer_margin(4)
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
full_span_widget(ui, false);
|
full_span_widget(ui, false);
|
||||||
stack_ui(ui);
|
stack_ui(ui);
|
||||||
|
|
||||||
egui::Frame {
|
egui::Frame::new()
|
||||||
stroke: ui.visuals().noninteractive().bg_stroke,
|
.stroke(ui.visuals().noninteractive().bg_stroke)
|
||||||
inner_margin: egui::Margin::same(8),
|
.inner_margin(8)
|
||||||
outer_margin: egui::Margin::same(6),
|
.outer_margin(6)
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
full_span_widget(ui, false);
|
full_span_widget(ui, false);
|
||||||
stack_ui(ui);
|
stack_ui(ui);
|
||||||
|
|
@ -126,11 +122,9 @@ impl eframe::App for MyApp {
|
||||||
// Ui nesting test
|
// Ui nesting test
|
||||||
ui.add_space(20.0);
|
ui.add_space(20.0);
|
||||||
ui.label("UI nesting test:");
|
ui.label("UI nesting test:");
|
||||||
egui::Frame {
|
egui::Frame::new()
|
||||||
stroke: ui.visuals().noninteractive().bg_stroke,
|
.stroke(ui.visuals().noninteractive().bg_stroke)
|
||||||
inner_margin: egui::Margin::same(4),
|
.inner_margin(4)
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
|
|
@ -265,11 +259,9 @@ fn stack_ui(ui: &mut egui::Ui) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stack_ui_impl(ui: &mut egui::Ui, stack: &egui::UiStack) {
|
fn stack_ui_impl(ui: &mut egui::Ui, stack: &egui::UiStack) {
|
||||||
egui::Frame {
|
egui::Frame::new()
|
||||||
stroke: ui.style().noninteractive().fg_stroke,
|
.stroke(ui.style().noninteractive().fg_stroke)
|
||||||
inner_margin: egui::Margin::same(4),
|
.inner_margin(4)
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue