Skip to content

Commit

Permalink
refactor(core): 💡 merge builtin field of anchors(left, right, top, bo…
Browse files Browse the repository at this point in the history
…ttom) into anchor
  • Loading branch information
wjian23 authored and M-Adoo committed Dec 20, 2023
1 parent cf61845 commit 0b00fff
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 323 deletions.
5 changes: 1 addition & 4 deletions core/src/builtin_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,7 @@ impl_builtin_obj!(
TransformWidget,
HAlignWidget,
VAlignWidget,
LeftAnchor,
RightAnchor,
TopAnchor,
BottomAnchor,
RelativeAnchor,
GlobalAnchor,
Visibility,
Opacity,
Expand Down
234 changes: 163 additions & 71 deletions core/src/builtin_widgets/anchor.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,186 @@
use crate::prelude::*;

/// Widget use to anchor child constraints with the left edge of parent widget.
#[derive(Declare, Query, SingleChild)]
pub struct LeftAnchor {
#[declare(builtin, default)]
pub left_anchor: f32,
}
/// Specifies the horizontal position you want to anchor the widget.
#[derive(Debug, Clone, Copy)]
pub enum HAnchor {
/// positions the widget's left edge x pixels to the right of the target's
/// left edge.
Left(f32),

/// Widget use to anchor child constraints with the right edge of parent widget.
#[derive(Declare, Query, SingleChild)]
pub struct RightAnchor {
#[declare(builtin, default)]
pub right_anchor: f32,
/// positions the widget's right edge x pixels to the left of the target's
/// right edge.
Right(f32),
}

/// Widget use to anchor child constraints with the top edge of parent widget.
#[derive(Declare, Query, SingleChild)]
pub struct TopAnchor {
#[declare(builtin, default)]
pub top_anchor: f32,
}
/// Specifies the vertical position you want to anchor the widget.
#[derive(Debug, Clone, Copy)]
pub enum VAnchor {
/// positions the widget's top edge x pixels bellow the target's top edge.
Top(f32),

/// Widget use to anchor child constraints with the bottom edge of parent
/// widget.
#[derive(Declare, Query, SingleChild)]
pub struct BottomAnchor {
#[declare(builtin, default)]
pub bottom_anchor: f32,
/// positions the widget's bottom edge x pixels above the target's bottom
/// edge.
Bottom(f32),
}

impl Render for LeftAnchor {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
let mut layouter = ctx.assert_single_child_layouter();
let child_size = layouter.perform_widget_layout(clamp);
let left = self.left_anchor;
layouter.update_position(Point::new(left, 0.));
child_size
impl HAnchor {
pub fn map(self, f: impl FnOnce(f32) -> f32) -> Self {
match self {
HAnchor::Left(x) => HAnchor::Left(f(x)),
HAnchor::Right(x) => HAnchor::Right(f(x)),
}
}
}

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

fn hit_test(&self, _: &HitTestCtx, _: Point) -> HitTest {
HitTest { hit: false, can_hit_child: true }
impl VAnchor {
pub fn map(self, f: impl FnOnce(f32) -> f32) -> Self {
match self {
VAnchor::Top(x) => VAnchor::Top(f(x)),
VAnchor::Bottom(x) => VAnchor::Bottom(f(x)),
}
}
}

impl Render for RightAnchor {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
let mut layouter = ctx.assert_single_child_layouter();
let child_size = layouter.perform_widget_layout(clamp);
let right = self.right_anchor;
let x = clamp.max.width - child_size.width - right;
layouter.update_position(Point::new(x, 0.));
impl PartialEq for HAnchor {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(HAnchor::Left(x1), HAnchor::Left(x2)) => (x1 - x2).abs() < f32::EPSILON,
(HAnchor::Right(x1), HAnchor::Right(x2)) => (x1 - x2).abs() < f32::EPSILON,
_ => false,
}
}
}

child_size
impl PartialEq for VAnchor {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(VAnchor::Top(y1), VAnchor::Top(y2)) => (y1 - y2).abs() < f32::EPSILON,
(VAnchor::Bottom(y1), VAnchor::Bottom(y2)) => (y1 - y2).abs() < f32::EPSILON,
_ => false,
}
}
}

fn paint(&self, _: &mut PaintingCtx) {}
#[derive(Clone, Copy, Default, PartialEq)]
pub struct Anchor {
/// Specifies the horizontal position you want to anchor the widget, See
/// [`HAnchor`]!. if None, the widget is anchored by the parent
pub x: Option<HAnchor>,

fn hit_test(&self, _: &HitTestCtx, _: Point) -> HitTest {
HitTest { hit: false, can_hit_child: true }
/// Specifies the vertical position you want to anchor the widget, See
/// [`VAnchor`]! if None, the widget is anchored by the parent
pub y: Option<VAnchor>,
}

impl Lerp for HAnchor {
fn lerp(&self, other: &Self, t: f32) -> Self {
match (self, other) {
(HAnchor::Left(x1), HAnchor::Left(x2)) => HAnchor::Left(x1.lerp(x2, t)),
(HAnchor::Right(x1), HAnchor::Right(x2)) => HAnchor::Right(x1.lerp(x2, t)),
_ => unreachable!(),
}
}
}

impl Render for TopAnchor {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
let mut layouter = ctx.assert_single_child_layouter();
let child_size = layouter.perform_widget_layout(clamp);
let top = self.top_anchor;
layouter.update_position(Point::new(0., top));
child_size
impl Lerp for VAnchor {
fn lerp(&self, other: &Self, t: f32) -> Self {
match (self, other) {
(VAnchor::Top(y1), VAnchor::Top(y2)) => VAnchor::Top(y1.lerp(y2, t)),
(VAnchor::Bottom(y1), VAnchor::Bottom(y2)) => VAnchor::Bottom(y1.lerp(y2, t)),
_ => unreachable!(),
}
}
}

fn paint(&self, _: &mut PaintingCtx) {}
impl Lerp for Anchor {
fn lerp(&self, other: &Self, t: f32) -> Self {
let x = match (self.x, other.x) {
(Some(x1), Some(x2)) => Some(x1.lerp(&x2, t)),
(Some(x1), None) => Some(x1.map(|x| x.lerp(&0., t))),
(None, Some(x1)) => Some(x1.map(|x| 0_f32.lerp(&x, t))),
_ => None,
};

fn hit_test(&self, _: &HitTestCtx, _: Point) -> HitTest {
HitTest { hit: false, can_hit_child: true }
let y = match (self.y, other.y) {
(Some(y1), Some(y2)) => Some(y1.lerp(&y2, t)),
(Some(y1), None) => Some(y1.map(|y| y.lerp(&0., t))),
(None, Some(y1)) => Some(y1.map(|y| 0_f32.lerp(&y, t))),
_ => None,
};
Self { x, y }
}
}

impl Render for BottomAnchor {
impl Anchor {
pub fn new(x: HAnchor, y: VAnchor) -> Self { Self { x: Some(x), y: Some(y) } }

/// Return Anchor that positions the widget's left top corner to the position
pub fn from_point(pos: Point) -> Self { Self::new(HAnchor::Left(pos.x), VAnchor::Top(pos.y)) }

/// Return Anchor that positions the widget's left edge x pixels to the right
/// of the target's left edge.
pub fn left(x: f32) -> Self { Self { x: Some(HAnchor::Left(x)), y: None } }

/// Return Anchor that positions the widget's right edge x pixels to the left
/// of the target's right edge.
pub fn right(x: f32) -> Self { Self { x: Some(HAnchor::Right(x)), y: None } }

/// Return Anchor that positions the widget's top edge x pixels bellow the
/// target's top edge.
pub fn top(y: f32) -> Self { Self { x: None, y: Some(VAnchor::Top(y)) } }

/// Return Anchor that positions the widget's bottom edge x pixels above the
/// parent's bottom edge.
pub fn bottom(y: f32) -> Self { Self { x: None, y: Some(VAnchor::Bottom(y)) } }

/// Return Anchor that positions the widget's left top corner to the position
/// x pixel right, y pixel bellow relative to the left top corner of
/// the target
pub fn left_top(x: f32, y: f32) -> Self { Self::new(HAnchor::Left(x), VAnchor::Top(y)) }

/// Return Anchor that positions the widget's right top corner to the position
/// x pixel left, y pixel bellow relative to the right top corner of
/// the target
pub fn right_top(x: f32, y: f32) -> Self { Self::new(HAnchor::Right(x), VAnchor::Top(y)) }

/// Return Anchor that positions the widget's left bottom corner to the
/// position x pixel right, y pixel above relative to the left bottom corner
/// of the target
pub fn left_bottom(x: f32, y: f32) -> Self { Self::new(HAnchor::Left(x), VAnchor::Bottom(y)) }

/// Return Anchor that positions the widget's right bottom corner to the
/// position x pixel left, y pixel above relative to the right bottom corner
/// of the target
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(Declare, Query, SingleChild)]
pub struct RelativeAnchor {
#[declare(builtin, default)]
pub anchor: Anchor,
}

impl Render for RelativeAnchor {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
let mut layouter = ctx.assert_single_child_layouter();
let child_size = layouter.perform_widget_layout(clamp);
let bottom = self.bottom_anchor;
let y = clamp.max.height - child_size.height - bottom;
layouter.update_position(Point::new(0., y));

let Anchor { x, y } = self.anchor;
let x = x
.map(|x| match x {
HAnchor::Left(x) => x,
HAnchor::Right(x) => clamp.max.width - child_size.width - x,
})
.unwrap_or_default();
let y = y
.map(|y| match y {
VAnchor::Top(y) => y,
VAnchor::Bottom(y) => clamp.max.height - child_size.height - y,
})
.unwrap_or_default();

layouter.update_position(Point::new(x, y));
child_size
}

Expand All @@ -95,6 +190,7 @@ impl Render for BottomAnchor {
HitTest { hit: false, can_hit_child: true }
}
}

#[cfg(test)]
mod test {
use super::*;
Expand All @@ -107,63 +203,59 @@ mod test {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
left_anchor: 1.,
top_anchor: 1.,
anchor: Anchor::left_top(1., 1.),
}
}
}
widget_layout_test!(
pixel_left_top,
wnd_size = WND_SIZE,
{ path = [0, 0], y == 1., }
{ path = [0, 0, 0], x == 1., }
{ path = [0, 0], x == 1., }
);

fn pixel_left_bottom() -> impl WidgetBuilder {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
left_anchor: 1.,
bottom_anchor: 1.,
anchor: Anchor::left_bottom(1., 1.),
}
}
}
widget_layout_test!(
pixel_left_bottom,
wnd_size = WND_SIZE,
{ path = [0, 0], y == 49.,}
{ path = [0, 0, 0], x == 1., }
{ path = [0, 0], x == 1., }
);

fn pixel_top_right() -> impl WidgetBuilder {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
right_anchor: 1.,
top_anchor: 1.,
anchor: Anchor::right_top(1., 1.),
}
}
}
widget_layout_test!(
pixel_top_right,
wnd_size = WND_SIZE,
{ path = [0, 0], y == 1.,}
{ path = [0, 0, 0], x == 49.,}
{ path = [0, 0], x == 49.,}
);

fn pixel_bottom_right() -> impl WidgetBuilder {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
right_anchor: 1.,
bottom_anchor: 1.,
anchor: Anchor::right_bottom(1., 1.)
}
}
}
widget_layout_test!(
pixel_bottom_right,
wnd_size = WND_SIZE,
{ path = [0, 0], y == 49.,}
{ path = [0, 0, 0], x== 49.,}
{ path = [0, 0], x == 49.,}
);
}
Loading

0 comments on commit 0b00fff

Please sign in to comment.