diff --git a/masonry/README.md b/masonry/README.md index 108c2d688..c953c4cf6 100644 --- a/masonry/README.md +++ b/masonry/README.md @@ -133,7 +133,7 @@ pub fn make_widget_tree() -> NewWidget { let list = Flex::column() .with_fixed( NewWidget::new(Flex::row().with(text_input, 1.0).with_fixed(button)) - .with_props(PropertySet::new().with(Padding::all(WIDGET_SPACING.get()))), + .with_props(PropertySet::new().with(Padding::all(WIDGET_SPACING))), ) .with_fixed_spacer(WIDGET_SPACING); diff --git a/masonry/examples/calc_masonry.rs b/masonry/examples/calc_masonry.rs index be14fb0d5..12f310872 100644 --- a/masonry/examples/calc_masonry.rs +++ b/masonry/examples/calc_masonry.rs @@ -255,7 +255,7 @@ pub fn build_calc() -> NewWidget { NewWidget::new(root_widget).with_props( PropertySet::new() .with(Background::Color(AlphaColor::from_str("#794869").unwrap())) - .with(Padding::all(2.0)) + .with(Padding::all(2.px())) .with(Gap::new(1.px())), ) } @@ -274,7 +274,7 @@ fn custom_property_set() -> DefaultProperties { PropertySet::new() .with(Background::Color(BLUE)) .with(BorderColor::new(Color::TRANSPARENT)) - .with(BorderWidth::all(2.0)), + .with(BorderWidth::all(2.px())), ); stack.push( Selector::classes(&["op_button"]).with_active(true), @@ -285,7 +285,7 @@ fn custom_property_set() -> DefaultProperties { PropertySet::new() .with(Background::Color(GRAY)) .with(BorderColor::new(Color::TRANSPARENT)) - .with(BorderWidth::all(2.0)), + .with(BorderWidth::all(2.px())), ); stack.push( Selector::classes(&["digit_button"]).with_active(true), diff --git a/masonry/examples/custom_widget.rs b/masonry/examples/custom_widget.rs index 0d2671cd7..b9cab1efe 100644 --- a/masonry/examples/custom_widget.rs +++ b/masonry/examples/custom_widget.rs @@ -16,7 +16,7 @@ use masonry::core::{ }; use masonry::imaging::Painter; use masonry::kurbo::{Affine, Axis, BezPath, Point, Rect, Size, Stroke}; -use masonry::layout::LenReq; +use masonry::layout::{AsUnit, LenReq, Length}; use masonry::parley::FontFamilyName; use masonry::parley::style::{FontFamily, GenericFamily, StyleProperty}; use masonry::peniko::{Color, ImageBrush, ImageFormat}; @@ -80,16 +80,16 @@ impl Widget for CustomWidget { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - _cross_length: Option, - ) -> f64 { + _cross_length: Option, + ) -> Length { // We currently just define our preferred min/max, // but often it takes actual work to derive these. let min_size = Size::new(100., 50.); let max_size = Size::new(200., 200.); // Measurement is per axis, so we only care about a single dimension right now - let min_length = min_size.get_coord(axis); - let max_length = max_size.get_coord(axis); + let min_length = min_size.get_coord(axis).px(); + let max_length = max_size.get_coord(axis).px(); // Return a result based on the parent's request match len_req { @@ -113,10 +113,9 @@ impl Widget for CustomWidget { painter: &mut Painter<'_>, ) { // Clear the whole widget with the color of your choice - // (ctx.content_box_size() returns the size of the content rect we're painting in) - let size = ctx.content_box_size(); - let rect = ctx.content_box(); - painter.fill(rect, palette::css::WHITE).draw(); + let content_box = ctx.content_box(); + let size = content_box.size(); + painter.fill(content_box, palette::css::WHITE).draw(); // Create an arbitrary bezier path let mut path = BezPath::new(); @@ -166,7 +165,7 @@ impl Widget for CustomWidget { width: 256, height: 256, }); - let transform = ObjectFit::Stretch.affine(size, Size::new(256., 256.)); + let transform = ObjectFit::Stretch.affine(content_box, Rect::new(0., 0., 256., 256.)); painter.draw_image(&image_data, transform); } diff --git a/masonry/examples/gallery/badge.rs b/masonry/examples/gallery/badge.rs index b4c1a7a3d..b1f767048 100644 --- a/masonry/examples/gallery/badge.rs +++ b/masonry/examples/gallery/badge.rs @@ -6,7 +6,7 @@ use masonry::core::{ ErasedAction, Handled, NewWidget, PropertySet, StyleProperty, Widget, WidgetId, WidgetTag, }; use masonry::kurbo::Vec2; -use masonry::layout::Length; +use masonry::layout::{AsUnit, Length}; use masonry::parley::style::FontWeight; use masonry::peniko::Color; use masonry::properties::types::CrossAxisAlignment; @@ -93,7 +93,7 @@ impl DemoPage for BadgeDemo { let outline_badge = NewWidget::new(Badge::with_text("99+")).with_props( PropertySet::new() .with(Background::Color(Color::TRANSPARENT)) - .with(BorderWidth { width: 1.0 }) + .with(BorderWidth { width: 1.px() }) .with(BorderColor { color: Color::from_rgb8(0x71, 0x71, 0x7a), }), @@ -155,8 +155,8 @@ impl DemoPage for BadgeDemo { .with_props( PropertySet::new() .with(Background::Color(Color::from_rgb8(0x3f, 0x3f, 0x46))) - .with(CornerRadius { radius: 999.0 }) - .with(Padding::all(0.0)), + .with(CornerRadius { radius: 999.px() }) + .with(Padding::ZERO), ); let online_dot = NewWidget::new(Badge::new( @@ -166,9 +166,9 @@ impl DemoPage for BadgeDemo { )) .with_props( PropertySet::new() - .with(Padding::all(0.0)) - .with(CornerRadius { radius: 999.0 }) - .with(BorderWidth { width: 0.0 }) + .with(Padding::ZERO) + .with(CornerRadius { radius: 999.px() }) + .with(BorderWidth { width: 0.px() }) .with(Background::Color(Color::from_rgb8(0x22, 0xc5, 0x5e))), ); diff --git a/masonry/examples/gallery/image.rs b/masonry/examples/gallery/image.rs index 1a3025d47..aa16d8cb3 100644 --- a/masonry/examples/gallery/image.rs +++ b/masonry/examples/gallery/image.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use masonry::core::{NewWidget, PropertySet, StyleProperty, Widget}; -use masonry::layout::AsUnit as _; +use masonry::layout::AsUnit; use masonry::peniko::{ImageAlphaType, ImageData, ImageFormat}; use masonry::properties::ObjectFit; use masonry::properties::types::CrossAxisAlignment; @@ -54,7 +54,7 @@ impl DemoPage for ImageDemo { .prepare(), ) .with_fixed_spacer(CONTENT_GAP) - .with_fixed(SizedBox::new(image).size(420.0.px(), 280.0.px()).prepare()); + .with_fixed(SizedBox::new(image).size(420.px(), 280.px()).prepare()); wrap_in_shell(self.shell, NewWidget::new(body).erased()) } diff --git a/masonry/examples/gallery/kitchen_sink.rs b/masonry/examples/gallery/kitchen_sink.rs index 6bf2ff18d..9e7a29c37 100644 --- a/masonry/examples/gallery/kitchen_sink.rs +++ b/masonry/examples/gallery/kitchen_sink.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use masonry::core::{NewWidget, PropertySet, StyleProperty, Widget}; -use masonry::layout::{AsUnit as _, UnitPoint}; +use masonry::layout::{AsUnit, UnitPoint}; use masonry::peniko::Color; use masonry::properties::types::CrossAxisAlignment; use masonry::properties::{Background, Padding}; @@ -43,10 +43,10 @@ impl DemoPage for KitchenSinkDemo { let grid = NewWidget::new(SizedBox::new(grid.prepare())).with_props( PropertySet::new() .with(Background::Color(Color::from_rgb8(0x24, 0x24, 0x24))) - .with(Padding::all(12.0)), + .with(Padding::all(12.px())), ); - let bg = NewWidget::new(SizedBox::empty().size(220.0.px(), 120.0.px())).with_props( + let bg = NewWidget::new(SizedBox::empty().size(220.px(), 120.px())).with_props( PropertySet::one(Background::Color(Color::from_rgb8(0x44, 0x22, 0x66))), ); diff --git a/masonry/examples/gallery/main.rs b/masonry/examples/gallery/main.rs index beaf20028..23c373ce4 100644 --- a/masonry/examples/gallery/main.rs +++ b/masonry/examples/gallery/main.rs @@ -32,6 +32,7 @@ mod transforms; use masonry::core::{ErasedAction, NewWidget, StyleProperty, Widget as _, WidgetId, WidgetTag}; use masonry::dpi::LogicalSize; +use masonry::layout::Length; use masonry::parley::style::FontWeight; use masonry::properties::Padding; use masonry::properties::types::CrossAxisAlignment; @@ -45,11 +46,11 @@ use masonry_winit::winit::window::Window; use crate::demo::{DemoPage, new_demo_shell_tags}; use crate::switch::SwitchDemo; -const SIDEBAR_WIDTH: masonry::layout::Length = masonry::layout::Length::const_px(240.0); -const SIDEBAR_SCROLLBAR_INSET: f64 = 12.0; -const LEFT_PANE_TOP_PADDING: f64 = 12.0; -const LEFT_PANE_LEFT_PADDING: f64 = 12.0; -const RIGHT_PANE_PADDING: f64 = 12.0; +const SIDEBAR_WIDTH: Length = Length::const_px(240.0); +const SIDEBAR_SCROLLBAR_INSET: Length = Length::const_px(12.0); +const LEFT_PANE_TOP_PADDING: Length = Length::const_px(12.0); +const LEFT_PANE_LEFT_PADDING: Length = Length::const_px(12.0); +const RIGHT_PANE_PADDING: Length = Length::const_px(12.0); const DEMO_TITLE_FONT_SIZE: f32 = 20.0; @@ -222,7 +223,7 @@ fn main() { // scrollbar doesn't sit on top of the buttons. let list = NewWidget::new(SizedBox::new(list.prepare())).with_props(Padding { top: LEFT_PANE_TOP_PADDING, - bottom: 0.0, + bottom: Length::ZERO, left: LEFT_PANE_LEFT_PADDING, right: SIDEBAR_SCROLLBAR_INSET, }); diff --git a/masonry/examples/gallery/spinner.rs b/masonry/examples/gallery/spinner.rs index fafe54a0f..1048362e8 100644 --- a/masonry/examples/gallery/spinner.rs +++ b/masonry/examples/gallery/spinner.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use masonry::core::{NewWidget, Widget}; -use masonry::layout::AsUnit as _; +use masonry::layout::AsUnit; use masonry::properties::types::CrossAxisAlignment; use masonry::widgets::{Flex, SizedBox, Spinner}; @@ -32,7 +32,7 @@ impl DemoPage for SpinnerDemo { .cross_axis_alignment(CrossAxisAlignment::Center) .with_fixed( SizedBox::new(Spinner::new().prepare()) - .size(80.0.px(), 80.0.px()) + .size(80.px(), 80.px()) .prepare(), ); diff --git a/masonry/examples/gallery/split.rs b/masonry/examples/gallery/split.rs index 5a5e9ed93..142524cab 100644 --- a/masonry/examples/gallery/split.rs +++ b/masonry/examples/gallery/split.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use masonry::core::{NewWidget, PropertySet, StyleProperty, Widget}; -use masonry::layout::AsUnit as _; +use masonry::layout::AsUnit; use masonry::peniko::Color; use masonry::properties::{Background, Padding}; use masonry::widgets::{Label, SizedBox, Split}; @@ -37,7 +37,7 @@ impl DemoPage for SplitDemo { .with_props( PropertySet::new() .with(Background::Color(Color::from_rgb8(0x1f, 0x2a, 0x44))) - .with(Padding::all(12.0)), + .with(Padding::all(12.px())), ); let right = NewWidget::new(SizedBox::new( @@ -48,11 +48,11 @@ impl DemoPage for SplitDemo { .with_props( PropertySet::new() .with(Background::Color(Color::from_rgb8(0x2b, 0x3c, 0x2f))) - .with(Padding::all(12.0)), + .with(Padding::all(12.px())), ); - let body = SizedBox::new(Split::new(left, right).split_fraction(0.33).prepare()) - .height(260.0.px()); + let body = + SizedBox::new(Split::new(left, right).split_fraction(0.33).prepare()).height(260.px()); wrap_in_shell(self.shell, NewWidget::new(body).erased()) } diff --git a/masonry/examples/gallery/step_input.rs b/masonry/examples/gallery/step_input.rs index 3dad7c9f6..785af4b87 100644 --- a/masonry/examples/gallery/step_input.rs +++ b/masonry/examples/gallery/step_input.rs @@ -72,7 +72,7 @@ impl DemoPage for StepInputDemo { stack.push( Selector::new(), - (BorderWidth::all(2.), CornerRadius::all(20.)), + (BorderWidth::all(2.px()), CornerRadius::all(20.px())), ); stack.push( diff --git a/masonry/examples/gallery/transforms.rs b/masonry/examples/gallery/transforms.rs index 4a424f059..0dc94bed4 100644 --- a/masonry/examples/gallery/transforms.rs +++ b/masonry/examples/gallery/transforms.rs @@ -6,7 +6,7 @@ use masonry::core::{ ErasedAction, Handled, NewWidget, PropertySet, StyleProperty, Widget, WidgetId, WidgetTag, }; use masonry::kurbo::{Affine, Vec2}; -use masonry::layout::AsUnit as _; +use masonry::layout::AsUnit; use masonry::peniko::Color; use masonry::properties::types::CrossAxisAlignment; use masonry::properties::{Background, Padding}; @@ -108,13 +108,13 @@ impl DemoPage for TransformsDemo { .with_style(StyleProperty::FontSize(14.0)) .prepare(), ) - .size(160.0.px(), 160.0.px()), + .size(160.px(), 160.px()), ) .with_tag(self.target) .with_props( PropertySet::new() .with(Background::Color(Color::from_rgb8(0x35, 0x35, 0x35))) - .with(Padding::all(12.0)), + .with(Padding::all(12.px())), ); let body = Flex::column() diff --git a/masonry/examples/grid_masonry.rs b/masonry/examples/grid_masonry.rs index 92dd95b08..d287be9b1 100644 --- a/masonry/examples/grid_masonry.rs +++ b/masonry/examples/grid_masonry.rs @@ -11,7 +11,7 @@ use masonry::core::{ ErasedAction, NewWidget, PointerButton, PropertySet, StyleProperty, Widget as _, WidgetId, }; use masonry::dpi::LogicalSize; -use masonry::layout::Length; +use masonry::layout::{AsUnit, Length}; use masonry::peniko::Color; use masonry::properties::{BorderColor, BorderWidth, Gap}; use masonry::theme::default_property_set; @@ -69,7 +69,7 @@ pub fn make_grid(grid_gap: f64) -> NewWidget { let props = PropertySet::new() .with(BorderColor::new(Color::from_rgb8(40, 40, 80))) - .with(BorderWidth::all(1.0)); + .with(BorderWidth::all(1.px())); let label = SizedBox::new(NewWidget::new(label).with_props(props)); let button_inputs = vec![ diff --git a/masonry/examples/layers.rs b/masonry/examples/layers.rs index e5115b3b1..3e577ba9f 100644 --- a/masonry/examples/layers.rs +++ b/masonry/examples/layers.rs @@ -15,7 +15,7 @@ use masonry::core::{ use masonry::imaging::Painter; use masonry::kurbo::{Axis, Point, Size, Vec2}; use masonry::layers::Tooltip; -use masonry::layout::{AsUnit, LayoutSize, LenReq, SizeDef}; +use masonry::layout::{AsUnit, LayoutSize, LenReq, Length, SizeDef}; use masonry::parley::FontWeight; use masonry::peniko::Color; use masonry::properties::{Background, BorderColor, BorderWidth, ContentColor}; @@ -117,8 +117,8 @@ impl Widget for OverlayBox { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { + cross_length: Option, + ) -> Length { let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); @@ -178,7 +178,7 @@ fn main() { .with_props(PropertySet::one(ContentColor::new(Color::BLACK))), )) .with_props(PropertySet::from(( - BorderWidth::all(1.), + BorderWidth::all(1.px()), BorderColor::new(Color::BLACK), Background::Color(Color::WHITE), ))) diff --git a/masonry/examples/to_do_list.rs b/masonry/examples/to_do_list.rs index 5a44311d7..21947a2a5 100644 --- a/masonry/examples/to_do_list.rs +++ b/masonry/examples/to_do_list.rs @@ -77,7 +77,7 @@ pub fn make_widget_tree() -> NewWidget { let root = Flex::column() .with_fixed( NewWidget::new(Flex::row().with(text_input, 1.0).with_fixed(button)) - .with_props(PropertySet::new().with(Padding::all(WIDGET_SPACING.get()))), + .with_props(PropertySet::new().with(Padding::all(WIDGET_SPACING))), ) .with_fixed_spacer(WIDGET_SPACING) .with(portal, 1.0); diff --git a/masonry/src/doc/color_rectangle.rs b/masonry/src/doc/color_rectangle.rs index 930ab9bff..be3498409 100644 --- a/masonry/src/doc/color_rectangle.rs +++ b/masonry/src/doc/color_rectangle.rs @@ -25,13 +25,14 @@ use masonry::core::{Update, UpdateCtx}; // --- use masonry::core::{LayoutCtx, MeasureCtx, PropertiesRef}; use masonry::kurbo::{Axis, Size}; -use masonry::layout::LenReq; +use masonry::layout::{LenReq, Length}; // --- use masonry::accesskit::{Node, Role}; use masonry::core::{AccessCtx, PaintCtx}; use masonry::imaging::Painter; // --- use masonry::core::WidgetId; +use masonry_core::layout::AsUnit; use tracing::{Span, trace_span}; // --- use masonry::core::{ChildrenIds, RegisterCtx}; @@ -130,16 +131,12 @@ impl Widget for ColorRectangle { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - _cross_length: Option, - ) -> f64 { - // TODO: Remove HACK: Until scale factor rework happens, just pretend it's always 1.0. - // https://github.com/linebender/xilem/issues/1264 - let scale = 1.0; - + _cross_length: Option, + ) -> Length { match len_req { LenReq::MinContent | LenReq::MaxContent => match axis { - Axis::Horizontal => 200. * scale, - Axis::Vertical => 100. * scale, + Axis::Horizontal => 200.px(), + Axis::Vertical => 100.px(), }, LenReq::FitContent(space) => space, } diff --git a/masonry/src/doc/implementing_container_widget.md b/masonry/src/doc/implementing_container_widget.md index c41430a20..ae4fdad8d 100644 --- a/masonry/src/doc/implementing_container_widget.md +++ b/masonry/src/doc/implementing_container_widget.md @@ -54,7 +54,7 @@ A container widget needs to pay special attention to these methods: trait Widget { // ... - fn measure(&mut self, ctx: &mut MeasureCtx<'_>, props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, cross_length: Option) -> f64; + fn measure(&mut self, ctx: &mut MeasureCtx<'_>, props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, cross_length: Option) -> Length; fn layout(&mut self, ctx: &mut LayoutCtx<'_>, props: &PropertiesRef<'_>, size: Size); fn compose(&mut self, ctx: &mut ComposeCtx<'_>); @@ -87,7 +87,7 @@ For our `VerticalStack`, we'll lay out our children in a vertical line, with a g ```rust,ignore use masonry::core::{LayoutCtx, MeasureCtx, PropertiesRef}; use masonry::kurbo::{Axis, Point, Size}; -use masonry::layout::{LayoutSize, LenDef, LenReq, SizeDef}; +use masonry::layout::{AsUnit, LayoutSize, Length, LenDef, LenReq, SizeDef}; impl Widget for VerticalStack { // ... @@ -98,28 +98,29 @@ impl Widget for VerticalStack { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { + cross_length: Option, + ) -> Length { let (len_req, min_result) = match len_req { - LenReq::MinContent | LenReq::MaxContent => (len_req, 0.), + LenReq::MinContent | LenReq::MaxContent => (len_req, Length::ZERO), LenReq::FitContent(space) => (LenReq::MinContent, space), }; let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); - let mut length: f64 = 0.; + let mut length = Length::ZERO; for child in &mut self.children { let child_length = ctx.compute_length(child, auto_length, context_size, axis, cross_length); match axis { Axis::Horizontal => length = length.max(child_length), - Axis::Vertical => length += child_length, + Axis::Vertical => length = length.saturating_add(child_length), } } if axis == Axis::Vertical { let gap_count = (self.children.len() - 1) as f64; - length += gap_count * self.gap; + let gaps_length = gap_count * self.gap; + length = length.saturating_add(gaps_length.px()); } min_result.max(length) @@ -135,8 +136,8 @@ impl Widget for VerticalStack { let total_child_vertical_space = size.height - self.gap * gap_count; let child_vertical_space = total_child_vertical_space / self.children.len() as f64; - let width_def = LenDef::FitContent(size.width); - let height_def = LenDef::FitContent(child_vertical_space.max(0.)); + let width_def = LenDef::FitContent(size.width.px()); + let height_def = LenDef::FitContent(child_vertical_space.max(0.).px()); let auto_size = SizeDef::new(width_def, height_def); let context_size = size.into(); diff --git a/masonry/src/doc/implementing_widget.md b/masonry/src/doc/implementing_widget.md index 44e135cf3..c72e1be55 100644 --- a/masonry/src/doc/implementing_widget.md +++ b/masonry/src/doc/implementing_widget.md @@ -32,7 +32,7 @@ trait Widget { fn on_anim_frame(&mut self, ctx: &mut UpdateCtx<'_>, props: &mut PropertiesMut<'_>, interval: u64); fn update(&mut self, ctx: &mut UpdateCtx<'_>, props: &mut PropertiesMut<'_>, event: &Update); - fn measure(&mut self, ctx: &mut MeasureCtx<'_>, props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, cross_length: Option) -> f64; + fn measure(&mut self, ctx: &mut MeasureCtx<'_>, props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, cross_length: Option) -> Length; fn layout(&mut self, ctx: &mut LayoutCtx<'_>, props: &PropertiesRef<'_>, size: Size); fn paint(&mut self, ctx: &mut PaintCtx<'_>, props: &PropertiesRef<'_>, painter: &mut Painter<'_>); @@ -169,7 +169,7 @@ Next we implement layout: ```rust,ignore use masonry::core::{LayoutCtx, MeasureCtx, PropertiesRef}; use masonry::kurbo::{Axis, Size}; -use masonry::layout::LenReq; +use masonry::layout::{Length, LenReq}; impl Widget for ColorRectangle { // ... @@ -180,16 +180,13 @@ impl Widget for ColorRectangle { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - _cross_length: Option, - ) -> f64 { - // TODO: Remove HACK: Until scale factor rework happens, just pretend it's always 1.0. - // https://github.com/linebender/xilem/issues/1264 - let scale = 1.0; + _cross_length: Option, + ) -> Length { match len_req { LenReq::MinContent | LenReq::MaxContent => match axis { - Axis::Horizontal => 200. * scale, - Axis::Vertical => 100. * scale, + Axis::Horizontal => 200.px(), + Axis::Vertical => 100.px(), } LenReq::FitContent(space) => space, } diff --git a/masonry/src/doc/vertical_stack.rs b/masonry/src/doc/vertical_stack.rs index 880d57218..cb19b8641 100644 --- a/masonry/src/doc/vertical_stack.rs +++ b/masonry/src/doc/vertical_stack.rs @@ -21,7 +21,7 @@ use masonry::core::{Widget, WidgetPod}; // --- use masonry::core::{LayoutCtx, MeasureCtx, PropertiesRef}; use masonry::kurbo::{Axis, Point, Size}; -use masonry::layout::{LayoutSize, LenDef, LenReq, SizeDef}; +use masonry::layout::{AsUnit, LayoutSize, LenDef, LenReq, Length, SizeDef}; // --- use masonry::core::ComposeCtx; // --- @@ -94,28 +94,29 @@ impl Widget for VerticalStack { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { + cross_length: Option, + ) -> Length { let (len_req, min_result) = match len_req { - LenReq::MinContent | LenReq::MaxContent => (len_req, 0.), + LenReq::MinContent | LenReq::MaxContent => (len_req, Length::ZERO), LenReq::FitContent(space) => (LenReq::MinContent, space), }; let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); - let mut length: f64 = 0.; + let mut length = Length::ZERO; for child in &mut self.children { let child_length = ctx.compute_length(child, auto_length, context_size, axis, cross_length); match axis { Axis::Horizontal => length = length.max(child_length), - Axis::Vertical => length += child_length, + Axis::Vertical => length = length.saturating_add(child_length), } } if axis == Axis::Vertical { let gap_count = (self.children.len() - 1) as f64; - length += gap_count * self.gap; + let gaps_length = gap_count * self.gap; + length = length.saturating_add(gaps_length.px()); } min_result.max(length) @@ -131,8 +132,8 @@ impl Widget for VerticalStack { let total_child_vertical_space = size.height - self.gap * gap_count; let child_vertical_space = total_child_vertical_space / self.children.len() as f64; - let width_def = LenDef::FitContent(size.width); - let height_def = LenDef::FitContent(child_vertical_space.max(0.)); + let width_def = LenDef::FitContent(size.width.px()); + let height_def = LenDef::FitContent(child_vertical_space.max(0.).px()); let auto_size = SizeDef::new(width_def, height_def); let context_size = size.into(); diff --git a/masonry/src/layers/selector_menu.rs b/masonry/src/layers/selector_menu.rs index 460322211..951bbe389 100644 --- a/masonry/src/layers/selector_menu.rs +++ b/masonry/src/layers/selector_menu.rs @@ -12,7 +12,7 @@ use crate::core::{ }; use crate::imaging::Painter; use crate::kurbo::{Axis, Point, Size}; -use crate::layout::{LayoutSize, LenDef, LenReq, SizeDef}; +use crate::layout::{AsUnit, LayoutSize, LenDef, LenReq, Length, SizeDef}; use crate::properties::Gap; use crate::widgets::{SelectionChanged, Selector}; @@ -236,58 +236,51 @@ impl Widget for SelectorMenu { props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { - // TODO: Remove HACK: Until scale factor rework happens, just pretend it's always 1.0. - // https://github.com/linebender/xilem/issues/1264 - let scale = 1.0; - + cross_length: Option, + ) -> Length { let cache = ctx.property_cache(); let gap = props.get::(cache); - let gap_length = gap.gap.dp(scale); + let gap_length = gap.gap.get(); let (len_req, min_result) = match len_req { - LenReq::MinContent | LenReq::MaxContent => (len_req, 0.), + LenReq::MinContent | LenReq::MaxContent => (len_req, Length::ZERO), LenReq::FitContent(space) => (LenReq::MinContent, space), }; let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); - let mut length: f64 = 0.; + let mut length = Length::ZERO; for child in &mut self.children { let child_length = ctx.compute_length(child, auto_length, context_size, axis, cross_length); match axis { Axis::Horizontal => length = length.max(child_length), - Axis::Vertical => length += child_length, + Axis::Vertical => length = length.saturating_add(child_length), } } if axis == Axis::Vertical { let gap_count = (self.children.len() - 1) as f64; - length += gap_count * gap_length; + let gaps_length = gap_count * gap_length; + length = length.saturating_add(gaps_length.px()); } min_result.max(length) } fn layout(&mut self, ctx: &mut LayoutCtx<'_>, props: &PropertiesRef<'_>, size: Size) { - // TODO: Remove HACK: Until scale factor rework happens, just pretend it's always 1.0. - // https://github.com/linebender/xilem/issues/1264 - let scale = 1.0; - let cache = ctx.property_cache(); let gap = props.get::(cache); let gap_count = (self.children.len() - 1) as f64; - let gap_length = gap.gap.dp(scale); + let gap_length = gap.gap.get(); let total_child_vertical_space = size.height - gap_length * gap_count; let child_vertical_space = total_child_vertical_space / self.children.len() as f64; - let width_def = LenDef::FitContent(size.width); - let height_def = LenDef::FitContent(child_vertical_space.max(0.)); + let width_def = LenDef::FitContent(size.width.px()); + let height_def = LenDef::FitContent(child_vertical_space.max(0.).px()); let auto_size = SizeDef::new(width_def, height_def); let context_size = size.into(); diff --git a/masonry/src/layers/tooltip.rs b/masonry/src/layers/tooltip.rs index 2e489919d..78953fff6 100644 --- a/masonry/src/layers/tooltip.rs +++ b/masonry/src/layers/tooltip.rs @@ -13,7 +13,7 @@ use crate::core::{ }; use crate::imaging::Painter; use crate::kurbo::{Axis, Size}; -use crate::layout::{LayoutSize, LenReq, SizeDef}; +use crate::layout::{LayoutSize, LenReq, Length, SizeDef}; /// A [`Layer`] representing a simple tooltip showing some content until the mouse moves. pub struct Tooltip { @@ -94,8 +94,8 @@ impl Widget for Tooltip { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { + cross_length: Option, + ) -> Length { let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); diff --git a/masonry/src/lib.rs b/masonry/src/lib.rs index 57e24e869..9baf3fe54 100644 --- a/masonry/src/lib.rs +++ b/masonry/src/lib.rs @@ -103,7 +103,7 @@ //! let list = Flex::column() //! .with_fixed( //! NewWidget::new(Flex::row().with(text_input, 1.0).with_fixed(button)) -//! .with_props(PropertySet::new().with(Padding::all(WIDGET_SPACING.get()))), +//! .with_props(PropertySet::new().with(Padding::all(WIDGET_SPACING))), //! ) //! .with_fixed_spacer(WIDGET_SPACING); //! diff --git a/masonry/src/properties/object_fit.rs b/masonry/src/properties/object_fit.rs index caf9f3bbf..4fe8cb2da 100644 --- a/masonry/src/properties/object_fit.rs +++ b/masonry/src/properties/object_fit.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::core::Property; -use crate::kurbo::{Affine, Axis, Size}; -use crate::layout::LenReq; +use crate::kurbo::{Affine, Axis, Rect, Size}; +use crate::layout::{LenReq, Length}; use crate::util::Sanitize; // These are based on https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit @@ -67,8 +67,6 @@ impl Default for ObjectFit { } } -// TODO - Need to write tests for this, in a way that's relatively easy to visualize. - impl ObjectFit { /// Calculates an [`Affine`] transform to fit `content` inside `container`. /// @@ -76,25 +74,22 @@ impl ObjectFit { /// /// # Panics /// - /// Panics if either `content` or `container` is non-finite or negative + /// Panics if either `content` or `container` has non-finite or negative size /// and debug assertions are enabled. - pub fn affine(self, container: Size, content: Size) -> Affine { + pub fn affine(self, container: Rect, content: Rect) -> Affine { // Guard against invalid input - let container = Size::new( - container.width.sanitize("container width"), - container.height.sanitize("container height"), - ); - let content = Size::new( - content.width.sanitize("content width"), - content.height.sanitize("content height"), - ); + let container_width = container.width().sanitize("container width"); + let container_height = container.height().sanitize("container height"); + let content_width = content.width().sanitize("content width"); + let content_height = content.height().sanitize("content height"); + // Guard against division by zero - if content.width == 0. || content.height == 0. { + if content_width == 0. || content_height == 0. { return Affine::IDENTITY; } - let raw_scalex = container.width / content.width; - let raw_scaley = container.height / content.height; + let raw_scalex = container_width / content_width; + let raw_scaley = container_height / content_height; let (scalex, scaley) = match self { Self::Contain => { @@ -115,13 +110,20 @@ impl ObjectFit { Self::Stretch => (raw_scalex, raw_scaley), }; - let origin_x = (container.width - (content.width * scalex)) * 0.5; - let origin_y = (container.height - (content.height * scaley)) * 0.5; + let origin_x = container.x0 + (container_width - (content_width * scalex)) * 0.5; + let origin_y = container.y0 + (container_height - (content_height * scaley)) * 0.5; - Affine::new([scalex, 0., 0., scaley, origin_x, origin_y]) + Affine::new([ + scalex, + 0., + 0., + scaley, + origin_x - content.x0 * scalex, + origin_y - content.y0 * scaley, + ]) } - /// Calculates the length of `axis`. + /// Calculates the [`Length`] of `axis`. /// /// `preferred_size` is the natural size that is used for /// both aspect ratio and minimum preferred size. @@ -129,15 +131,15 @@ impl ObjectFit { self, axis: Axis, len_req: LenReq, - cross_length: Option, + cross_length: Option, preferred_size: Size, - ) -> f64 { + ) -> Length { let preferred_length = preferred_size.get_coord(axis); let (space, space_or_preferred) = match len_req { - LenReq::MinContent => return preferred_length, + LenReq::MinContent => return Length::px(preferred_length), LenReq::MaxContent => (f64::INFINITY, preferred_length), - LenReq::FitContent(space) => (space, space), + LenReq::FitContent(space) => (space.get(), space.get()), }; let mut ar = preferred_size.get_coord(axis) / preferred_size.get_coord(axis.cross()); @@ -145,11 +147,11 @@ impl ObjectFit { ar = 1.; } - match self { + let length = match self { // Use all available space or if cross is known attempt to maintain AR, // but don't exceed available space (will letterbox cross). Self::Contain => cross_length - .map(|cl| (cl * ar).min(space)) + .map(|cl| (cl.get() * ar).min(space)) .unwrap_or(space_or_preferred), // Always use all available space. Self::Cover | Self::Stretch => space_or_preferred, @@ -157,7 +159,7 @@ impl ObjectFit { // Greedily take all horizontal space unless cross is known. Self::FitHeight => match axis { Axis::Horizontal => cross_length - .map(|cl| (cl * ar).min(space)) + .map(|cl| (cl.get() * ar).min(space)) .unwrap_or(space_or_preferred), Axis::Vertical => space_or_preferred, }, @@ -166,16 +168,18 @@ impl ObjectFit { Self::FitWidth => match axis { Axis::Horizontal => space_or_preferred, Axis::Vertical => cross_length - .map(|cl| (cl * ar).min(space)) + .map(|cl| (cl.get() * ar).min(space)) .unwrap_or(space_or_preferred), }, // None == preferred size Self::None => preferred_length, // ScaleDown == min(Contain, None) Self::ScaleDown => cross_length - .map(|cl| (cl * ar).min(space)) + .map(|cl| (cl.get() * ar).min(space)) .unwrap_or(space_or_preferred) .min(preferred_length), - } + }; + + Length::px(length) } } diff --git a/masonry/src/properties/slider.rs b/masonry/src/properties/slider.rs index 28f01de32..66a6eb4f6 100644 --- a/masonry/src/properties/slider.rs +++ b/masonry/src/properties/slider.rs @@ -4,16 +4,17 @@ use std::any::TypeId; use crate::core::{Property, UpdateCtx}; +use crate::layout::Length; use crate::peniko::Color; use crate::theme; -/// The thickness of a slider's track, in logical pixels. +/// The thickness of a slider's track. #[derive(Default, Clone, Copy, Debug, PartialEq)] -pub struct TrackThickness(pub f64); +pub struct TrackThickness(pub Length); impl Property for TrackThickness { fn static_default() -> &'static Self { - static DEFAULT: TrackThickness = TrackThickness(4.); + static DEFAULT: TrackThickness = TrackThickness(Length::const_px(4.)); &DEFAULT } } @@ -27,13 +28,13 @@ impl TrackThickness { } } -/// The radius of a slider's thumb, in logical pixels. +/// The radius of a slider's thumb. #[derive(Default, Clone, Copy, Debug, PartialEq)] -pub struct ThumbRadius(pub f64); +pub struct ThumbRadius(pub Length); impl Property for ThumbRadius { fn static_default() -> &'static Self { - static DEFAULT: ThumbRadius = ThumbRadius(6.); + static DEFAULT: ThumbRadius = ThumbRadius(Length::const_px(6.)); &DEFAULT } } diff --git a/masonry/src/tests/action.rs b/masonry/src/tests/action.rs index 0c3cd1544..212307e9b 100644 --- a/masonry/src/tests/action.rs +++ b/masonry/src/tests/action.rs @@ -8,7 +8,7 @@ use assert_matches::assert_matches; use crate::core::{ChildrenIds, Widget}; use crate::kurbo::Point; -use crate::layout::{AsUnit, LayoutSize}; +use crate::layout::{AsUnit, LayoutSize, Length}; use crate::properties::Dimensions; use crate::testing::{ModularWidget, TestHarness}; use crate::theme::test_property_set; @@ -61,7 +61,7 @@ fn action_source_removed() { ctx.compute_length(child, auto_length, context_size, axis, cross_length) } else { - 0. + Length::ZERO } }) .layout_fn(move |child, ctx, _props, size| { diff --git a/masonry/src/tests/compose.rs b/masonry/src/tests/compose.rs index fb28558f5..09d69484f 100644 --- a/masonry/src/tests/compose.rs +++ b/masonry/src/tests/compose.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use assert_matches::assert_matches; -use masonry_testing::{ModularWidget, Record, TestHarness, TestWidgetExt}; use crate::core::{ChildrenIds, NewWidget, Widget, WidgetPod, WidgetTag}; use crate::kurbo::{Affine, Point, Vec2}; -use crate::layout::SizeDef; +use crate::layout::{Length, SizeDef}; +use crate::testing::{ModularWidget, Record, TestHarness, TestWidgetExt}; use crate::theme::test_property_set; use crate::widgets::SizedBox; @@ -29,7 +29,7 @@ fn request_compose() { }; let parent = ModularWidget::new(child) - .measure_fn(|_state, _ctx, _props, _axis, _len_req, _cross_length| 0.) + .measure_fn(|_state, _ctx, _props, _axis, _len_req, _cross_length| Length::ZERO) .layout_fn(|state, ctx, _props, size| { let child_size = ctx.compute_size(&mut state.child, SizeDef::fit(size), size.into()); ctx.run_layout(&mut state.child, child_size); diff --git a/masonry/src/tests/event.rs b/masonry/src/tests/event.rs index 1afdae6e0..28d2db98d 100644 --- a/masonry/src/tests/event.rs +++ b/masonry/src/tests/event.rs @@ -28,7 +28,7 @@ fn create_capture_target() -> ModularWidget<()> { ctx.capture_pointer(); } }) - .measure_fn(|_, _, _, _, _, _| 10.) + .measure_fn(|_, _, _, _, _, _| 10.px()) } #[test] diff --git a/masonry/src/tests/layout.rs b/masonry/src/tests/layout.rs index 37e23274d..d50452314 100644 --- a/masonry/src/tests/layout.rs +++ b/masonry/src/tests/layout.rs @@ -53,7 +53,7 @@ fn layout_simple() { #[test] fn forget_to_recurse_layout() { let widget = ModularWidget::new_parent(Flex::row().prepare()) - .measure_fn(|_, _, _, _, _, _| 0.) + .measure_fn(|_, _, _, _, _, _| Length::ZERO) .layout_fn(|_child, _ctx, _, _| { // We forget to call ctx.run_layout(); }) @@ -83,7 +83,7 @@ fn forget_to_call_place_child() { #[test] fn call_place_child_before_layout() { let widget = ModularWidget::new_parent(Flex::row().prepare()) - .measure_fn(|_, _, _, _, _, _| 0.) + .measure_fn(|_, _, _, _, _, _| Length::ZERO) .layout_fn(|child, ctx, _, _| { // We call ctx.place_child(), but forget run_layout ctx.place_child(child, Point::ORIGIN); @@ -229,7 +229,7 @@ fn layout_insets() { let parent_tag = WidgetTag::named("parent"); let child_widget = ModularWidget::new(()) - .measure_fn(|_, _, _, _, _, _| BOX_WIDTH) + .measure_fn(|_, _, _, _, _, _| BOX_WIDTH.px()) .layout_fn(|_, ctx, _, _| { // this widget paints twenty points above and below its layout bounds ctx.set_paint_insets(Insets::uniform_xy(0., 20.)); @@ -274,12 +274,12 @@ fn content_box() { let props = ( Dimensions::fixed(100.px(), 100.px()), Padding { - left: 1., - right: 2., - top: 3., - bottom: 4., + left: 1.px(), + right: 2.px(), + top: 3.px(), + bottom: 4.px(), }, - BorderWidth::all(1.), + BorderWidth::all(1.px()), ); let hero = NewWidget::new(Button::with_text("Hero")) diff --git a/masonry/src/tests/mod.rs b/masonry/src/tests/mod.rs index 64da0e86a..5c56e3162 100644 --- a/masonry/src/tests/mod.rs +++ b/masonry/src/tests/mod.rs @@ -5,6 +5,8 @@ //! both to centralize tests in a single crate and to have access to the `masonry` //! widget/property set in our tests if needed. +use crate::kurbo::Rect; + mod accessibility; mod action; mod anim; @@ -16,3 +18,19 @@ mod paint; mod properties; mod update; mod widget_tag; + +#[track_caller] +pub(crate) fn assert_approx_eq(name: &str, actual: f64, expected: f64) { + assert!( + (actual - expected).abs() <= 1e-9, + "{name}: expected {expected}, got {actual}" + ); +} + +#[track_caller] +pub(crate) fn assert_rect_approx_eq(name: &str, actual: Rect, expected: Rect) { + assert_approx_eq(&format!("{name}.x0"), actual.x0, expected.x0); + assert_approx_eq(&format!("{name}.y0"), actual.y0, expected.y0); + assert_approx_eq(&format!("{name}.x1"), actual.x1, expected.x1); + assert_approx_eq(&format!("{name}.y1"), actual.y1, expected.y1); +} diff --git a/masonry/src/tests/paint.rs b/masonry/src/tests/paint.rs index 708406250..5852d5895 100644 --- a/masonry/src/tests/paint.rs +++ b/masonry/src/tests/paint.rs @@ -76,7 +76,7 @@ fn paint_order() { let children = vec![child1, child2, child3]; let parent = NewWidget::new( ModularWidget::new_multi_parent(children) - .measure_fn(|_, _, _, _, _, _| SQUARE_SIZE * 2.) + .measure_fn(|_, _, _, _, _, _| (SQUARE_SIZE * 2.).px()) .layout_fn(move |children, ctx, _props, size| { let mut pos = Point::ZERO; for child in children { @@ -122,7 +122,7 @@ fn paint_clipping() { let parent = NewWidget::new( ModularWidget::new(()) - .measure_fn(|_, _, _, _, _, _| SQUARE_SIZE) + .measure_fn(|_, _, _, _, _, _| SQUARE_SIZE.px()) .layout_fn(|_, ctx, _, size| { ctx.set_clip_path(size.to_rect()); }) @@ -159,14 +159,14 @@ fn paint_clipping() { fn make_layer_split_tree(isolate_trailing_box: bool) -> NewWidget { let leading = NewWidget::new( ModularWidget::new(()) - .measure_fn(|_, _, _, _, _, _| 20.) + .measure_fn(|_, _, _, _, _, _| 20.px()) .paint_fn(|_, ctx, _, scene| { scene.fill(ctx.content_box(), RED).draw(); }), ); let trailing = NewWidget::new( ModularWidget::new(isolate_trailing_box) - .measure_fn(|_, _, _, _, _, _| 20.) + .measure_fn(|_, _, _, _, _, _| 20.px()) .paint_fn(|isolate, ctx, _, scene| { if *isolate { ctx.set_paint_layer_mode(PaintLayerMode::IsolatedScene); @@ -184,14 +184,14 @@ fn make_layer_split_tree(isolate_trailing_box: bool) -> NewWidget { fn make_external_placeholder_tree() -> NewWidget { let leading = NewWidget::new( ModularWidget::new(()) - .measure_fn(|_, _, _, _, _, _| 20.) + .measure_fn(|_, _, _, _, _, _| 20.px()) .paint_fn(|_, ctx, _, scene| { scene.fill(ctx.content_box(), RED).draw(); }), ); let placeholder = NewWidget::new( ModularWidget::new(()) - .measure_fn(|_, _, _, _, _, _| 20.) + .measure_fn(|_, _, _, _, _, _| 20.px()) .layout_fn(|_, ctx, _, size| { ctx.set_clip_path(size.to_rect()); }) @@ -201,7 +201,7 @@ fn make_external_placeholder_tree() -> NewWidget { ); let trailing = NewWidget::new( ModularWidget::new(()) - .measure_fn(|_, _, _, _, _, _| 20.) + .measure_fn(|_, _, _, _, _, _| 20.px()) .paint_fn(|_, ctx, _, scene| { scene.fill(ctx.content_box(), BLUE).draw(); }), @@ -330,7 +330,7 @@ fn paint_transparency() { GridParams::new(13, 0, 3, 1), ); - let props = (Padding::all(20.), Gap::new(10.px())); + let props = (Padding::all(20.px()), Gap::new(10.px())); let grid_a = grid_a.prepare().with_props(props); let grid_b = grid_b.prepare().with_props(props); diff --git a/masonry/src/tests/properties.rs b/masonry/src/tests/properties.rs index a82b6a081..c99f0365a 100644 --- a/masonry/src/tests/properties.rs +++ b/masonry/src/tests/properties.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::core::Widget as _; -use crate::layout::AsUnit as _; +use crate::kurbo::Rect; +use crate::layout::AsUnit; use crate::palette::css::BLUE; -use crate::properties::{ContentColor, Dimensions, Gap}; +use crate::properties::{ContentColor, Dimensions, Gap, ObjectFit}; +use crate::tests::assert_rect_approx_eq; use crate::widgets::Button; #[test] @@ -30,3 +32,31 @@ fn widget_new_properties() { assert_eq!(props.get::(), Some(&Gap::ZERO)); assert_eq!(props.get::(), Some(&ContentColor::new(BLUE))); } + +#[test] +fn object_fit_affine_stretch_maps_rect_to_rect() { + let container = Rect::new(10., -20., 110., 30.); + let content = Rect::new(-5., 10., 15., 20.); + + let transform = ObjectFit::Stretch.affine(container, content); + + assert_rect_approx_eq( + "transformed", + transform.transform_rect_bbox(content), + container, + ); +} + +#[test] +fn object_fit_affine_contain_handles_negative_origins() { + let container = Rect::new(-30., -20., 70., 30.); + let content = Rect::new(-10., -5., 10., 15.); + + let transform = ObjectFit::Contain.affine(container, content); + + assert_rect_approx_eq( + "transformed", + transform.transform_rect_bbox(content), + Rect::new(-5., -20., 45., 30.), + ); +} diff --git a/masonry/src/tests/transforms.rs b/masonry/src/tests/transforms.rs index b0c8dba71..bf81b52bd 100644 --- a/masonry/src/tests/transforms.rs +++ b/masonry/src/tests/transforms.rs @@ -21,7 +21,7 @@ fn blue_box(inner: impl Widget) -> impl Widget { let mut box_props = PropertySet::new(); box_props.insert(Background::Color(palette::css::BLUE)); box_props.insert(BorderColor::new(palette::css::TEAL)); - box_props.insert(BorderWidth::all(2.0)); + box_props.insert(BorderWidth::all(2.px())); WrapperWidget::new( SizedBox::new(inner.prepare()) diff --git a/masonry/src/tests/update.rs b/masonry/src/tests/update.rs index 4b5d8c460..f93756449 100644 --- a/masonry/src/tests/update.rs +++ b/masonry/src/tests/update.rs @@ -4,17 +4,17 @@ use std::sync::mpsc; use assert_matches::assert_matches; -use masonry_testing::{ - DebugName, ModularWidget, PRIMARY_MOUSE, Record, TestHarness, TestWidgetExt, assert_any, - assert_debug_panics, -}; use crate::core::pointer::PointerEvent; use crate::core::{ CursorIcon, Ime, NewWidget, PropertySet, TextEvent, Update, Widget, WidgetId, WidgetPod, WidgetTag, }; -use crate::layout::Length; +use crate::layout::{AsUnit, Length}; +use crate::testing::{ + DebugName, ModularWidget, PRIMARY_MOUSE, Record, TestHarness, TestWidgetExt, assert_any, + assert_debug_panics, +}; use crate::theme::test_property_set; use crate::widgets::{Button, Flex, Label, SizedBox, TextArea}; @@ -557,7 +557,7 @@ fn create_icon_widget() -> ModularWidget<()> { } }) .cursor_icon(CursorIcon::Crosshair) - .measure_fn(|_, _, _, _, _, _| 10.) + .measure_fn(|_, _, _, _, _, _| 10.px()) } #[test] @@ -648,12 +648,11 @@ fn change_hovered_when_widget_changes() { let parent_tag = WidgetTag::named("parent"); let child = - NewWidget::new(ModularWidget::new(BOX_SIZE).measure_fn(|size, _, _, _, _, _| size.get())) + NewWidget::new(ModularWidget::new(BOX_SIZE).measure_fn(|size, _, _, _, _, _| *size)) .with_tag(child_tag); - let parent = NewWidget::new( - ModularWidget::new_parent(child).measure_fn(|_, _, _, _, _, _| BOX_SIZE.get()), - ) - .with_tag(parent_tag); + let parent = + NewWidget::new(ModularWidget::new_parent(child).measure_fn(|_, _, _, _, _, _| BOX_SIZE)) + .with_tag(parent_tag); let mut harness = TestHarness::create(test_property_set(), parent); let child_id = harness.get_widget(child_tag).id(); @@ -697,7 +696,7 @@ fn make_reporter_parent( ctx.set_handled(); } }) - .measure_fn(|_, _, _, _, _, _| 100.) + .measure_fn(|_, _, _, _, _, _| 100.px()) .update_fn(move |_, _, _, event| { sender.send((event.short_name().to_string(), n)).unwrap(); }) diff --git a/masonry/src/theme.rs b/masonry/src/theme.rs index b58e751db..ce8b6b47d 100644 --- a/masonry/src/theme.rs +++ b/masonry/src/theme.rs @@ -8,7 +8,7 @@ use crate::core::{ DefaultProperties, PropertySet, PropertyStack, Selector, StyleProperty, StyleSet, }; -use crate::layout::Length; +use crate::layout::{AsUnit, Length}; use crate::palette::css::DIM_GRAY; use crate::parley::{GenericFamily, LineHeight}; use crate::peniko::Color; @@ -23,7 +23,7 @@ use crate::widgets::*; /// it should clear with this color by default. pub const BACKGROUND_COLOR: Color = Color::from_rgb8(0x1D, 0x1D, 0x1D); -pub const BORDER_WIDTH: f64 = 1.; +pub const BORDER_WIDTH: Length = Length::const_px(1.); // Zync color variations from https://tailwindcss.com/docs/colors pub const ZYNC_900: Color = Color::from_rgb8(0x18, 0x18, 0x1b); @@ -59,9 +59,9 @@ pub fn default_property_set() -> DefaultProperties { let mut properties = DefaultProperties::new(); // Badge - properties.insert::(Padding::from_vh(3., 5.)); - properties.insert::(CornerRadius { radius: 999. }); - properties.insert::(BorderWidth { width: 0. }); + properties.insert::(Padding::from_vh(3.px(), 5.px())); + properties.insert::(CornerRadius { radius: 999.px() }); + properties.insert::(BorderWidth { width: 0.px() }); properties.insert::(Background::Color(ACCENT_COLOR)); properties.insert::(BorderColor { color: ZYNC_700 }); { @@ -74,8 +74,8 @@ pub fn default_property_set() -> DefaultProperties { } // Button - properties.insert::(Padding::from_vh(6., 16.)); - properties.insert::(CornerRadius { radius: 6. }); + properties.insert::(Padding::from_vh(6.px(), 16.px())); + properties.insert::(CornerRadius { radius: 6.px() }); properties.insert::(BorderWidth { width: BORDER_WIDTH, }); @@ -103,7 +103,7 @@ pub fn default_property_set() -> DefaultProperties { } // Checkbox - properties.insert::(CornerRadius { radius: 4. }); + properties.insert::(CornerRadius { radius: 4.px() }); properties.insert::(BorderWidth { width: BORDER_WIDTH, }); @@ -142,21 +142,21 @@ pub fn default_property_set() -> DefaultProperties { Length::const_px(16.), Length::const_px(16.), )); - properties.insert::(Padding::all(4.)); + properties.insert::(Padding::all(4.px())); // Divider properties.insert::(ContentColor::new(ZYNC_500)); // Switch - properties.insert::(CornerRadius { radius: 10. }); // Full pill shape + properties.insert::(CornerRadius { radius: 10.px() }); // Full pill shape properties.insert::(BorderWidth { width: BORDER_WIDTH, }); properties.insert::(Background::Color(ZYNC_700)); properties.insert::(BorderColor { color: ZYNC_700 }); properties.insert::(ThumbColor(Color::WHITE)); - properties.insert::(ThumbRadius(8.0)); - properties.insert::(TrackThickness(20.0)); + properties.insert::(ThumbRadius(8.px())); + properties.insert::(TrackThickness(20.px())); { let mut stack = PropertyStack::new(); stack.push( @@ -186,8 +186,8 @@ pub fn default_property_set() -> DefaultProperties { use crate::widgets::Selector as SelectorButton; // Selector - properties.insert::(Padding::from_vh(6., 16.)); - properties.insert::(CornerRadius { radius: 2. }); + properties.insert::(Padding::from_vh(6.px(), 16.px())); + properties.insert::(CornerRadius { radius: 2.px() }); properties.insert::(BorderWidth { width: BORDER_WIDTH, }); @@ -216,7 +216,7 @@ pub fn default_property_set() -> DefaultProperties { } // SelectorItem - properties.insert::(Padding::from_vh(6., 16.)); + properties.insert::(Padding::from_vh(6.px(), 16.px())); properties.insert::(Background::Color(ZYNC_900)); { let mut stack = PropertyStack::new(); @@ -238,8 +238,8 @@ pub fn default_property_set() -> DefaultProperties { properties.insert::(Gap::ZERO); // TextInput - properties.insert::(Padding::from_vh(6., 12.)); - properties.insert::(CornerRadius { radius: 4. }); + properties.insert::(Padding::from_vh(6.px(), 12.px())); + properties.insert::(CornerRadius { radius: 4.px() }); properties.insert::(BorderWidth { width: BORDER_WIDTH, }); @@ -309,7 +309,7 @@ pub fn default_property_set() -> DefaultProperties { } // ProgressBar - properties.insert::(CornerRadius { radius: 2. }); + properties.insert::(CornerRadius { radius: 2.px() }); properties.insert::(BorderWidth { width: BORDER_WIDTH, }); @@ -381,8 +381,8 @@ pub fn default_text_styles(styles: &mut StyleSet) { } fn default_step_input_style(properties: &mut DefaultProperties) { - properties.insert::, _>(Padding::from_vh(6., 0.)); - properties.insert::, _>(CornerRadius { radius: 6. }); + properties.insert::, _>(Padding::from_vh(6.px(), 0.px())); + properties.insert::, _>(CornerRadius { radius: 6.px() }); properties.insert::, _>(BorderWidth { width: BORDER_WIDTH, }); diff --git a/masonry/src/widgets/align.rs b/masonry/src/widgets/align.rs index 68a12183b..bf6445f85 100644 --- a/masonry/src/widgets/align.rs +++ b/masonry/src/widgets/align.rs @@ -17,7 +17,7 @@ use crate::core::{ use crate::core::{MeasureCtx, WidgetMut}; use crate::imaging::Painter; use crate::kurbo::{Axis, Rect, Size}; -use crate::layout::{LayoutSize, LenReq, SizeDef, UnitPoint}; +use crate::layout::{AsUnit, LayoutSize, LenReq, Length, SizeDef, UnitPoint}; // TODO - Have child widget type as generic argument @@ -125,8 +125,8 @@ impl Widget for Align { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { + cross_length: Option, + ) -> Length { let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); @@ -152,7 +152,7 @@ impl Widget for Align { Axis::Horizontal => self.width_factor, Axis::Vertical => self.height_factor, } { - length = child_length * factor; + length = (child_length.get() * factor).px(); } // Never return a length larger than the bounds @@ -258,7 +258,7 @@ mod tests { .with_tag(align_tag) .with_props(( Dimensions::fixed(50.px(), 50.px()), - BorderWidth::all(2.), + BorderWidth::all(2.px()), BorderColor::new(palette::css::BLACK), )); let root = Align::centered(align).prepare(); diff --git a/masonry/src/widgets/badge.rs b/masonry/src/widgets/badge.rs index 1d15b8b59..39bd8e30b 100644 --- a/masonry/src/widgets/badge.rs +++ b/masonry/src/widgets/badge.rs @@ -14,7 +14,7 @@ use crate::core::{ }; use crate::imaging::Painter; use crate::kurbo::{Axis, Size}; -use crate::layout::{LayoutSize, LenReq, SizeDef}; +use crate::layout::{LayoutSize, LenReq, Length, SizeDef}; use crate::parley::style::FontWeight; use crate::widgets::Label; @@ -165,8 +165,8 @@ impl Widget for Badge { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { + cross_length: Option, + ) -> Length { let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); diff --git a/masonry/src/widgets/badged.rs b/masonry/src/widgets/badged.rs index 3e2f681b8..c41912f53 100644 --- a/masonry/src/widgets/badged.rs +++ b/masonry/src/widgets/badged.rs @@ -11,7 +11,7 @@ use crate::core::{ }; use crate::imaging::Painter; use crate::kurbo::{Axis, Point, Size, Vec2}; -use crate::layout::{LayoutSize, LenReq, SizeDef}; +use crate::layout::{LayoutSize, LenReq, Length, SizeDef}; /// Where a badge is placed relative to the content. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -185,8 +185,8 @@ impl Widget for Badged { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { + cross_length: Option, + ) -> Length { let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); ctx.compute_length( diff --git a/masonry/src/widgets/button.rs b/masonry/src/widgets/button.rs index b1ed5da9f..fca9869ee 100644 --- a/masonry/src/widgets/button.rs +++ b/masonry/src/widgets/button.rs @@ -18,7 +18,7 @@ use crate::core::{ }; use crate::imaging::Painter; use crate::kurbo::{Axis, Size}; -use crate::layout::{LayoutSize, LenReq, SizeDef}; +use crate::layout::{LayoutSize, LenReq, Length, SizeDef}; use crate::theme; use crate::widgets::Label; @@ -178,12 +178,8 @@ impl Widget for Button { _props: &PropertiesRef<'_>, axis: Axis, len_req: LenReq, - cross_length: Option, - ) -> f64 { - // TODO: Remove HACK: Until scale factor rework happens, just pretend it's always 1.0. - // https://github.com/linebender/xilem/issues/1264 - let scale = 1.0; - + cross_length: Option, + ) -> Length { let auto_length = len_req.into(); let context_size = LayoutSize::maybe(axis.cross(), cross_length); @@ -202,7 +198,7 @@ impl Widget for Button { // we make sure we will have at least the same height as the default text input. match axis { Axis::Horizontal => length, - Axis::Vertical => length.max(theme::BASIC_WIDGET_HEIGHT.dp(scale)), + Axis::Vertical => length.max(theme::BASIC_WIDGET_HEIGHT), } } @@ -373,9 +369,9 @@ mod tests { harness.edit_root_widget(|mut button| { button.insert_prop(BorderColor { color: red }); - button.insert_prop(BorderWidth { width: 5.0 }); - button.insert_prop(CornerRadius { radius: 20.0 }); - button.insert_prop(Padding::from_vh(3., 8.)); + button.insert_prop(BorderWidth { width: 5.px() }); + button.insert_prop(CornerRadius { radius: 20.px() }); + button.insert_prop(Padding::from_vh(3.px(), 8.px())); let mut label = Button::child_mut(&mut button); label.insert_prop(ContentColor::new(red)); @@ -407,7 +403,7 @@ mod tests { ); let root_widget = NewWidget::new(grid).with_props( PropertySet::new() - .with(Padding::all(20.0)) + .with(Padding::all(20.px())) .with(Gap::new(40.px())), ); @@ -427,19 +423,19 @@ mod tests { { let mut button = Grid::get_mut(&mut grid, 1); let mut button = button.downcast::