From 5c720e6009a1e2528e6011e0f702635eb166b207 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Wed, 13 Dec 2023 08:23:20 -0800 Subject: [PATCH] Align + Overlay + Shadow fixes - Blur no longer expands the shadow geometry, but instead is clamped to avoid overlapping drawing calls. - Overlay now handles hit tests correctly with regards to the original relative widget. - Align was using an Into conversion that wasn't actually correct, causing the contents to not actually get aligned in some situations. --- examples/container-shadow.rs | 12 +++++--- examples/containers.rs | 2 +- examples/overlays.rs | 21 +++++++++---- src/widget.rs | 11 +------ src/widgets/align.rs | 12 +++++--- src/widgets/container.rs | 59 ++++++++++++++++++++++-------------- src/widgets/layers.rs | 6 +--- 7 files changed, 69 insertions(+), 54 deletions(-) diff --git a/examples/container-shadow.rs b/examples/container-shadow.rs index 76b2b6a..4cc1062 100644 --- a/examples/container-shadow.rs +++ b/examples/container-shadow.rs @@ -4,7 +4,6 @@ use gooey::value::{Dynamic, MapEachCloned}; use gooey::widget::MakeWidget; use gooey::widgets::container::ContainerShadow; use gooey::widgets::slider::Slidable; -use gooey::widgets::Space; use gooey::Run; use kludgine::figures::units::Lp; use kludgine::figures::{Point, Size}; @@ -63,15 +62,18 @@ fn main() -> gooey::Result { .and( "Preview" .h3() - .align_left() .and( - Space::clear() + "Hello, World!" .size(Size::squared(Lp::inches(2))) .contain() .shadow(shadow) - .with(&CornerRadius, corners), + .with(&CornerRadius, corners) + .centered() + .contain() + .expand(), ) - .into_rows(), + .into_rows() + .expand(), ) .into_columns() .expand() diff --git a/examples/containers.rs b/examples/containers.rs index 94d52ab..2ed377e 100644 --- a/examples/containers.rs +++ b/examples/containers.rs @@ -59,5 +59,5 @@ fn set_of_containers(repeat: usize, theme_mode: Dynamic) -> WidgetIns } fn drop_shadow() -> ContainerShadow { - ContainerShadow::new(Point::new(Lp::ZERO, Lp::mm(1))).spread(Lp::mm_f(1.)) + ContainerShadow::new(Point::new(Lp::ZERO, Lp::mm(1))).spread(Lp::mm(1)) } diff --git a/examples/overlays.rs b/examples/overlays.rs index 1335140..3b27225 100644 --- a/examples/overlays.rs +++ b/examples/overlays.rs @@ -1,8 +1,11 @@ use std::panic::UnwindSafe; use gooey::widget::{MakeWidget, MakeWidgetWithId, WidgetTag}; +use gooey::widgets::container::ContainerShadow; use gooey::widgets::layers::{OverlayBuilder, OverlayLayer}; use gooey::Run; +use kludgine::figures::units::Lp; +use kludgine::figures::Point; use kludgine::Color; use rand::{thread_rng, Rng}; @@ -31,12 +34,18 @@ fn test_widget(overlay: &OverlayLayer, is_root: bool) -> impl MakeWidget { .contain(); if !is_root { - buttons = buttons.background_color(Color::new( - thread_rng().gen(), - thread_rng().gen(), - thread_rng().gen(), - 255, - )) + buttons = buttons + .background_color(Color::new( + thread_rng().gen(), + thread_rng().gen(), + thread_rng().gen(), + 255, + )) + .shadow( + ContainerShadow::new(Point::new(Lp::ZERO, Lp::mm(2))) + .blur_radius(Lp::mm(1)) + .spread(Lp::mm(1)), + ); } buttons.pad().make_with_id(my_tag) diff --git a/src/widget.rs b/src/widget.rs index c3dd010..3bb25cd 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -260,19 +260,10 @@ pub enum RootBehavior { pub struct WrappedLayout { /// The region the child widget occupies within its parent. pub child: Rect, - /// The size the wrapper widget should report as.q + /// The size the wrapper widget should report as. pub size: Size, } -impl From> for WrappedLayout { - fn from(child: Rect) -> Self { - WrappedLayout { - child, - size: child.size.into_unsigned(), - } - } -} - impl From> for WrappedLayout { fn from(size: Size) -> Self { WrappedLayout { diff --git a/src/widgets/align.rs b/src/widgets/align.rs index 403f883..11a0e4e 100644 --- a/src/widgets/align.rs +++ b/src/widgets/align.rs @@ -190,11 +190,13 @@ impl WrapperWidget for Align { ) -> WrappedLayout { let layout = self.measure(available_space, context); - Rect::new( - Point::new(layout.margin.left, layout.margin.top).into_signed(), - layout.content.into_signed(), - ) - .into() + WrappedLayout { + child: Rect::new( + Point::new(layout.margin.left, layout.margin.top).into_signed(), + layout.content.into_signed(), + ), + size: layout.content + layout.margin.size(), + } } } diff --git a/src/widgets/container.rs b/src/widgets/container.rs index 61cd624..41a2400 100644 --- a/src/widgets/container.rs +++ b/src/widgets/container.rs @@ -225,7 +225,7 @@ impl Widget for Container { // check if the shadow would be obscured before we try to draw it. if child_area.origin != Point::ZERO || child_size != context.gfx.region().size { - render_shadow(&child_area, &corner_radii, &shadow, background, context); + render_shadow(&child_area, corner_radii, &shadow, background, context); } context.gfx.draw_shape(&Shape::filled_round_rect( @@ -246,8 +246,33 @@ impl Widget for Container { ) -> Size { let child = self.child.mounted(context); - let padding = self.padding(context).into_upx(context.gfx.scale()); + let corner_radii = context.get(&CornerRadius).into_upx(context.gfx.scale()); + + let max_space = available_space.map(ConstraintLimit::max); + let min_dimension = max_space.width.min(max_space.height); + let max_corner_radii = min_dimension / 2; + + let corner_radii = corner_radii.map(|r| r.min(max_corner_radii)); + + let mut padding = self.padding(context).into_upx(context.gfx.scale()); + padding.left = padding + .left + .max(corner_radii.top_left / std::f32::consts::PI) + .max(corner_radii.bottom_left / std::f32::consts::PI); + padding.right = padding + .right + .max(corner_radii.top_right / std::f32::consts::PI) + .max(corner_radii.bottom_right / std::f32::consts::PI); + padding.top = padding + .top + .max(corner_radii.top_right / std::f32::consts::PI) + .max(corner_radii.top_left / std::f32::consts::PI); + padding.bottom = padding + .bottom + .max(corner_radii.bottom_right / std::f32::consts::PI) + .max(corner_radii.bottom_left / std::f32::consts::PI); let padding_amount = padding.size(); + let shadow = self.effective_shadow(context).into_px(context.gfx.scale()); let shadow_spread = shadow.spread.into_unsigned(); @@ -266,21 +291,7 @@ impl Widget for Container { .into_signed(), ); - let corner_radii = context.get(&CornerRadius).into_upx(context.gfx.scale()); - let max_corner = corner_radii - .top_left - .max(corner_radii.top_right) - .max(corner_radii.bottom_left) - .max(corner_radii.bottom_right); - let max_blur = (child_size - .width - .min(child_size.height) - .saturating_sub(shadow.spread.into_unsigned()) - / 2) - .saturating_sub(max_corner); - let blur_overage = shadow.blur_radius.into_unsigned().saturating_sub(max_blur); - - child_size + padding_amount + child_shadow_offset_amount + shadow_spread * 2 + blur_overage + child_size + padding_amount + child_shadow_offset_amount + shadow_spread * 2 } fn root_behavior( @@ -320,7 +331,7 @@ impl Widget for Container { #[allow(clippy::too_many_lines)] fn render_shadow( child_area: &Rect, - corner_radii: &CornerRadii, + mut corner_radii: CornerRadii, shadow: &ContainerShadow, background: Color, context: &mut GraphicsContext<'_, '_, '_, '_, '_>, @@ -329,15 +340,19 @@ fn render_shadow( let shadow_color = shadow_color.with_alpha_f32(shadow_color.alpha_f32() * background.alpha_f32()); + let min_dimension = child_area.size.width.min(child_area.size.height); + let max_corner_radii = min_dimension / 2; + + corner_radii = corner_radii.map(|r| r.min(max_corner_radii)); + let max_corner = corner_radii .top_left .max(corner_radii.top_right) .max(corner_radii.bottom_left) .max(corner_radii.bottom_right); - let max_blur = - (child_area.size.width.min(child_area.size.height) - shadow.spread) / 2 - max_corner; - let blur = shadow.blur_radius.min(max_blur); + let max_blur = min_dimension / 2 - max_corner; + let blur = shadow.blur_radius.min(max_blur).max(Px::ZERO); let gradient_size = shadow.spread + blur; if gradient_size > 0 { @@ -544,7 +559,7 @@ fn render_shadow( } else { context.gfx.draw_shape(&Shape::filled_round_rect( Rect::new(shadow.offset.max(Point::ZERO), child_area.size), - *corner_radii, + corner_radii, shadow_color, )); } diff --git a/src/widgets/layers.rs b/src/widgets/layers.rs index eac04c4..e407693 100644 --- a/src/widgets/layers.rs +++ b/src/widgets/layers.rs @@ -250,11 +250,7 @@ impl Widget for OverlayLayer { fn hit_test(&mut self, location: Point, context: &mut EventContext<'_, '_>) -> bool { let state = self.state.lock(); - if let Some(index) = state.test_point(location, false, context) { - index > 0 - } else { - !(state.overlays.is_empty() || state.point_is_in_root_relative(location, context)) - } + state.test_point(location, false, context).is_some() } fn hover(