Skip to content

Commit

Permalink
feat(core): 🎸 Added WrapRender to combines render widget with its c…
Browse files Browse the repository at this point in the history
…hild

Reimplemented `HAlignWidget`, `VAlignWidget`, `RelativeAnchor`,
`BoxDecoration`, `ConstrainedBox`, `IgnorePoint`, `Opacity`, `Padding`,
`TransformWidget`, and `VisibilityRender` as `WrapRender`.
  • Loading branch information
M-Adoo committed Sep 18, 2024
1 parent fc1da9b commit 3332e21
Show file tree
Hide file tree
Showing 45 changed files with 513 additions and 323 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

### Features

- **core:: Added `StateWriter::into_render` to covert writer to reader if no other writer exist. (#pr @M-Adoo)
- **core**: Added `WrapRender` for a render widget that combines with its child as a single widget tree node. (#626 @M-Adoo)
- **core:: Added `StateWriter::into_render` to covert writer to reader if no other writer exist. (#626 @M-Adoo)

### Changed

- **core**: Reimplemented `HAlignWidget`, `VAlignWidget`, `RelativeAnchor`, `BoxDecoration`, `ConstrainedBox`, `IgnorePoint`, `Opacity`, `Padding`, `TransformWidget`, and `VisibilityRender` as `WrapRender`. (#626 @M-Adoo)


### Fixed

Expand Down
91 changes: 42 additions & 49 deletions core/src/builtin_widgets/align.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::prelude::*;
use crate::{prelude::*, wrap_render::WrapRender};

/// A enum that describe how widget align to its box.
#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -54,13 +54,13 @@ pub enum VAlign {
}

/// A widget that align its child in x-axis, base on child's width.
#[derive(SingleChild, Default)]
#[derive(Default)]
pub struct HAlignWidget {
pub h_align: HAlign,
}

/// A widget that align its child in y-axis, base on child's height.
#[derive(SingleChild, Default)]
#[derive(Default)]
pub struct VAlignWidget {
pub v_align: VAlign,
}
Expand All @@ -77,51 +77,50 @@ impl Declare for VAlignWidget {
fn declarer() -> Self::Builder { FatObj::new(()) }
}

impl Render for HAlignWidget {
fn perform_layout(&self, mut clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
impl<'c> ComposeChild<'c> for HAlignWidget {
type Child = Widget<'c>;

fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
WrapRender::combine_child(this, child)
}
}

impl<'c> ComposeChild<'c> for VAlignWidget {
type Child = Widget<'c>;

fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
WrapRender::combine_child(this, child)
}
}

impl WrapRender for HAlignWidget {
fn perform_layout(&self, mut clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {
let align: Align = self.h_align.into();
if align == Align::Stretch {
clamp.min.width = clamp.max.width;
} else {
clamp.min.width = 0.;
}
let child = ctx.assert_single_child();
let child_size = ctx.perform_child_layout(child, clamp);
let box_width = clamp.max.width;
let x = align.align_value(child_size.width, box_width);
ctx.update_position(child, Point::new(x, 0.));
Size::new(box_width, child_size.height)
}

fn paint(&self, _: &mut PaintingCtx) {}

#[inline]
fn hit_test(&self, _: &HitTestCtx, _: Point) -> HitTest {
HitTest { hit: false, can_hit_child: true }
let child_size = host.perform_layout(clamp, ctx);
let x = align.align_value(child_size.width, clamp.max.width);
ctx.update_position(ctx.widget_id(), Point::new(x, 0.));
clamp.clamp(child_size)
}
}

impl Render for VAlignWidget {
fn perform_layout(&self, mut clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
impl WrapRender for VAlignWidget {
fn perform_layout(&self, mut clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {
let align: Align = self.v_align.into();
if align == Align::Stretch {
clamp.min.height = clamp.max.height;
} else {
clamp.min.height = 0.;
}
let child = ctx.assert_single_child();
let child_size = ctx.perform_child_layout(child, clamp);
let box_height = clamp.max.height;
let y = align.align_value(child_size.height, box_height);
ctx.update_position(child, Point::new(0., y));
Size::new(child_size.width, box_height)
}

fn paint(&self, _: &mut PaintingCtx) {}

#[inline]
fn hit_test(&self, _: &HitTestCtx, _: Point) -> HitTest {
HitTest { hit: false, can_hit_child: true }
let child_size = host.perform_layout(clamp, ctx);
let y = align.align_value(child_size.height, clamp.max.height);
ctx.update_position(ctx.widget_id(), Point::new(0., y));
clamp.clamp(child_size)
}
}

Expand Down Expand Up @@ -181,34 +180,32 @@ mod tests {
widget_layout_test!(
left_align,
WidgetTester::new(h_align(HAlign::Left)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(100., 10.)),
LayoutCase::new(&[0, 0]).with_size(CHILD_SIZE)
LayoutCase::default()
.with_size(CHILD_SIZE)
.with_x(0.)
);

widget_layout_test!(
h_center_align,
WidgetTester::new(h_align(HAlign::Center)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(100., 10.)),
LayoutCase::new(&[0, 0])
LayoutCase::default()
.with_size(CHILD_SIZE)
.with_x(45.)
);

widget_layout_test!(
right_align,
WidgetTester::new(h_align(HAlign::Right)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(100., 10.)),
LayoutCase::new(&[0, 0])
LayoutCase::default()
.with_size(CHILD_SIZE)
.with_x(90.)
);

widget_layout_test!(
h_stretch_algin,
WidgetTester::new(h_align(HAlign::Stretch)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(100., 10.)),
LayoutCase::new(&[0, 0])
.with_size(Size::new(100., 10.))
LayoutCase::default()
.with_size(Size::new(WND_SIZE.width, 10.))
.with_x(0.)
);

Expand All @@ -225,34 +222,30 @@ mod tests {
widget_layout_test!(
top_align,
WidgetTester::new(v_align(VAlign::Top)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(10., 100.)),
LayoutCase::new(&[0, 0])
LayoutCase::default()
.with_size(CHILD_SIZE)
.with_y(0.)
);

widget_layout_test!(
v_center_align,
WidgetTester::new(v_align(VAlign::Center)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(10., 100.)),
LayoutCase::new(&[0, 0])
LayoutCase::default()
.with_size(CHILD_SIZE)
.with_y(45.)
);

widget_layout_test!(
bottom_align,
WidgetTester::new(v_align(VAlign::Bottom)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(10., 100.)),
LayoutCase::new(&[0, 0])
LayoutCase::default()
.with_size(CHILD_SIZE)
.with_y(90.)
);

widget_layout_test!(
v_stretch_align,
WidgetTester::new(v_align(VAlign::Stretch)).with_wnd_size(WND_SIZE),
LayoutCase::default().with_size(Size::new(10., 100.)),
LayoutCase::new(&[0, 0]).with_size(Size::new(10., 100.))
LayoutCase::default().with_size(Size::new(10., WND_SIZE.height))
);
}
42 changes: 26 additions & 16 deletions core/src/builtin_widgets/anchor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::prelude::*;
use crate::{prelude::*, wrap_render::WrapRender};

/// Specifies the horizontal position you want to anchor the widget.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -154,8 +154,11 @@ impl Anchor {
pub fn right_bottom(x: f32, y: f32) -> Self { Self::new(HAnchor::Right(x), VAnchor::Bottom(y)) }
}

/// Widget use to anchor child constraints relative to parent widget.
#[derive(SingleChild, Default)]
/// This widget is used to anchor child constraints relative to the parent
/// widget. It's important to note that if you anchor the child widget outside
/// of its parent, it may become unable to click, so ensure there is ample space
/// within the parent.
#[derive(Default)]
pub struct RelativeAnchor {
pub anchor: Anchor,
}
Expand All @@ -166,10 +169,17 @@ impl Declare for RelativeAnchor {
fn declarer() -> Self::Builder { FatObj::new(()) }
}

impl Render for RelativeAnchor {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
let child = ctx.assert_single_child();
let child_size = ctx.perform_child_layout(child, clamp);
impl<'c> ComposeChild<'c> for RelativeAnchor {
type Child = Widget<'c>;

fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
WrapRender::combine_child(this, child)
}
}

impl WrapRender for RelativeAnchor {
fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {
let child_size = host.perform_layout(clamp, ctx);

let Anchor { x, y } = self.anchor;
let x = x
Expand All @@ -185,14 +195,14 @@ impl Render for RelativeAnchor {
})
.unwrap_or_default();

ctx.update_position(child, Point::new(x, y));
ctx.update_position(ctx.widget_id(), Point::new(x, y));
child_size
}
}

fn paint(&self, _: &mut PaintingCtx) {}

fn hit_test(&self, _: &HitTestCtx, _: Point) -> HitTest {
HitTest { hit: false, can_hit_child: true }
impl From<Point> for Anchor {
fn from(value: Point) -> Self {
Anchor { x: Some(HAnchor::Left(value.x)), y: Some(VAnchor::Top(value.y)) }
}
}

Expand All @@ -214,24 +224,24 @@ mod test {
widget_layout_test!(
pixel_left_top,
widget_tester(Anchor::left_top(1., 1.)),
LayoutCase::new(&[0, 0]).with_pos(Point::new(1., 1.))
LayoutCase::default().with_pos(Point::new(1., 1.))
);

widget_layout_test!(
pixel_left_bottom,
widget_tester(Anchor::left_bottom(1., 1.)),
LayoutCase::new(&[0, 0]).with_pos((1., 49.).into())
LayoutCase::default().with_pos((1., 49.).into())
);

widget_layout_test!(
pixel_top_right,
widget_tester(Anchor::right_top(1., 1.)),
LayoutCase::new(&[0, 0]).with_pos((49., 1.).into())
LayoutCase::default().with_pos((49., 1.).into())
);

widget_layout_test!(
pixel_bottom_right,
widget_tester(Anchor::right_bottom(1., 1.)),
LayoutCase::new(&[0, 0]).with_pos((49., 49.).into())
LayoutCase::default().with_pos((49., 49.).into())
);
}
23 changes: 16 additions & 7 deletions core/src/builtin_widgets/box_decoration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::prelude::*;
use crate::{prelude::*, wrap_render::WrapRender};

/// The BoxDecoration provides a variety of ways to draw a box.
#[derive(SingleChild, Default, Clone)]
#[derive(Default, Clone)]
pub struct BoxDecoration {
/// The background of the box.
pub background: Option<Brush>,
Expand Down Expand Up @@ -32,18 +32,26 @@ pub struct BorderSide {
pub width: f32,
}

impl<'c> ComposeChild<'c> for BoxDecoration {
type Child = Widget<'c>;

fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
WrapRender::combine_child(this, child)
}
}

impl BorderSide {
#[inline]
pub fn new(width: f32, color: Brush) -> Self { Self { width, color } }
}

impl Render for BoxDecoration {
impl WrapRender for BoxDecoration {
#[inline]
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
ctx.assert_perform_single_child_layout(clamp)
fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {
host.perform_layout(clamp, ctx)
}

fn paint(&self, ctx: &mut PaintingCtx) {
fn paint(&self, host: &dyn Render, ctx: &mut PaintingCtx) {
let size = ctx.box_size().unwrap();
if !size.is_empty() {
let rect = Rect::from_size(size);
Expand All @@ -58,6 +66,7 @@ impl Render for BoxDecoration {
painter.fill();
}
self.paint_border(painter, &rect);
host.paint(ctx)
}
}
}
Expand Down Expand Up @@ -211,6 +220,6 @@ mod tests {
}
}),
LayoutCase::default().with_size(Size::new(100., 100.)),
LayoutCase::new(&[0, 0]).with_rect(ribir_geom::rect(0., 0., 100., 100.))
LayoutCase::new(&[0]).with_rect(ribir_geom::rect(0., 0., 100., 100.))
);
}
26 changes: 14 additions & 12 deletions core/src/builtin_widgets/constrained_box.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::prelude::*;
use crate::{prelude::*, wrap_render::WrapRender};

/// a widget that imposes additional constraints clamp on its child.
#[derive(SingleChild, Clone, Default)]
#[derive(Clone, Default)]
pub struct ConstrainedBox {
pub clamp: BoxClamp,
}
Expand All @@ -12,18 +12,20 @@ impl Declare for ConstrainedBox {
fn declarer() -> Self::Builder { FatObj::new(()) }
}

impl Render for ConstrainedBox {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
impl<'c> ComposeChild<'c> for ConstrainedBox {
type Child = Widget<'c>;

fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
WrapRender::combine_child(this, child)
}
}

impl WrapRender for ConstrainedBox {
fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {
let max = clamp.clamp(self.clamp.max);
let min = clamp.clamp(self.clamp.min);
ctx.assert_perform_single_child_layout(BoxClamp { min, max })
host.perform_layout(BoxClamp { min, max }, ctx)
}

#[inline]
fn only_sized_by_parent(&self) -> bool { false }

#[inline]
fn paint(&self, _: &mut PaintingCtx) {}
}

#[cfg(test)]
Expand All @@ -43,7 +45,7 @@ mod tests {
}
}
}),
LayoutCase::new(&[0, 0, 0]).with_size(Size::new(50., 50.))
LayoutCase::new(&[0]).with_size(Size::new(50., 50.))
);

widget_layout_test!(
Expand Down
Loading

0 comments on commit 3332e21

Please sign in to comment.