Merge pull request #2192 from iced-rs/fix/layout-inconsistencies
Layout consistency
This commit is contained in:
commit
50c310fd9f
73 changed files with 1310 additions and 788 deletions
|
|
@ -6,7 +6,7 @@ use crate::renderer;
|
|||
use crate::widget;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Clipboard, Color, Layout, Length, Rectangle, Shell, Vector, Widget,
|
||||
Clipboard, Color, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
use std::any::Any;
|
||||
|
|
@ -296,12 +296,8 @@ where
|
|||
self.widget.diff(tree);
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.widget.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.widget.height()
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.widget.size()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -466,12 +462,8 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
|
|||
where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.element.widget.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.element.widget.height()
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.element.widget.size()
|
||||
}
|
||||
|
||||
fn tag(&self) -> tree::Tag {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub mod flex;
|
|||
pub use limits::Limits;
|
||||
pub use node::Node;
|
||||
|
||||
use crate::{Point, Rectangle, Size, Vector};
|
||||
use crate::{Length, Padding, Point, Rectangle, Size, Vector};
|
||||
|
||||
/// The bounds of a [`Node`] and its children, using absolute coordinates.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -71,12 +71,12 @@ pub fn next_to_each_other(
|
|||
left: impl FnOnce(&Limits) -> Node,
|
||||
right: impl FnOnce(&Limits) -> Node,
|
||||
) -> Node {
|
||||
let mut left_node = left(limits);
|
||||
let left_node = left(limits);
|
||||
let left_size = left_node.size();
|
||||
|
||||
let right_limits = limits.shrink(Size::new(left_size.width + spacing, 0.0));
|
||||
|
||||
let mut right_node = right(&right_limits);
|
||||
let right_node = right(&right_limits);
|
||||
let right_size = right_node.size();
|
||||
|
||||
let (left_y, right_y) = if left_size.height > right_size.height {
|
||||
|
|
@ -85,14 +85,106 @@ pub fn next_to_each_other(
|
|||
((right_size.height - left_size.height) / 2.0, 0.0)
|
||||
};
|
||||
|
||||
left_node.move_to(Point::new(0.0, left_y));
|
||||
right_node.move_to(Point::new(left_size.width + spacing, right_y));
|
||||
|
||||
Node::with_children(
|
||||
Size::new(
|
||||
left_size.width + spacing + right_size.width,
|
||||
left_size.height.max(right_size.height),
|
||||
),
|
||||
vec![left_node, right_node],
|
||||
vec![
|
||||
left_node.move_to(Point::new(0.0, left_y)),
|
||||
right_node.move_to(Point::new(left_size.width + spacing, right_y)),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the resulting [`Node`] that fits the [`Limits`] given
|
||||
/// some width and height requirements and no intrinsic size.
|
||||
pub fn atomic(
|
||||
limits: &Limits,
|
||||
width: impl Into<Length>,
|
||||
height: impl Into<Length>,
|
||||
) -> Node {
|
||||
let width = width.into();
|
||||
let height = height.into();
|
||||
|
||||
Node::new(limits.resolve(width, height, Size::ZERO))
|
||||
}
|
||||
|
||||
/// Computes the resulting [`Node`] that fits the [`Limits`] given
|
||||
/// some width and height requirements and a closure that produces
|
||||
/// the intrinsic [`Size`] inside the given [`Limits`].
|
||||
pub fn sized(
|
||||
limits: &Limits,
|
||||
width: impl Into<Length>,
|
||||
height: impl Into<Length>,
|
||||
f: impl FnOnce(&Limits) -> Size,
|
||||
) -> Node {
|
||||
let width = width.into();
|
||||
let height = height.into();
|
||||
|
||||
let limits = limits.width(width).height(height);
|
||||
let intrinsic_size = f(&limits);
|
||||
|
||||
Node::new(limits.resolve(width, height, intrinsic_size))
|
||||
}
|
||||
|
||||
/// Computes the resulting [`Node`] that fits the [`Limits`] given
|
||||
/// some width and height requirements and a closure that produces
|
||||
/// the content [`Node`] inside the given [`Limits`].
|
||||
pub fn contained(
|
||||
limits: &Limits,
|
||||
width: impl Into<Length>,
|
||||
height: impl Into<Length>,
|
||||
f: impl FnOnce(&Limits) -> Node,
|
||||
) -> Node {
|
||||
let width = width.into();
|
||||
let height = height.into();
|
||||
|
||||
let limits = limits.width(width).height(height);
|
||||
let content = f(&limits);
|
||||
|
||||
Node::with_children(
|
||||
limits.resolve(width, height, content.size()),
|
||||
vec![content],
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the [`Node`] that fits the [`Limits`] given some width, height, and
|
||||
/// [`Padding`] requirements and a closure that produces the content [`Node`]
|
||||
/// inside the given [`Limits`].
|
||||
pub fn padded(
|
||||
limits: &Limits,
|
||||
width: impl Into<Length>,
|
||||
height: impl Into<Length>,
|
||||
padding: impl Into<Padding>,
|
||||
layout: impl FnOnce(&Limits) -> Node,
|
||||
) -> Node {
|
||||
positioned(limits, width, height, padding, layout, |content, _| content)
|
||||
}
|
||||
|
||||
/// Computes a [`padded`] [`Node`] with a positioning step.
|
||||
pub fn positioned(
|
||||
limits: &Limits,
|
||||
width: impl Into<Length>,
|
||||
height: impl Into<Length>,
|
||||
padding: impl Into<Padding>,
|
||||
layout: impl FnOnce(&Limits) -> Node,
|
||||
position: impl FnOnce(Node, Size) -> Node,
|
||||
) -> Node {
|
||||
let width = width.into();
|
||||
let height = height.into();
|
||||
let padding = padding.into();
|
||||
|
||||
let limits = limits.width(width).height(height);
|
||||
let content = layout(&limits.shrink(padding));
|
||||
let padding = padding.fit(content.size(), limits.max());
|
||||
|
||||
let size = limits
|
||||
.shrink(padding)
|
||||
.resolve(width, height, content.size());
|
||||
|
||||
Node::with_children(
|
||||
size.expand(padding),
|
||||
vec![position(content.move_to((padding.left, padding.top)), size)],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::Element;
|
|||
|
||||
use crate::layout::{Limits, Node};
|
||||
use crate::widget;
|
||||
use crate::{Alignment, Padding, Point, Size};
|
||||
use crate::{Alignment, Length, Padding, Point, Size};
|
||||
|
||||
/// The main axis of a flex layout.
|
||||
#[derive(Debug)]
|
||||
|
|
@ -47,7 +47,7 @@ impl Axis {
|
|||
}
|
||||
}
|
||||
|
||||
fn pack(&self, main: f32, cross: f32) -> (f32, f32) {
|
||||
fn pack<T>(&self, main: T, cross: T) -> (T, T) {
|
||||
match self {
|
||||
Axis::Horizontal => (main, cross),
|
||||
Axis::Vertical => (cross, main),
|
||||
|
|
@ -63,6 +63,8 @@ pub fn resolve<Message, Renderer>(
|
|||
axis: Axis,
|
||||
renderer: &Renderer,
|
||||
limits: &Limits,
|
||||
width: Length,
|
||||
height: Length,
|
||||
padding: Padding,
|
||||
spacing: f32,
|
||||
align_items: Alignment,
|
||||
|
|
@ -72,26 +74,64 @@ pub fn resolve<Message, Renderer>(
|
|||
where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
let limits = limits.pad(padding);
|
||||
let limits = limits.width(width).height(height).shrink(padding);
|
||||
let total_spacing = spacing * items.len().saturating_sub(1) as f32;
|
||||
let max_cross = axis.cross(limits.max());
|
||||
|
||||
let mut fill_sum = 0;
|
||||
let mut cross = axis.cross(limits.min()).max(axis.cross(limits.fill()));
|
||||
let mut fill_main_sum = 0;
|
||||
let mut cross = match axis {
|
||||
Axis::Horizontal => match height {
|
||||
Length::Shrink => 0.0,
|
||||
_ => max_cross,
|
||||
},
|
||||
Axis::Vertical => match width {
|
||||
Length::Shrink => 0.0,
|
||||
_ => max_cross,
|
||||
},
|
||||
};
|
||||
|
||||
let mut available = axis.main(limits.max()) - total_spacing;
|
||||
|
||||
let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
|
||||
nodes.resize(items.len(), Node::default());
|
||||
|
||||
for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() {
|
||||
let fill_factor = match axis {
|
||||
Axis::Horizontal => child.as_widget().width(),
|
||||
Axis::Vertical => child.as_widget().height(),
|
||||
}
|
||||
.fill_factor();
|
||||
let (fill_main_factor, fill_cross_factor) = {
|
||||
let size = child.as_widget().size();
|
||||
|
||||
if fill_factor == 0 {
|
||||
let (max_width, max_height) = axis.pack(available, max_cross);
|
||||
axis.pack(size.width.fill_factor(), size.height.fill_factor())
|
||||
};
|
||||
|
||||
if fill_main_factor == 0 {
|
||||
if fill_cross_factor == 0 {
|
||||
let (max_width, max_height) = axis.pack(available, max_cross);
|
||||
|
||||
let child_limits =
|
||||
Limits::new(Size::ZERO, Size::new(max_width, max_height));
|
||||
|
||||
let layout =
|
||||
child.as_widget().layout(tree, renderer, &child_limits);
|
||||
let size = layout.size();
|
||||
|
||||
available -= axis.main(size);
|
||||
cross = cross.max(axis.cross(size));
|
||||
|
||||
nodes[i] = layout;
|
||||
}
|
||||
} else {
|
||||
fill_main_sum += fill_main_factor;
|
||||
}
|
||||
}
|
||||
|
||||
for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() {
|
||||
let (fill_main_factor, fill_cross_factor) = {
|
||||
let size = child.as_widget().size();
|
||||
|
||||
axis.pack(size.width.fill_factor(), size.height.fill_factor())
|
||||
};
|
||||
|
||||
if fill_main_factor == 0 && fill_cross_factor != 0 {
|
||||
let (max_width, max_height) = axis.pack(available, cross);
|
||||
|
||||
let child_limits =
|
||||
Limits::new(Size::ZERO, Size::new(max_width, max_height));
|
||||
|
|
@ -101,34 +141,47 @@ where
|
|||
let size = layout.size();
|
||||
|
||||
available -= axis.main(size);
|
||||
cross = cross.max(axis.cross(size));
|
||||
cross = cross.max(axis.cross(layout.size()));
|
||||
|
||||
nodes[i] = layout;
|
||||
} else {
|
||||
fill_sum += fill_factor;
|
||||
}
|
||||
}
|
||||
|
||||
let remaining = available.max(0.0);
|
||||
let remaining = match axis {
|
||||
Axis::Horizontal => match width {
|
||||
Length::Shrink => 0.0,
|
||||
_ => available.max(0.0),
|
||||
},
|
||||
Axis::Vertical => match height {
|
||||
Length::Shrink => 0.0,
|
||||
_ => available.max(0.0),
|
||||
},
|
||||
};
|
||||
|
||||
for (i, (child, tree)) in items.iter().zip(trees).enumerate() {
|
||||
let fill_factor = match axis {
|
||||
Axis::Horizontal => child.as_widget().width(),
|
||||
Axis::Vertical => child.as_widget().height(),
|
||||
}
|
||||
.fill_factor();
|
||||
let (fill_main_factor, fill_cross_factor) = {
|
||||
let size = child.as_widget().size();
|
||||
|
||||
axis.pack(size.width.fill_factor(), size.height.fill_factor())
|
||||
};
|
||||
|
||||
if fill_main_factor != 0 {
|
||||
let max_main =
|
||||
remaining * fill_main_factor as f32 / fill_main_sum as f32;
|
||||
|
||||
if fill_factor != 0 {
|
||||
let max_main = remaining * fill_factor as f32 / fill_sum as f32;
|
||||
let min_main = if max_main.is_infinite() {
|
||||
0.0
|
||||
} else {
|
||||
max_main
|
||||
};
|
||||
|
||||
let (min_width, min_height) =
|
||||
axis.pack(min_main, axis.cross(limits.min()));
|
||||
let max_cross = if fill_cross_factor == 0 {
|
||||
max_cross
|
||||
} else {
|
||||
cross
|
||||
};
|
||||
|
||||
let (min_width, min_height) = axis.pack(min_main, 0.0);
|
||||
let (max_width, max_height) = axis.pack(max_main, max_cross);
|
||||
|
||||
let child_limits = Limits::new(
|
||||
|
|
@ -154,18 +207,18 @@ where
|
|||
|
||||
let (x, y) = axis.pack(main, pad.1);
|
||||
|
||||
node.move_to(Point::new(x, y));
|
||||
node.move_to_mut(Point::new(x, y));
|
||||
|
||||
match axis {
|
||||
Axis::Horizontal => {
|
||||
node.align(
|
||||
node.align_mut(
|
||||
Alignment::Start,
|
||||
align_items,
|
||||
Size::new(0.0, cross),
|
||||
);
|
||||
}
|
||||
Axis::Vertical => {
|
||||
node.align(
|
||||
node.align_mut(
|
||||
align_items,
|
||||
Alignment::Start,
|
||||
Size::new(cross, 0.0),
|
||||
|
|
@ -178,8 +231,12 @@ where
|
|||
main += axis.main(size);
|
||||
}
|
||||
|
||||
let (width, height) = axis.pack(main - pad.0, cross);
|
||||
let size = limits.resolve(Size::new(width, height));
|
||||
let (intrinsic_width, intrinsic_height) = axis.pack(main - pad.0, cross);
|
||||
let size = limits.resolve(
|
||||
width,
|
||||
height,
|
||||
Size::new(intrinsic_width, intrinsic_height),
|
||||
);
|
||||
|
||||
Node::with_children(size.pad(padding), nodes)
|
||||
Node::with_children(size.expand(padding), nodes)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
#![allow(clippy::manual_clamp)]
|
||||
use crate::{Length, Padding, Size};
|
||||
use crate::{Length, Size};
|
||||
|
||||
/// A set of size constraints for layouting.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Limits {
|
||||
min: Size,
|
||||
max: Size,
|
||||
fill: Size,
|
||||
}
|
||||
|
||||
impl Limits {
|
||||
|
|
@ -14,16 +13,11 @@ impl Limits {
|
|||
pub const NONE: Limits = Limits {
|
||||
min: Size::ZERO,
|
||||
max: Size::INFINITY,
|
||||
fill: Size::INFINITY,
|
||||
};
|
||||
|
||||
/// Creates new [`Limits`] with the given minimum and maximum [`Size`].
|
||||
pub const fn new(min: Size, max: Size) -> Limits {
|
||||
Limits {
|
||||
min,
|
||||
max,
|
||||
fill: Size::INFINITY,
|
||||
}
|
||||
Limits { min, max }
|
||||
}
|
||||
|
||||
/// Returns the minimum [`Size`] of the [`Limits`].
|
||||
|
|
@ -36,26 +30,15 @@ impl Limits {
|
|||
self.max
|
||||
}
|
||||
|
||||
/// Returns the fill [`Size`] of the [`Limits`].
|
||||
pub fn fill(&self) -> Size {
|
||||
self.fill
|
||||
}
|
||||
|
||||
/// Applies a width constraint to the current [`Limits`].
|
||||
pub fn width(mut self, width: impl Into<Length>) -> Limits {
|
||||
match width.into() {
|
||||
Length::Shrink => {
|
||||
self.fill.width = self.min.width;
|
||||
}
|
||||
Length::Fill | Length::FillPortion(_) => {
|
||||
self.fill.width = self.fill.width.min(self.max.width);
|
||||
}
|
||||
Length::Shrink | Length::Fill | Length::FillPortion(_) => {}
|
||||
Length::Fixed(amount) => {
|
||||
let new_width = amount.min(self.max.width).max(self.min.width);
|
||||
|
||||
self.min.width = new_width;
|
||||
self.max.width = new_width;
|
||||
self.fill.width = new_width;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,19 +48,13 @@ impl Limits {
|
|||
/// Applies a height constraint to the current [`Limits`].
|
||||
pub fn height(mut self, height: impl Into<Length>) -> Limits {
|
||||
match height.into() {
|
||||
Length::Shrink => {
|
||||
self.fill.height = self.min.height;
|
||||
}
|
||||
Length::Fill | Length::FillPortion(_) => {
|
||||
self.fill.height = self.fill.height.min(self.max.height);
|
||||
}
|
||||
Length::Shrink | Length::Fill | Length::FillPortion(_) => {}
|
||||
Length::Fixed(amount) => {
|
||||
let new_height =
|
||||
amount.min(self.max.height).max(self.min.height);
|
||||
|
||||
self.min.height = new_height;
|
||||
self.max.height = new_height;
|
||||
self.fill.height = new_height;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,13 +89,10 @@ impl Limits {
|
|||
self
|
||||
}
|
||||
|
||||
/// Shrinks the current [`Limits`] to account for the given padding.
|
||||
pub fn pad(&self, padding: Padding) -> Limits {
|
||||
self.shrink(Size::new(padding.horizontal(), padding.vertical()))
|
||||
}
|
||||
|
||||
/// Shrinks the current [`Limits`] by the given [`Size`].
|
||||
pub fn shrink(&self, size: Size) -> Limits {
|
||||
pub fn shrink(&self, size: impl Into<Size>) -> Limits {
|
||||
let size = size.into();
|
||||
|
||||
let min = Size::new(
|
||||
(self.min().width - size.width).max(0.0),
|
||||
(self.min().height - size.height).max(0.0),
|
||||
|
|
@ -129,12 +103,7 @@ impl Limits {
|
|||
(self.max().height - size.height).max(0.0),
|
||||
);
|
||||
|
||||
let fill = Size::new(
|
||||
(self.fill.width - size.width).max(0.0),
|
||||
(self.fill.height - size.height).max(0.0),
|
||||
);
|
||||
|
||||
Limits { min, max, fill }
|
||||
Limits { min, max }
|
||||
}
|
||||
|
||||
/// Removes the minimum width constraint for the current [`Limits`].
|
||||
|
|
@ -142,22 +111,39 @@ impl Limits {
|
|||
Limits {
|
||||
min: Size::ZERO,
|
||||
max: self.max,
|
||||
fill: self.fill,
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the resulting [`Size`] that fits the [`Limits`] given the
|
||||
/// intrinsic size of some content.
|
||||
pub fn resolve(&self, intrinsic_size: Size) -> Size {
|
||||
Size::new(
|
||||
intrinsic_size
|
||||
.width
|
||||
.min(self.max.width)
|
||||
.max(self.fill.width),
|
||||
intrinsic_size
|
||||
/// Computes the resulting [`Size`] that fits the [`Limits`] given
|
||||
/// some width and height requirements and the intrinsic size of
|
||||
/// some content.
|
||||
pub fn resolve(
|
||||
&self,
|
||||
width: impl Into<Length>,
|
||||
height: impl Into<Length>,
|
||||
intrinsic_size: Size,
|
||||
) -> Size {
|
||||
let width = match width.into() {
|
||||
Length::Fill | Length::FillPortion(_) => self.max.width,
|
||||
Length::Fixed(amount) => {
|
||||
amount.min(self.max.width).max(self.min.width)
|
||||
}
|
||||
Length::Shrink => {
|
||||
intrinsic_size.width.min(self.max.width).max(self.min.width)
|
||||
}
|
||||
};
|
||||
|
||||
let height = match height.into() {
|
||||
Length::Fill | Length::FillPortion(_) => self.max.height,
|
||||
Length::Fixed(amount) => {
|
||||
amount.min(self.max.height).max(self.min.height)
|
||||
}
|
||||
Length::Shrink => intrinsic_size
|
||||
.height
|
||||
.min(self.max.height)
|
||||
.max(self.fill.height),
|
||||
)
|
||||
.max(self.min.height),
|
||||
};
|
||||
|
||||
Size::new(width, height)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{Alignment, Point, Rectangle, Size, Vector};
|
||||
use crate::{Alignment, Padding, Point, Rectangle, Size, Vector};
|
||||
|
||||
/// The bounds of an element and its children.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
@ -26,6 +26,14 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`Node`] that wraps a single child with some [`Padding`].
|
||||
pub fn container(child: Self, padding: Padding) -> Self {
|
||||
Self::with_children(
|
||||
child.bounds.size().expand(padding),
|
||||
vec![child.move_to(Point::new(padding.left, padding.top))],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the [`Size`] of the [`Node`].
|
||||
pub fn size(&self) -> Size {
|
||||
Size::new(self.bounds.width, self.bounds.height)
|
||||
|
|
@ -43,6 +51,17 @@ impl Node {
|
|||
|
||||
/// Aligns the [`Node`] in the given space.
|
||||
pub fn align(
|
||||
mut self,
|
||||
horizontal_alignment: Alignment,
|
||||
vertical_alignment: Alignment,
|
||||
space: Size,
|
||||
) -> Self {
|
||||
self.align_mut(horizontal_alignment, vertical_alignment, space);
|
||||
self
|
||||
}
|
||||
|
||||
/// Mutable reference version of [`Self::align`].
|
||||
pub fn align_mut(
|
||||
&mut self,
|
||||
horizontal_alignment: Alignment,
|
||||
vertical_alignment: Alignment,
|
||||
|
|
@ -70,13 +89,23 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Moves the [`Node`] to the given position.
|
||||
pub fn move_to(&mut self, position: Point) {
|
||||
pub fn move_to(mut self, position: impl Into<Point>) -> Self {
|
||||
self.move_to_mut(position);
|
||||
self
|
||||
}
|
||||
|
||||
/// Mutable reference version of [`Self::move_to`].
|
||||
pub fn move_to_mut(&mut self, position: impl Into<Point>) {
|
||||
let position = position.into();
|
||||
|
||||
self.bounds.x = position.x;
|
||||
self.bounds.y = position.y;
|
||||
}
|
||||
|
||||
/// Translates the [`Node`] by the given translation.
|
||||
pub fn translate(self, translation: Vector) -> Self {
|
||||
pub fn translate(self, translation: impl Into<Vector>) -> Self {
|
||||
let translation = translation.into();
|
||||
|
||||
Self {
|
||||
bounds: self.bounds + translation,
|
||||
..self
|
||||
|
|
|
|||
|
|
@ -36,6 +36,24 @@ impl Length {
|
|||
Length::Fixed(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` iff the [`Length`] is either [`Length::Fill`] or
|
||||
// [`Length::FillPortion`].
|
||||
pub fn is_fill(&self) -> bool {
|
||||
self.fill_factor() != 0
|
||||
}
|
||||
|
||||
/// Returns the "fluid" variant of the [`Length`].
|
||||
///
|
||||
/// Specifically:
|
||||
/// - [`Length::Shrink`] if [`Length::Shrink`] or [`Length::Fixed`].
|
||||
/// - [`Length::Fill`] otherwise.
|
||||
pub fn fluid(&self) -> Length {
|
||||
match self {
|
||||
Length::Fill | Length::FillPortion(_) => Length::Fill,
|
||||
Length::Shrink | Length::Fixed(_) => Length::Shrink,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pixels> for Length {
|
||||
|
|
|
|||
|
|
@ -154,3 +154,9 @@ impl From<[f32; 4]> for Padding {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Padding> for Size {
|
||||
fn from(padding: Padding) -> Self {
|
||||
Self::new(padding.horizontal(), padding.vertical())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,20 +36,26 @@ impl<T: Num> Point<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 2]> for Point {
|
||||
fn from([x, y]: [f32; 2]) -> Self {
|
||||
impl<T> From<[T; 2]> for Point<T>
|
||||
where
|
||||
T: Num,
|
||||
{
|
||||
fn from([x, y]: [T; 2]) -> Self {
|
||||
Point { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u16; 2]> for Point<u16> {
|
||||
fn from([x, y]: [u16; 2]) -> Self {
|
||||
Point::new(x, y)
|
||||
impl<T> From<(T, T)> for Point<T>
|
||||
where
|
||||
T: Num,
|
||||
{
|
||||
fn from((x, y): (T, T)) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point> for [f32; 2] {
|
||||
fn from(point: Point) -> [f32; 2] {
|
||||
impl<T> From<Point<T>> for [T; 2] {
|
||||
fn from(point: Point<T>) -> [T; 2] {
|
||||
[point.x, point.y]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{Padding, Vector};
|
||||
use crate::Vector;
|
||||
|
||||
/// An amount of space in 2 dimensions.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
@ -26,15 +26,7 @@ impl Size {
|
|||
/// A [`Size`] with infinite width and height.
|
||||
pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY);
|
||||
|
||||
/// Increments the [`Size`] to account for the given padding.
|
||||
pub fn pad(&self, padding: Padding) -> Self {
|
||||
Size {
|
||||
width: self.width + padding.horizontal(),
|
||||
height: self.height + padding.vertical(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum of each component of this size and another
|
||||
/// Returns the minimum of each component of this size and another.
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
Size {
|
||||
width: self.width.min(other.width),
|
||||
|
|
@ -42,13 +34,23 @@ impl Size {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum of each component of this size and another
|
||||
/// Returns the maximum of each component of this size and another.
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
Size {
|
||||
width: self.width.max(other.width),
|
||||
height: self.height.max(other.height),
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands this [`Size`] by the given amount.
|
||||
pub fn expand(self, other: impl Into<Size>) -> Self {
|
||||
let other = other.into();
|
||||
|
||||
Size {
|
||||
width: self.width + other.width,
|
||||
height: self.height + other.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 2]> for Size {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::layout::{self, Layout};
|
|||
use crate::mouse;
|
||||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::{Clipboard, Length, Rectangle, Shell};
|
||||
use crate::{Clipboard, Length, Rectangle, Shell, Size};
|
||||
|
||||
/// A component that displays information and allows interaction.
|
||||
///
|
||||
|
|
@ -43,11 +43,16 @@ pub trait Widget<Message, Renderer>
|
|||
where
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
/// Returns the width of the [`Widget`].
|
||||
fn width(&self) -> Length;
|
||||
/// Returns the [`Size`] of the [`Widget`] in lengths.
|
||||
fn size(&self) -> Size<Length>;
|
||||
|
||||
/// Returns the height of the [`Widget`].
|
||||
fn height(&self) -> Length;
|
||||
/// Returns a [`Size`] hint for laying out the [`Widget`].
|
||||
///
|
||||
/// This hint may be used by some widget containers to adjust their sizing strategy
|
||||
/// during construction.
|
||||
fn size_hint(&self) -> Size<Length> {
|
||||
self.size()
|
||||
}
|
||||
|
||||
/// Returns the [`layout::Node`] of the [`Widget`].
|
||||
///
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use crate::mouse;
|
|||
use crate::renderer;
|
||||
use crate::text::{self, Paragraph};
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{Color, Element, Layout, Length, Pixels, Point, Rectangle, Widget};
|
||||
use crate::{
|
||||
Color, Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget,
|
||||
};
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
|
@ -134,12 +136,11 @@ where
|
|||
tree::State::new(State(Renderer::Paragraph::default()))
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -205,28 +206,27 @@ pub fn layout<Renderer>(
|
|||
where
|
||||
Renderer: text::Renderer,
|
||||
{
|
||||
let limits = limits.width(width).height(height);
|
||||
let bounds = limits.max();
|
||||
layout::sized(limits, width, height, |limits| {
|
||||
let bounds = limits.max();
|
||||
|
||||
let size = size.unwrap_or_else(|| renderer.default_size());
|
||||
let font = font.unwrap_or_else(|| renderer.default_font());
|
||||
let size = size.unwrap_or_else(|| renderer.default_size());
|
||||
let font = font.unwrap_or_else(|| renderer.default_font());
|
||||
|
||||
let State(ref mut paragraph) = state;
|
||||
let State(ref mut paragraph) = state;
|
||||
|
||||
paragraph.update(text::Text {
|
||||
content,
|
||||
bounds,
|
||||
size,
|
||||
line_height,
|
||||
font,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
shaping,
|
||||
});
|
||||
paragraph.update(text::Text {
|
||||
content,
|
||||
bounds,
|
||||
size,
|
||||
line_height,
|
||||
font,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
shaping,
|
||||
});
|
||||
|
||||
let size = limits.resolve(paragraph.min_bounds());
|
||||
|
||||
layout::Node::new(size)
|
||||
paragraph.min_bounds()
|
||||
})
|
||||
}
|
||||
|
||||
/// Draws text using the same logic as the [`Text`] widget.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue