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.
This commit is contained in:
Jonathan Johnson 2023-12-13 08:23:20 -08:00
parent 79a09ee614
commit 5c720e6009
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
7 changed files with 69 additions and 54 deletions

View file

@ -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()

View file

@ -59,5 +59,5 @@ fn set_of_containers(repeat: usize, theme_mode: Dynamic<ThemeMode>) -> WidgetIns
}
fn drop_shadow() -> ContainerShadow<Lp> {
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))
}

View file

@ -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)

View file

@ -260,19 +260,10 @@ pub enum RootBehavior {
pub struct WrappedLayout {
/// The region the child widget occupies within its parent.
pub child: Rect<Px>,
/// The size the wrapper widget should report as.q
/// The size the wrapper widget should report as.
pub size: Size<UPx>,
}
impl From<Rect<Px>> for WrappedLayout {
fn from(child: Rect<Px>) -> Self {
WrappedLayout {
child,
size: child.size.into_unsigned(),
}
}
}
impl From<Size<Px>> for WrappedLayout {
fn from(size: Size<Px>) -> Self {
WrappedLayout {

View file

@ -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(),
}
}
}

View file

@ -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<UPx> {
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<Px>,
corner_radii: &CornerRadii<Px>,
mut corner_radii: CornerRadii<Px>,
shadow: &ContainerShadow<Px>,
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,
));
}

View file

@ -250,11 +250,7 @@ impl Widget for OverlayLayer {
fn hit_test(&mut self, location: Point<Px>, 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(