Merge branch 'master' into feature/test-recorder
This commit is contained in:
commit
9e81c2b9e8
88 changed files with 1225 additions and 1158 deletions
|
|
@ -263,3 +263,16 @@ impl From<Radius> for [f32; 4] {
|
|||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<f32> for Radius {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, scale: f32) -> Self::Output {
|
||||
Self {
|
||||
top_left: self.top_left * scale,
|
||||
top_right: self.top_right * scale,
|
||||
bottom_right: self.bottom_right * scale,
|
||||
bottom_left: self.bottom_left * scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ impl Color {
|
|||
}
|
||||
|
||||
/// Returns the relative luminance of the [`Color`].
|
||||
/// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
|
||||
/// <https://www.w3.org/TR/WCAG21/#dfn-relative-luminance>
|
||||
pub fn relative_luminance(self) -> f32 {
|
||||
let linear = self.into_linear();
|
||||
0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ where
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
|
|
@ -300,7 +300,7 @@ where
|
|||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
|
|
@ -426,7 +426,7 @@ where
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
|
|
@ -435,7 +435,7 @@ where
|
|||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
|
|
@ -554,7 +554,7 @@ where
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
_tree: &mut Tree,
|
||||
_renderer: &Renderer,
|
||||
_limits: &layout::Limits,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the [`Layout`] of the children of a [`Node`].
|
||||
/// Returns an iterator over the children of this [`Layout`].
|
||||
pub fn children(self) -> impl DoubleEndedIterator<Item = Layout<'a>> {
|
||||
self.node.children().iter().map(move |node| {
|
||||
Layout::with_offset(
|
||||
|
|
@ -62,6 +62,19 @@ impl<'a> Layout<'a> {
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [`Layout`] of the child at the given index.
|
||||
///
|
||||
/// This can be useful if you ever need to access children out of order
|
||||
/// for layering purposes.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if index is out of bounds.
|
||||
pub fn child(self, index: usize) -> Layout<'a> {
|
||||
let node = &self.node.children()[index];
|
||||
|
||||
Layout::with_offset(Vector::new(self.position.x, self.position.y), node)
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces a [`Node`] with two children nodes one right next to each other.
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ pub fn resolve<Message, Theme, Renderer>(
|
|||
padding: Padding,
|
||||
spacing: f32,
|
||||
align_items: Alignment,
|
||||
items: &[Element<'_, Message, Theme, Renderer>],
|
||||
items: &mut [Element<'_, Message, Theme, Renderer>],
|
||||
trees: &mut [widget::Tree],
|
||||
) -> Node
|
||||
where
|
||||
|
|
@ -78,13 +78,13 @@ where
|
|||
let total_spacing = spacing * items.len().saturating_sub(1) as f32;
|
||||
let max_cross = axis.cross(limits.max());
|
||||
|
||||
let compression = limits.compression();
|
||||
let (main_compress, cross_compress) =
|
||||
axis.pack(compression.width, compression.height);
|
||||
|
||||
let mut fill_main_sum = 0;
|
||||
let mut some_fill_cross = false;
|
||||
let (mut cross, cross_compress) = match axis {
|
||||
Axis::Vertical if width == Length::Shrink => (0.0, true),
|
||||
Axis::Horizontal if height == Length::Shrink => (0.0, true),
|
||||
_ => (max_cross, false),
|
||||
};
|
||||
let mut cross = if cross_compress { 0.0 } else { max_cross };
|
||||
|
||||
let mut available = axis.main(limits.max()) - total_spacing;
|
||||
|
||||
|
|
@ -95,14 +95,16 @@ where
|
|||
// We lay out non-fluid elements in the main axis.
|
||||
// If we need to compress the cross axis, then we skip any of these elements
|
||||
// that are also fluid in the cross axis.
|
||||
for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() {
|
||||
for (i, (child, tree)) in items.iter_mut().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 && (!cross_compress || fill_cross_factor == 0)
|
||||
if (main_compress || fill_main_factor == 0)
|
||||
&& (!cross_compress || fill_cross_factor == 0)
|
||||
{
|
||||
let (max_width, max_height) = axis.pack(
|
||||
available,
|
||||
|
|
@ -113,11 +115,14 @@ where
|
|||
},
|
||||
);
|
||||
|
||||
let child_limits =
|
||||
Limits::new(Size::ZERO, Size::new(max_width, max_height));
|
||||
let child_limits = Limits::with_compression(
|
||||
Size::ZERO,
|
||||
Size::new(max_width, max_height),
|
||||
compression,
|
||||
);
|
||||
|
||||
let layout =
|
||||
child.as_widget().layout(tree, renderer, &child_limits);
|
||||
child.as_widget_mut().layout(tree, renderer, &child_limits);
|
||||
let size = layout.size();
|
||||
|
||||
available -= axis.main(size);
|
||||
|
|
@ -141,7 +146,8 @@ where
|
|||
// We can defer the layout of any elements that have a fixed size in the main axis,
|
||||
// allowing them to use the cross calculations of the next pass.
|
||||
if cross_compress && some_fill_cross {
|
||||
for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate()
|
||||
for (i, (child, tree)) in
|
||||
items.iter_mut().zip(trees.iter_mut()).enumerate()
|
||||
{
|
||||
let (main_size, cross_size) = {
|
||||
let size = child.as_widget().size();
|
||||
|
|
@ -149,7 +155,9 @@ where
|
|||
axis.pack(size.width, size.height)
|
||||
};
|
||||
|
||||
if main_size.fill_factor() == 0 && cross_size.fill_factor() != 0 {
|
||||
if (main_compress || main_size.fill_factor() == 0)
|
||||
&& cross_size.fill_factor() != 0
|
||||
{
|
||||
if let Length::Fixed(main) = main_size {
|
||||
available -= main;
|
||||
continue;
|
||||
|
|
@ -157,11 +165,14 @@ where
|
|||
|
||||
let (max_width, max_height) = axis.pack(available, cross);
|
||||
|
||||
let child_limits =
|
||||
Limits::new(Size::ZERO, Size::new(max_width, max_height));
|
||||
let child_limits = Limits::with_compression(
|
||||
Size::ZERO,
|
||||
Size::new(max_width, max_height),
|
||||
compression,
|
||||
);
|
||||
|
||||
let layout =
|
||||
child.as_widget().layout(tree, renderer, &child_limits);
|
||||
child.as_widget_mut().layout(tree, renderer, &child_limits);
|
||||
let size = layout.size();
|
||||
|
||||
available -= axis.main(size);
|
||||
|
|
@ -172,63 +183,59 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
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),
|
||||
},
|
||||
};
|
||||
let remaining = available.max(0.0);
|
||||
|
||||
// THIRD PASS
|
||||
// THIRD PASS (conditional)
|
||||
// We lay out the elements that are fluid in the main axis.
|
||||
// We use the remaining space to evenly allocate space based on fill factors.
|
||||
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();
|
||||
if !main_compress {
|
||||
for (i, (child, tree)) in
|
||||
items.iter_mut().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 {
|
||||
let max_main =
|
||||
remaining * fill_main_factor as f32 / fill_main_sum as f32;
|
||||
|
||||
let max_main = if max_main.is_nan() {
|
||||
f32::INFINITY
|
||||
} else {
|
||||
max_main
|
||||
axis.pack(size.width.fill_factor(), size.height.fill_factor())
|
||||
};
|
||||
|
||||
let min_main = if max_main.is_infinite() {
|
||||
0.0
|
||||
} else {
|
||||
max_main
|
||||
};
|
||||
if fill_main_factor != 0 {
|
||||
let max_main =
|
||||
remaining * fill_main_factor as f32 / fill_main_sum as f32;
|
||||
|
||||
let (min_width, min_height) = axis.pack(min_main, 0.0);
|
||||
let (max_width, max_height) = axis.pack(
|
||||
max_main,
|
||||
if fill_cross_factor == 0 {
|
||||
max_cross
|
||||
let max_main = if max_main.is_nan() {
|
||||
f32::INFINITY
|
||||
} else {
|
||||
cross
|
||||
},
|
||||
);
|
||||
max_main
|
||||
};
|
||||
|
||||
let child_limits = Limits::new(
|
||||
Size::new(min_width, min_height),
|
||||
Size::new(max_width, max_height),
|
||||
);
|
||||
let min_main = if max_main.is_infinite() {
|
||||
0.0
|
||||
} else {
|
||||
max_main
|
||||
};
|
||||
|
||||
let layout =
|
||||
child.as_widget().layout(tree, renderer, &child_limits);
|
||||
cross = cross.max(axis.cross(layout.size()));
|
||||
let (min_width, min_height) = axis.pack(min_main, 0.0);
|
||||
let (max_width, max_height) = axis.pack(
|
||||
max_main,
|
||||
if fill_cross_factor == 0 {
|
||||
max_cross
|
||||
} else {
|
||||
cross
|
||||
},
|
||||
);
|
||||
|
||||
nodes[i] = layout;
|
||||
let child_limits = Limits::with_compression(
|
||||
Size::new(min_width, min_height),
|
||||
Size::new(max_width, max_height),
|
||||
compression,
|
||||
);
|
||||
|
||||
let layout =
|
||||
child.as_widget_mut().layout(tree, renderer, &child_limits);
|
||||
cross = cross.max(axis.cross(layout.size()));
|
||||
|
||||
nodes[i] = layout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,7 +244,7 @@ where
|
|||
// These are elements that must be compressed in their cross axis and have
|
||||
// a fixed length in the main axis.
|
||||
if cross_compress && some_fill_cross {
|
||||
for (i, (child, tree)) in items.iter().zip(trees).enumerate() {
|
||||
for (i, (child, tree)) in items.iter_mut().zip(trees).enumerate() {
|
||||
let (main_size, cross_size) = {
|
||||
let size = child.as_widget().size();
|
||||
|
||||
|
|
@ -255,7 +262,7 @@ where
|
|||
Limits::new(Size::ZERO, Size::new(max_width, max_height));
|
||||
|
||||
let layout =
|
||||
child.as_widget().layout(tree, renderer, &child_limits);
|
||||
child.as_widget_mut().layout(tree, renderer, &child_limits);
|
||||
let size = layout.size();
|
||||
|
||||
cross = cross.max(axis.cross(size));
|
||||
|
|
|
|||
|
|
@ -6,18 +6,34 @@ use crate::{Length, Size};
|
|||
pub struct Limits {
|
||||
min: Size,
|
||||
max: Size,
|
||||
compression: Size<bool>,
|
||||
}
|
||||
|
||||
impl Limits {
|
||||
/// No limits
|
||||
pub const NONE: Limits = Limits {
|
||||
min: Size::ZERO,
|
||||
max: Size::INFINITY,
|
||||
max: Size::INFINITE,
|
||||
compression: Size::new(false, false),
|
||||
};
|
||||
|
||||
/// Creates new [`Limits`] with the given minimum and maximum [`Size`].
|
||||
pub const fn new(min: Size, max: Size) -> Limits {
|
||||
Limits { min, max }
|
||||
Limits::with_compression(min, max, Size::new(false, false))
|
||||
}
|
||||
|
||||
/// Creates new [`Limits`] with the given minimun and maximum [`Size`], and
|
||||
/// whether fluid lengths should be compressed to intrinsic dimensions.
|
||||
pub const fn with_compression(
|
||||
min: Size,
|
||||
max: Size,
|
||||
compress: Size<bool>,
|
||||
) -> Self {
|
||||
Limits {
|
||||
min,
|
||||
max,
|
||||
compression: compress,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum [`Size`] of the [`Limits`].
|
||||
|
|
@ -30,16 +46,25 @@ impl Limits {
|
|||
self.max
|
||||
}
|
||||
|
||||
/// Returns the compression of the [`Limits`].
|
||||
pub fn compression(&self) -> Size<bool> {
|
||||
self.compression
|
||||
}
|
||||
|
||||
/// Applies a width constraint to the current [`Limits`].
|
||||
pub fn width(mut self, width: impl Into<Length>) -> Limits {
|
||||
match width.into() {
|
||||
Length::Shrink | Length::Fill | Length::FillPortion(_) => {}
|
||||
Length::Shrink => {
|
||||
self.compression.width = true;
|
||||
}
|
||||
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.compression.width = false;
|
||||
}
|
||||
Length::Fill | Length::FillPortion(_) => {}
|
||||
}
|
||||
|
||||
self
|
||||
|
|
@ -48,14 +73,18 @@ 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 | Length::Fill | Length::FillPortion(_) => {}
|
||||
Length::Shrink => {
|
||||
self.compression.height = true;
|
||||
}
|
||||
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.compression.height = false;
|
||||
}
|
||||
Length::Fill | Length::FillPortion(_) => {}
|
||||
}
|
||||
|
||||
self
|
||||
|
|
@ -103,7 +132,11 @@ impl Limits {
|
|||
(self.max().height - size.height).max(0.0),
|
||||
);
|
||||
|
||||
Limits { min, max }
|
||||
Limits {
|
||||
min,
|
||||
max,
|
||||
compression: self.compression,
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the minimum width constraint for the current [`Limits`].
|
||||
|
|
@ -111,6 +144,7 @@ impl Limits {
|
|||
Limits {
|
||||
min: Size::ZERO,
|
||||
max: self.max,
|
||||
compression: self.compression,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,21 +158,27 @@ impl Limits {
|
|||
intrinsic_size: Size,
|
||||
) -> Size {
|
||||
let width = match width.into() {
|
||||
Length::Fill | Length::FillPortion(_) => self.max.width,
|
||||
Length::Fill | Length::FillPortion(_)
|
||||
if !self.compression.width =>
|
||||
{
|
||||
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)
|
||||
}
|
||||
_ => intrinsic_size.width.min(self.max.width).max(self.min.width),
|
||||
};
|
||||
|
||||
let height = match height.into() {
|
||||
Length::Fill | Length::FillPortion(_) => self.max.height,
|
||||
Length::Fill | Length::FillPortion(_)
|
||||
if !self.compression.height =>
|
||||
{
|
||||
self.max.height
|
||||
}
|
||||
Length::Fixed(amount) => {
|
||||
amount.min(self.max.height).max(self.min.height)
|
||||
}
|
||||
Length::Shrink => intrinsic_size
|
||||
_ => intrinsic_size
|
||||
.height
|
||||
.min(self.max.height)
|
||||
.max(self.min.height),
|
||||
|
|
|
|||
|
|
@ -34,8 +34,11 @@ where
|
|||
}
|
||||
|
||||
impl Rectangle<f32> {
|
||||
/// A rectangle starting at [`Point::ORIGIN`] with infinite width and height.
|
||||
pub const INFINITE: Self = Self::new(Point::ORIGIN, Size::INFINITY);
|
||||
/// A rectangle starting at negative infinity and with infinite width and height.
|
||||
pub const INFINITE: Self = Self::new(
|
||||
Point::new(f32::NEG_INFINITY, f32::NEG_INFINITY),
|
||||
Size::INFINITE,
|
||||
);
|
||||
|
||||
/// Creates a new [`Rectangle`] with its top-left corner in the given
|
||||
/// [`Point`] and with the provided [`Size`].
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ pub trait Renderer {
|
|||
/// Fills a [`Quad`] with the provided [`Background`].
|
||||
fn fill_quad(&mut self, quad: Quad, background: impl Into<Background>);
|
||||
|
||||
/// Clears all of the recorded primitives in the [`Renderer`].
|
||||
fn clear(&mut self);
|
||||
/// Resets the [`Renderer`] to start drawing in the `new_bounds` from scratch.
|
||||
fn reset(&mut self, new_bounds: Rectangle);
|
||||
}
|
||||
|
||||
/// A polygon with four sides.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ impl Renderer for () {
|
|||
|
||||
fn end_transformation(&mut self) {}
|
||||
|
||||
fn clear(&mut self) {}
|
||||
fn reset(&mut self, _new_bounds: Rectangle) {}
|
||||
|
||||
fn fill_quad(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl Size {
|
|||
pub const UNIT: Size = Size::new(1., 1.);
|
||||
|
||||
/// A [`Size`] with infinite width and height.
|
||||
pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY);
|
||||
pub const INFINITE: Size = Size::new(f32::INFINITY, f32::INFINITY);
|
||||
|
||||
/// Returns the minimum of each component of this size and another.
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ where
|
|||
/// This [`layout::Node`] is used by the runtime to compute the [`Layout`] of the
|
||||
/// user interface.
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
|
|
@ -102,7 +102,7 @@ where
|
|||
|
||||
/// Applies an [`Operation`] to the [`Widget`].
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
_state: &mut Tree,
|
||||
_layout: Layout<'_>,
|
||||
_renderer: &Renderer,
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ where
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
|
|
@ -245,7 +245,7 @@ where
|
|||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
_state: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
_renderer: &Renderer,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ pub fn from_rgba(
|
|||
}
|
||||
|
||||
/// An window icon normally used for the titlebar or taskbar.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct Icon {
|
||||
rgba: Vec<u8>,
|
||||
size: Size<u32>,
|
||||
|
|
@ -48,6 +48,15 @@ impl Icon {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Icon {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Icon")
|
||||
.field("rgba", &format!("{} pixels", self.rgba.len() / 4))
|
||||
.field("size", &self.size)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
/// An error produced when using [`from_rgba`] with invalid arguments.
|
||||
pub enum Error {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue