Subpixels + Feathering + figures refactor

Much curves
This commit is contained in:
Jonathan Johnson 2023-11-19 10:34:06 -08:00
parent aea9def07d
commit d5bde44e27
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
20 changed files with 144 additions and 155 deletions

8
Cargo.lock generated
View file

@ -623,7 +623,7 @@ dependencies = [
[[package]]
name = "figures"
version = "0.1.0"
source = "git+https://github.com/khonsulabs/figures#4712514fbed861ad530bd48d4e62f8efcaa4df1f"
source = "git+https://github.com/khonsulabs/figures#0745fc0ff95e94ce2181aa1528a30bda3d6152f2"
dependencies = [
"bytemuck",
"euclid",
@ -1089,7 +1089,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kludgine"
version = "0.1.0"
source = "git+https://github.com/khonsulabs/kludgine#ca138e79a4b8ebc3bdd463006b1dadd3269183f3"
source = "git+https://github.com/khonsulabs/kludgine#0530299bbe79431b5fa398182991c63e06d94b90"
dependencies = [
"ahash",
"alot",
@ -1974,9 +1974,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.24"
version = "0.38.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234"
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
dependencies = [
"bitflags 2.4.1",
"errno",

View file

@ -1,7 +1,6 @@
use gooey::widgets::Canvas;
use gooey::{Run, Tick};
use kludgine::figures::units::Px;
use kludgine::figures::{Angle, IntoSigned, Point, Rect, Size};
use kludgine::figures::{Angle, IntoSigned, Point, Px2D, Rect, Size};
use kludgine::shapes::Shape;
use kludgine::text::{Text, TextOrigin};
use kludgine::{Color, DrawableExt};
@ -15,11 +14,11 @@ fn main() -> gooey::Result<()> {
context.gfx.draw_text(
Text::new("Canvas exposes the full power of Kludgine", Color::WHITE)
.origin(TextOrigin::Center)
.translate_by(center - Point::new(Px(0), Px(100))),
.translate_by(center - Point::px(0, 100)),
);
context.gfx.draw_shape(
Shape::filled_rect(
Rect::new(Point::new(Px(-50), Px(-50)), Size::new(Px(100), Px(100))),
Rect::new(Point::px(-50, -50), Size::px(100, 100)),
Color::RED,
)
.translate_by(center)

View file

@ -13,7 +13,7 @@ fn main() -> gooey::Result {
.and("Masked Input Field:")
.and(password.into_input())
.into_rows()
.width(Px(100)..Px(800))
.width(Px::new(100)..Px::new(800))
.scroll()
.centered()
.expand()

View file

@ -8,9 +8,10 @@ use gooey::kludgine::Color;
use gooey::value::Dynamic;
use gooey::widgets::TileMap;
use gooey::{Run, Tick};
use kludgine::figures::FloatConversion;
use kludgine::DrawableExt;
const PLAYER_SIZE: Px = Px(16);
const PLAYER_SIZE: Px = Px::new(16);
#[rustfmt::skip]
const TILES: [TileKind; 64] = {
@ -33,7 +34,7 @@ fn main() -> gooey::Result {
let myself = characters.push(Player {
color: Color::RED,
position: Point::new(TILE_SIZE.0 as f32, TILE_SIZE.0 as f32),
position: Point::new(TILE_SIZE.into_float(), TILE_SIZE.into_float()),
});
let layers = Dynamic::new((Tiles::new(8, 8, TILES), characters));
@ -58,10 +59,11 @@ fn main() -> gooey::Result {
direction.x -= 1.0;
}
let one_second_movement = direction * TILE_SIZE.0 as f32;
let one_second_movement = direction * TILE_SIZE.into_float();
layers.map_mut(|layers| {
layers.1[myself].position += Point::new(
// TODO fix this in figures
one_second_movement.x * elapsed.as_secs_f32(),
one_second_movement.y * elapsed.as_secs_f32(),
)
@ -85,10 +87,7 @@ impl Object for Player {
let zoomed_size = PLAYER_SIZE * zoom;
context.draw_shape(
Shape::filled_rect(
Rect::new(
Point::new(-zoomed_size / 2, -zoomed_size / 2),
Size::squared(zoomed_size),
),
Rect::new(Point::squared(-zoomed_size / 2), Size::squared(zoomed_size)),
self.color,
)
.translate_by(center),

View file

@ -10,7 +10,7 @@ use kludgine::app::winit::event::{
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::figures::units::{Lp, Px, UPx};
use kludgine::figures::{IntoSigned, IsZero, Point, Rect, ScreenScale, Size};
use kludgine::figures::{IntoSigned, Point, Px2D, Rect, ScreenScale, Size, Zero};
use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::{Color, Kludgine};
@ -525,7 +525,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
/// If the alpha channel of `color` is 0, this function does nothing.
pub fn fill(&mut self, color: Color) {
if color.alpha() > 0 {
let visible_rect = Rect::from(self.gfx.region().size - (Px(1), Px(1)));
let visible_rect = Rect::from(self.gfx.region().size - Size::px(1, 1));
let radii = self.get(&CornerRadius);
let radii = radii.map(|r| r.into_px(self.gfx.scale()));
@ -542,23 +542,23 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
/// Strokes an outline around this widget's contents.
pub fn stroke_outline<Unit>(&mut self, color: Color, options: StrokeOptions<Unit>)
where
Unit: ScreenScale<Px = Px, Lp = Lp, UPx = UPx> + IsZero,
Unit: ScreenScale<Px = Px, Lp = Lp, UPx = UPx> + Zero,
{
if color.alpha() > 0 {
let visible_rect = Rect::from(self.gfx.region().size - (Px(1), Px(1)));
let options = options.colored(color).into_px(self.gfx.scale());
let inset = options.line_width;
let visible_rect = Rect::new(
Point::squared(inset),
self.gfx.region().size - Point::new(inset * 2, inset * 2),
);
let radii = self.get(&CornerRadius);
let radii = radii.map(|r| r.into_px(self.gfx.scale()));
let focus_ring = if radii.is_zero() {
Shape::stroked_rect(visible_rect, color, options.into_px(self.gfx.scale()))
Shape::stroked_rect(visible_rect, options.into_px(self.gfx.scale()))
} else {
Shape::stroked_round_rect(
visible_rect,
radii,
color,
options.into_px(self.gfx.scale()),
)
Shape::stroked_round_rect(visible_rect, radii, options)
};
self.gfx.draw_shape(&focus_ring);
}

View file

@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut};
use kludgine::figures::units::{Px, UPx};
use kludgine::figures::{
self, Fraction, IntoSigned, IntoUnsigned, IsZero, Point, Rect, ScreenScale, ScreenUnit, Size,
self, Fraction, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, ScreenUnit, Size, Zero,
};
use kludgine::render::Renderer;
use kludgine::shapes::Shape;
@ -45,12 +45,12 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
let clip_origin = self.renderer.clip_rect().origin.into_signed();
-Point::new(
if clip_origin.x <= self.region.origin.x {
Px(0)
Px::ZERO
} else {
clip_origin.x - self.region.origin.x
},
if clip_origin.y <= self.region.origin.y {
Px(0)
Px::ZERO
} else {
clip_origin.y - self.region.origin.y
},
@ -147,7 +147,7 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
/// Draws a shape at the origin, rotating and scaling as needed.
pub fn draw_shape<'a, Unit>(&mut self, shape: impl Into<Drawable<&'a Shape<Unit, false>, Unit>>)
where
Unit: IsZero + ShaderScalable + figures::ScreenUnit + Copy,
Unit: Zero + ShaderScalable + figures::ScreenUnit + Copy,
{
let mut shape = shape.into();
shape.translation += Point::<Unit>::from_px(self.translation(), self.scale());
@ -171,7 +171,7 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
shape: impl Into<Drawable<&'shape Shape, Unit>>,
texture: &impl TextureSource,
) where
Unit: IsZero + ShaderScalable + figures::ScreenUnit + Copy,
Unit: Zero + ShaderScalable + figures::ScreenUnit + Copy,
i32: From<<Unit as IntoSigned>::Signed>,
Shape: ShapeSource<Unit, true> + 'shape,
{

View file

@ -25,7 +25,7 @@ use std::ops::Sub;
pub use kludgine;
use kludgine::app::winit::error::EventLoopError;
use kludgine::figures::units::UPx;
use kludgine::figures::{Fraction, ScreenUnit};
use kludgine::figures::{Fraction, ScreenUnit, Size};
pub use names::Name;
pub use utils::{Lazy, WithClone};
@ -68,6 +68,27 @@ impl ConstraintLimit {
}
}
/// An extension trait for `Size<ConstraintLimit>`.
pub trait FitMeasuredSize {
/// Returns the result of calling [`ConstraintLimit::fit_measured`] for each
/// matching component in `self` and `measured`.
fn fit_measured<Unit>(self, measured: Size<Unit>, scale: Fraction) -> Size<UPx>
where
Unit: ScreenUnit;
}
impl FitMeasuredSize for Size<ConstraintLimit> {
fn fit_measured<Unit>(self, measured: Size<Unit>, scale: Fraction) -> Size<UPx>
where
Unit: ScreenUnit,
{
Size::new(
self.width.fit_measured(measured.width, scale),
self.height.fit_measured(measured.height, scale),
)
}
}
impl Sub<UPx> for ConstraintLimit {
type Output = Self;

View file

@ -12,7 +12,7 @@ use std::sync::Arc;
use ahash::AHashMap;
use kludgine::figures::units::{Lp, Px, UPx};
use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, IsZero, Rect, ScreenScale, Size};
use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, Rect, ScreenScale, Size, Zero};
use kludgine::shapes::CornerRadii;
use kludgine::Color;
use palette::{IntoColor, Okhsl, OklabHue, Srgb};
@ -399,11 +399,6 @@ pub enum Dimension {
Lp(Lp),
}
impl Dimension {
/// A dimension of 0 pixels.
pub const ZERO: Self = Self::Px(Px(0));
}
impl Default for Dimension {
fn default() -> Self {
Self::ZERO
@ -422,7 +417,9 @@ impl From<Lp> for Dimension {
}
}
impl IsZero for Dimension {
impl Zero for Dimension {
const ZERO: Self = Dimension::Px(Px::ZERO);
fn is_zero(&self) -> bool {
match self {
Dimension::Px(x) => x.is_zero(),

View file

@ -759,7 +759,7 @@ pub trait MakeWidget: Sized {
/// Returns a new widget that renders `color` behind `self`.
fn background_color(self, color: impl IntoValue<Color>) -> Container {
self.contain().pad_by(Px(0)).background_color(color)
self.contain().pad_by(Px::ZERO).background_color(color)
}
/// Wraps `self` with the default padding.

View file

@ -186,10 +186,7 @@ impl WrapperWidget for Align {
let layout = self.measure(available_space, context);
Rect::new(
Point::new(
layout.margin.left.into_signed(),
layout.margin.top.into_signed(),
),
Point::new(layout.margin.left, layout.margin.top).into_signed(),
layout.content.into_signed(),
)
.into()

View file

@ -20,6 +20,7 @@ use crate::styles::{ColorExt, Styles};
use crate::utils::ModifiersExt;
use crate::value::{Dynamic, IntoValue, Value};
use crate::widget::{Callback, EventHandling, MakeWidget, Widget, WidgetRef, HANDLED, IGNORED};
use crate::FitMeasuredSize;
/// A clickable button.
#[derive(Debug)]
@ -362,12 +363,8 @@ impl Widget for Button {
let inset = context.get(&IntrinsicPadding).into_px(context.gfx.scale());
let focus_ring = Shape::stroked_rect(
Rect::new(
Point::new(inset, inset),
context.gfx.region().size - inset * 2,
),
color,
two_lp_stroke,
Rect::new(Point::squared(inset), context.gfx.region().size - inset * 2),
two_lp_stroke.colored(color),
);
context.gfx.draw_shape(&focus_ring);
} else if context.is_default() {
@ -452,22 +449,12 @@ impl Widget for Button {
let padding = context.get(&IntrinsicPadding).into_upx(context.gfx.scale());
let double_padding = padding * 2;
let mounted = self.content.mounted(&mut context.as_event_context());
let available_space = Size::new(
available_space.width - double_padding,
available_space.height - double_padding,
);
let available_space = available_space.map(|space| space - double_padding);
let size = context.for_other(&mounted).layout(available_space);
let size = Size::new(
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
);
let size = available_space.fit_measured(size, context.gfx.scale());
context.set_child_layout(
&mounted,
Rect::new(Point::new(padding, padding), size).into_signed(),
Rect::new(Point::squared(padding), size).into_signed(),
);
size + double_padding
}

View file

@ -7,7 +7,7 @@ use kludgine::figures::Size;
use crate::context::{GraphicsContext, LayoutContext};
use crate::value::Dynamic;
use crate::widget::Widget;
use crate::Tick;
use crate::{ConstraintLimit, Tick};
/// A 2d drawable surface.
#[must_use]
@ -55,7 +55,7 @@ impl Widget for Canvas {
available_space: Size<crate::ConstraintLimit>,
_context: &mut LayoutContext<'_, '_, '_, '_, '_>,
) -> Size<UPx> {
Size::new(available_space.width.max(), available_space.height.max())
available_space.map(ConstraintLimit::max)
}
}

View file

@ -162,7 +162,7 @@ impl WrapperWidget for CheckboxLabel {
let label_inset = checkbox_size + padding;
let size_with_checkbox = Size::new(size.width + label_inset, size.height).into_unsigned();
WrappedLayout {
child: Rect::new(Point::new(label_inset, Px(0)), size),
child: Rect::new(Point::new(label_inset, Px::ZERO), size),
size: size_with_checkbox,
}
}
@ -170,10 +170,7 @@ impl WrapperWidget for CheckboxLabel {
fn redraw_background(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
let checkbox_size = context.get(&LineHeight).into_px(context.gfx.scale());
let padding = context.get(&IntrinsicPadding).into_px(context.gfx.scale());
let checkbox_rect = Rect::new(
Point::new(padding, padding),
Size::new(checkbox_size, checkbox_size),
);
let checkbox_rect = Rect::new(Point::squared(padding), Size::squared(checkbox_size));
let stroke_options = StrokeOptions::lp_wide(Lp::points(2)).into_px(context.gfx.scale());
match self.value.get_tracking_refresh(context) {
state @ (CheckboxState::Checked | CheckboxState::Indeterminant) => {
@ -196,7 +193,7 @@ impl WrapperWidget for CheckboxLabel {
icon_area.origin.y,
))
.build()
.stroke(text_color, stroke_options),
.stroke(stroke_options.colored(text_color)),
);
} else {
context.gfx.draw_shape(
@ -206,15 +203,16 @@ impl WrapperWidget for CheckboxLabel {
center.y,
))
.build()
.stroke(text_color, stroke_options),
.stroke(stroke_options.colored(text_color)),
);
}
}
CheckboxState::Unchecked => {
let color = context.get(&OutlineColor);
context
.gfx
.draw_shape(&Shape::stroked_rect(checkbox_rect, color, stroke_options));
context.gfx.draw_shape(&Shape::stroked_rect(
checkbox_rect,
stroke_options.colored(color),
));
}
}
}

View file

@ -102,10 +102,7 @@ impl WrapperWidget for Expand {
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
) -> WrappedLayout {
let available_space = Size::new(
ConstraintLimit::Fill(available_space.width.max()),
ConstraintLimit::Fill(available_space.height.max()),
);
let available_space = available_space.map(|lim| ConstraintLimit::Fill(lim.max()));
let child = self.child.mounted(&mut context.as_event_context());
let size = context.for_other(&child).layout(available_space);

View file

@ -522,7 +522,7 @@ where
) -> (Point<Px>, Px) {
if measured.glyphs.is_empty() || (cursor.offset == 0 && cursor.affinity == Affinity::Before)
{
return (Point::default(), Px(0));
return (Point::default(), Px::ZERO);
}
// Space between glyphs isn't represented in the glyphs. If the cursor rests
@ -553,7 +553,7 @@ where
) {
(Ordering::Less | Ordering::Equal, Ordering::Less) => {
// cosmic text may have grouped multiple graphemes into a single glyph.
let mut grapheme_offset = Px(0);
let mut grapheme_offset = Px::ZERO;
if glyph.info.start < cursor.offset {
let clustered_bytes = glyph.info.end - glyph.info.start;
if clustered_bytes > 1 {
@ -567,8 +567,8 @@ where
return (
Point::new(
rect.origin.x + grapheme_offset,
measured.line_height.saturating_mul(Px(
i32::try_from(glyph.info.line).unwrap_or(i32::MAX)
measured.line_height.saturating_mul(Px::new(
i32::try_from(glyph.info.line).unwrap_or(i32::MAX),
)),
),
rect.size.width,
@ -586,9 +586,9 @@ where
if closest_after_index == usize::MAX {
let bottom_right = &measured.glyphs[bottom_right_index];
let bottom_y = measured
.line_height
.saturating_mul(Px(i32::try_from(bottom_right.info.line).unwrap_or(i32::MAX)));
let bottom_y = measured.line_height.saturating_mul(Px::new(
i32::try_from(bottom_right.info.line).unwrap_or(i32::MAX),
));
// No glyph could be found that started/contained the cursors offset.
let mut bottom_right_cursor = Point::new(
bottom_right_rect.origin.x + bottom_right_rect.size.width,
@ -609,7 +609,7 @@ where
}
// The cursor should be placed after the bottom_right glyph
(bottom_right_cursor, Px(0))
(bottom_right_cursor, Px::ZERO)
} else {
let before = &measured.glyphs[closest_before_index];
let after = &measured.glyphs[closest_after_index];
@ -617,7 +617,7 @@ where
let after_rect = after.rect();
let before_y = measured
.line_height
.saturating_mul(Px(i32::try_from(before.info.line).unwrap_or(i32::MAX)));
.saturating_mul(Px::new(i32::try_from(before.info.line).unwrap_or(i32::MAX)));
if before.info.line == after.info.line {
let before_right = before_rect.origin.x + before_rect.size.width;
@ -639,7 +639,10 @@ where
origin.x += before_rect.size.width;
(origin, before_y)
}
Affinity::After => (Point::new(Px(0), before_y + measured.line_height), Px(0)),
Affinity::After => (
Point::new(Px::ZERO, before_y + measured.line_height),
Px::ZERO,
),
}
}
}
@ -665,7 +668,7 @@ where
let mut closest: Option<(Cursor, i32)> = None;
let mut current_line = usize::MAX;
let mut current_line_y = Px(0);
let mut current_line_y = Px::ZERO;
for glyph in &cache.measured.glyphs {
if current_line != glyph.info.line {
current_line = glyph.info.line;
@ -673,7 +676,7 @@ where
current_line_y = cache
.measured
.line_height
.saturating_mul(Px(i32::try_from(current_line).unwrap_or(i32::MAX)));
.saturating_mul(Px::new(i32::try_from(current_line).unwrap_or(i32::MAX)));
}
let rect = glyph.rect();
let relative = location - Point::new(rect.origin.x, current_line_y);
@ -704,7 +707,7 @@ where
// Make relative be relative to the center of the glyph for a nearest search.
let relative = relative + rect.size / 2;
let xy = (relative.x.0.saturating_mul(relative.y.0)).saturating_abs();
let xy = (relative.x.get().saturating_mul(relative.y.get())).saturating_abs();
match closest {
Some((_, closest_xy)) if xy < closest_xy => {
closest = Some((
@ -872,7 +875,7 @@ where
context.gfx.draw_shape(
Shape::filled_rect(
Rect::new(
Point::new(Px(0), bottom_of_first_line),
Point::new(Px::ZERO, bottom_of_first_line),
Size::new(size.width.into_signed(), distance_between),
),
highlight,
@ -884,7 +887,7 @@ where
context.gfx.draw_shape(
Shape::filled_rect(
Rect::new(
Point::new(Px(0), end_position.y),
Point::new(Px::ZERO, end_position.y),
Size::new(end_position.x + end_width, cache.measured.line_height),
),
highlight,
@ -939,7 +942,7 @@ where
let cache = self.layout_text(Some(width.into_signed()), &mut context.graphics);
cache.measured.size.into_unsigned() + Size::new(padding * 2, padding * 2)
cache.measured.size.into_unsigned() + Size::squared(padding * 2)
}
fn keyboard_input(

View file

@ -98,10 +98,7 @@ impl WrapperWidget for Resize {
let size = if let (Some(width), Some(height)) =
(self.width.exact_dimension(), self.height.exact_dimension())
{
Size::new(
width.into_upx(context.gfx.scale()),
height.into_upx(context.gfx.scale()),
)
Size::new(width, height).map(|i| i.into_upx(context.gfx.scale()))
} else {
let available_space = Size::new(
override_constraint(available_space.width, self.width, context.gfx.scale()),

View file

@ -186,7 +186,7 @@ impl Widget for Scroll {
let max_scroll_x = if self.enabled.x {
-self.horizontal_bar.amount_hidden
} else {
Px(0)
Px::ZERO
};
self.vertical_bar =
@ -194,7 +194,7 @@ impl Widget for Scroll {
let max_scroll_y = if self.enabled.y {
-self.vertical_bar.amount_hidden
} else {
Px(0)
Px::ZERO
};
let new_max_scroll = Point::new(max_scroll_x, max_scroll_y);
if current_max_scroll != new_max_scroll {

View file

@ -53,9 +53,9 @@ impl<T> Slider<T> {
value: value.into_dynamic(),
minimum: min.into_value(),
maximum: max.into_value(),
knob_size: UPx(0),
knob_size: UPx::ZERO,
horizontal: true,
rendered_size: Px(0),
rendered_size: Px::ZERO,
}
}
@ -135,7 +135,7 @@ where
} else {
position.y
};
let position = position.clamp(Px(0), self.rendered_size);
let position = position.clamp(Px::ZERO, self.rendered_size);
let percent = position.into_float() / self.rendered_size.into_float();
let min = self.minimum.get();
let max = self.maximum.get();

View file

@ -172,7 +172,7 @@ impl Widget for Stack {
Rect::new(
self.layout
.orientation
.make_point(layout.offset, UPx(0))
.make_point(layout.offset, UPx::ZERO)
.into_signed(),
self.layout
.orientation
@ -313,9 +313,9 @@ impl Layout {
orientation,
children: OrderedLots::new(),
layouts: Vec::new(),
other: UPx(0),
other: UPx::ZERO,
total_weights: 0,
allocated_space: (UPx(0), Lp(0)),
allocated_space: (UPx::ZERO, Lp::ZERO),
fractional: Vec::new(),
fit_to_content: Vec::new(),
premeasured: Vec::new(),
@ -370,12 +370,12 @@ impl Layout {
let layout = match child {
StackDimension::FitContent => {
self.fit_to_content.push(id);
UPx(0)
UPx::ZERO
}
StackDimension::Fractional { weight } => {
self.total_weights += u32::from(weight);
self.fractional.push((id, weight));
UPx(0)
UPx::ZERO
}
StackDimension::Measured { min, .. } => {
self.premeasured.push(id);
@ -389,7 +389,7 @@ impl Layout {
self.layouts.insert(
index,
StackLayout {
offset: UPx(0),
offset: UPx::ZERO,
size: layout,
},
);
@ -412,7 +412,7 @@ impl Layout {
let needs_final_layout = !matches!(other_constraint, ConstraintLimit::Fill(_));
// Measure the children that fit their content
self.other = UPx(0);
self.other = UPx::ZERO;
for &id in &self.fit_to_content {
let index = self.children.index_of_id(id).expect("child not found");
let (measured, other) = self.orientation.split_size(measure(
@ -484,7 +484,7 @@ impl Layout {
};
// Finally, compute the offsets of all of the widgets.
let mut offset = UPx(0);
let mut offset = UPx::ZERO;
for index in 0..self.children.len() {
self.layouts[index].offset = offset;
offset += self.layouts[index].size;
@ -593,7 +593,7 @@ mod tests {
orientation.make_size(measured, other)
});
assert_eq!(computed_size, expected_size);
let mut offset = UPx(0);
let mut offset = UPx::ZERO;
for ((index, &child), &expected) in flex.iter().enumerate().zip(expected) {
assert_eq!(
child.size,
@ -635,11 +635,11 @@ mod tests {
fn size_to_fit() {
assert_measured_children(
&[Child::new(3, 1), Child::new(3, 1), Child::new(3, 1)],
ConstraintLimit::SizeToFit(UPx(10)),
ConstraintLimit::SizeToFit(UPx(10)),
&[UPx(3), UPx(3), UPx(3)],
UPx(9),
UPx(1),
ConstraintLimit::SizeToFit(UPx::new(10)),
ConstraintLimit::SizeToFit(UPx::new(10)),
&[UPx::new(3), UPx::new(3), UPx::new(3)],
UPx::new(9),
UPx::new(1),
);
}
@ -660,11 +660,11 @@ mod tests {
Child::new(3, 1).weighted(1),
Child::new(3, 1).weighted(1),
],
ConstraintLimit::Fill(UPx(10)),
ConstraintLimit::SizeToFit(UPx(10)),
&[UPx(4), UPx(3), UPx(3)],
UPx(10),
UPx(7), // 20 / 3 = 6.666, rounded up is 7
ConstraintLimit::Fill(UPx::new(10)),
ConstraintLimit::SizeToFit(UPx::new(10)),
&[UPx::new(4), UPx::new(3), UPx::new(3)],
UPx::new(10),
UPx::new(7), // 20 / 3 = 6.666, rounded up is 7
);
// Same as above, but with an 11px box. This creates a leftover of 3 px
// (11 % 4), adding 1px to all three children.
@ -674,11 +674,11 @@ mod tests {
Child::new(3, 1).weighted(1),
Child::new(3, 1).weighted(1),
],
ConstraintLimit::Fill(UPx(11)),
ConstraintLimit::SizeToFit(UPx(11)),
&[UPx(5), UPx(3), UPx(3)],
UPx(11),
UPx(7), // 20 / 3 = 6.666, rounded up is 7
ConstraintLimit::Fill(UPx::new(11)),
ConstraintLimit::SizeToFit(UPx::new(11)),
&[UPx::new(5), UPx::new(3), UPx::new(3)],
UPx::new(11),
UPx::new(7), // 20 / 3 = 6.666, rounded up is 7
);
// 12px box. This creates no leftover.
assert_measured_children(
@ -687,11 +687,11 @@ mod tests {
Child::new(3, 1).weighted(1),
Child::new(3, 1).weighted(1),
],
ConstraintLimit::Fill(UPx(12)),
ConstraintLimit::SizeToFit(UPx(12)),
&[UPx(6), UPx(3), UPx(3)],
UPx(12),
UPx(4), // 20 / 6 = 3.666, rounded up is 4
ConstraintLimit::Fill(UPx::new(12)),
ConstraintLimit::SizeToFit(UPx::new(12)),
&[UPx::new(6), UPx::new(3), UPx::new(3)],
UPx::new(12),
UPx::new(4), // 20 / 6 = 3.666, rounded up is 4
);
// 13px box. This creates a leftover of 1 px (13 % 4), adding 1px only
// to the final child
@ -701,11 +701,11 @@ mod tests {
Child::new(3, 1).weighted(1),
Child::new(3, 1).weighted(1),
],
ConstraintLimit::Fill(UPx(13)),
ConstraintLimit::SizeToFit(UPx(13)),
&[UPx(6), UPx(3), UPx(4)],
UPx(13),
UPx(4), // 20 / 6 = 3.666, rounded up is 4
ConstraintLimit::Fill(UPx::new(13)),
ConstraintLimit::SizeToFit(UPx::new(13)),
&[UPx::new(6), UPx::new(3), UPx::new(4)],
UPx::new(13),
UPx::new(4), // 20 / 6 = 3.666, rounded up is 4
);
}
@ -713,15 +713,15 @@ mod tests {
fn fixed_size() {
assert_measured_children(
&[
Child::new(3, 1).fixed_size(UPx(7)),
Child::new(3, 1).fixed_size(UPx::new(7)),
Child::new(3, 1).weighted(1),
Child::new(3, 1).weighted(1),
],
ConstraintLimit::Fill(UPx(15)),
ConstraintLimit::SizeToFit(UPx(15)),
&[UPx(7), UPx(4), UPx(4)],
UPx(15),
UPx(1),
ConstraintLimit::Fill(UPx::new(15)),
ConstraintLimit::SizeToFit(UPx::new(15)),
&[UPx::new(7), UPx::new(4), UPx::new(4)],
UPx::new(15),
UPx::new(1),
);
}
}

View file

@ -396,7 +396,7 @@ where
let min_width = resize
.width
.minimum()
.map_or(Px(0), |width| width.into_px(graphics.scale()));
.map_or(Px::ZERO, |width| width.into_px(graphics.scale()));
let max_width = resize
.width
.maximum()
@ -404,7 +404,7 @@ where
let min_height = resize
.height
.minimum()
.map_or(Px(0), |height| height.into_px(graphics.scale()));
.map_or(Px::ZERO, |height| height.into_px(graphics.scale()));
let max_height = resize
.height
.maximum()
@ -566,15 +566,9 @@ where
}
let actual_size = layout_context.layout(if is_expanded {
Size::new(
ConstraintLimit::Fill(window_size.width),
ConstraintLimit::Fill(window_size.height),
)
window_size.map(ConstraintLimit::Fill)
} else {
Size::new(
ConstraintLimit::SizeToFit(window_size.width),
ConstraintLimit::SizeToFit(window_size.height),
)
window_size.map(ConstraintLimit::SizeToFit)
});
let render_size = actual_size.min(window_size);
if actual_size != window_size && !resizable {