Merge branch 'master' into update-winit
This commit is contained in:
commit
534c7dd7b0
93 changed files with 1642 additions and 970 deletions
|
|
@ -126,7 +126,7 @@ bytemuck = { version = "1.0", features = ["derive"] }
|
|||
cosmic-text = "0.10"
|
||||
futures = "0.3"
|
||||
glam = "0.24"
|
||||
glyphon = { git = "https://github.com/grovesNL/glyphon.git", rev = "2caa9fc5e5923c1d827d177c3619cab7e9885b85" }
|
||||
glyphon = "0.4"
|
||||
guillotiere = "0.6"
|
||||
half = "2.2"
|
||||
image = "0.24"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ pub enum Event {
|
|||
/// for each file separately.
|
||||
FileHovered(PathBuf),
|
||||
|
||||
/// A file has beend dropped into the window.
|
||||
/// A file has been dropped into the window.
|
||||
///
|
||||
/// When the user drops multiple files at once, this event will be emitted
|
||||
/// for each file separately.
|
||||
|
|
|
|||
|
|
@ -26,12 +26,11 @@ mod quad {
|
|||
where
|
||||
Renderer: renderer::Renderer,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -33,12 +33,11 @@ mod circle {
|
|||
where
|
||||
Renderer: renderer::Renderer,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -73,16 +73,15 @@ impl Application for Example {
|
|||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
let downloads = Column::with_children(
|
||||
self.downloads.iter().map(Download::view).collect(),
|
||||
)
|
||||
.push(
|
||||
button("Add another download")
|
||||
.on_press(Message::Add)
|
||||
.padding(10),
|
||||
)
|
||||
.spacing(20)
|
||||
.align_items(Alignment::End);
|
||||
let downloads =
|
||||
Column::with_children(self.downloads.iter().map(Download::view))
|
||||
.push(
|
||||
button("Add another download")
|
||||
.on_press(Message::Add)
|
||||
.padding(10),
|
||||
)
|
||||
.spacing(20)
|
||||
.align_items(Alignment::End);
|
||||
|
||||
container(downloads)
|
||||
.width(Length::Fill)
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ iced.features = ["highlighter", "tokio", "debug"]
|
|||
tokio.workspace = true
|
||||
tokio.features = ["fs"]
|
||||
|
||||
rfd = "0.12"
|
||||
rfd = "0.13"
|
||||
|
|
|
|||
|
|
@ -82,8 +82,7 @@ impl Application for Events {
|
|||
self.last
|
||||
.iter()
|
||||
.map(|event| text(format!("{event:?}")).size(40))
|
||||
.map(Element::from)
|
||||
.collect(),
|
||||
.map(Element::from),
|
||||
);
|
||||
|
||||
let toggle = checkbox(
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ impl Application for GameOfLife {
|
|||
.view()
|
||||
.map(move |message| Message::Grid(message, version)),
|
||||
controls,
|
||||
];
|
||||
]
|
||||
.height(Length::Fill);
|
||||
|
||||
container(content)
|
||||
.width(Length::Fill)
|
||||
|
|
@ -178,7 +179,6 @@ fn view_controls<'a>(
|
|||
slider(1.0..=1000.0, speed as f32, Message::SpeedChanged),
|
||||
text(format!("x{speed}")).size(16),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,11 @@ mod rainbow {
|
|||
}
|
||||
|
||||
impl<Message> Widget<Message, Renderer> for Rainbow {
|
||||
fn width(&self) -> Length {
|
||||
Length::Fill
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Fill,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -30,9 +29,9 @@ mod rainbow {
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let size = limits.width(Length::Fill).resolve(Size::ZERO);
|
||||
let width = limits.max().width;
|
||||
|
||||
layout::Node::new(Size::new(size.width, size.width))
|
||||
layout::Node::new(Size::new(width, width))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
|
|
|
|||
|
|
@ -81,32 +81,25 @@ impl Program for Controls {
|
|||
);
|
||||
|
||||
Row::new()
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_items(Alignment::End)
|
||||
.push(
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.align_items(Alignment::End)
|
||||
.push(
|
||||
Column::new()
|
||||
.padding(10)
|
||||
.spacing(10)
|
||||
.push(
|
||||
Text::new("Background color")
|
||||
.style(Color::WHITE),
|
||||
)
|
||||
.push(sliders)
|
||||
.push(
|
||||
Text::new(format!("{background_color:?}"))
|
||||
.size(14)
|
||||
.style(Color::WHITE),
|
||||
)
|
||||
.push(
|
||||
text_input("Placeholder", text)
|
||||
.on_input(Message::TextChanged),
|
||||
),
|
||||
),
|
||||
Column::new().align_items(Alignment::End).push(
|
||||
Column::new()
|
||||
.padding(10)
|
||||
.spacing(10)
|
||||
.push(Text::new("Background color").style(Color::WHITE))
|
||||
.push(sliders)
|
||||
.push(
|
||||
Text::new(format!("{background_color:?}"))
|
||||
.size(14)
|
||||
.style(Color::WHITE),
|
||||
)
|
||||
.push(
|
||||
text_input("Placeholder", text)
|
||||
.on_input(Message::TextChanged),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
9
examples/layout/Cargo.toml
Normal file
9
examples/layout/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "layout"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["canvas"] }
|
||||
367
examples/layout/src/main.rs
Normal file
367
examples/layout/src/main.rs
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
use iced::executor;
|
||||
use iced::keyboard;
|
||||
use iced::mouse;
|
||||
use iced::theme;
|
||||
use iced::widget::{
|
||||
button, canvas, checkbox, column, container, horizontal_space, pick_list,
|
||||
row, scrollable, text, vertical_rule,
|
||||
};
|
||||
use iced::{
|
||||
color, Alignment, Application, Color, Command, Element, Font, Length,
|
||||
Point, Rectangle, Renderer, Settings, Subscription, Theme,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
Layout::run(Settings::default())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Layout {
|
||||
example: Example,
|
||||
explain: bool,
|
||||
theme: Theme,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
Next,
|
||||
Previous,
|
||||
ExplainToggled(bool),
|
||||
ThemeSelected(Theme),
|
||||
}
|
||||
|
||||
impl Application for Layout {
|
||||
type Message = Message;
|
||||
type Theme = Theme;
|
||||
type Executor = executor::Default;
|
||||
type Flags = ();
|
||||
|
||||
fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
|
||||
(
|
||||
Self {
|
||||
example: Example::default(),
|
||||
explain: false,
|
||||
theme: Theme::Light,
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
format!("{} - Layout - Iced", self.example.title)
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Next => {
|
||||
self.example = self.example.next();
|
||||
}
|
||||
Message::Previous => {
|
||||
self.example = self.example.previous();
|
||||
}
|
||||
Message::ExplainToggled(explain) => {
|
||||
self.explain = explain;
|
||||
}
|
||||
Message::ThemeSelected(theme) => {
|
||||
self.theme = theme;
|
||||
}
|
||||
}
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
keyboard::on_key_release(|key_code, _modifiers| match key_code {
|
||||
keyboard::KeyCode::Left => Some(Message::Previous),
|
||||
keyboard::KeyCode::Right => Some(Message::Next),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
let header = row![
|
||||
text(self.example.title).size(20).font(Font::MONOSPACE),
|
||||
horizontal_space(Length::Fill),
|
||||
checkbox("Explain", self.explain, Message::ExplainToggled),
|
||||
pick_list(
|
||||
Theme::ALL,
|
||||
Some(self.theme.clone()),
|
||||
Message::ThemeSelected
|
||||
),
|
||||
]
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center);
|
||||
|
||||
let example = container(if self.explain {
|
||||
self.example.view().explain(color!(0x0000ff))
|
||||
} else {
|
||||
self.example.view()
|
||||
})
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.extended_palette();
|
||||
|
||||
container::Appearance::default()
|
||||
.with_border(palette.background.strong.color, 4.0)
|
||||
})
|
||||
.padding(4)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y();
|
||||
|
||||
let controls = row([
|
||||
(!self.example.is_first()).then_some(
|
||||
button("← Previous")
|
||||
.padding([5, 10])
|
||||
.on_press(Message::Previous)
|
||||
.into(),
|
||||
),
|
||||
Some(horizontal_space(Length::Fill).into()),
|
||||
(!self.example.is_last()).then_some(
|
||||
button("Next →")
|
||||
.padding([5, 10])
|
||||
.on_press(Message::Next)
|
||||
.into(),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten());
|
||||
|
||||
column![header, example, controls]
|
||||
.spacing(10)
|
||||
.padding(20)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn theme(&self) -> Theme {
|
||||
self.theme.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct Example {
|
||||
title: &'static str,
|
||||
view: fn() -> Element<'static, Message>,
|
||||
}
|
||||
|
||||
impl Example {
|
||||
const LIST: &'static [Self] = &[
|
||||
Self {
|
||||
title: "Centered",
|
||||
view: centered,
|
||||
},
|
||||
Self {
|
||||
title: "Column",
|
||||
view: column_,
|
||||
},
|
||||
Self {
|
||||
title: "Row",
|
||||
view: row_,
|
||||
},
|
||||
Self {
|
||||
title: "Space",
|
||||
view: space,
|
||||
},
|
||||
Self {
|
||||
title: "Application",
|
||||
view: application,
|
||||
},
|
||||
Self {
|
||||
title: "Nested Quotes",
|
||||
view: nested_quotes,
|
||||
},
|
||||
];
|
||||
|
||||
fn is_first(self) -> bool {
|
||||
Self::LIST.first() == Some(&self)
|
||||
}
|
||||
|
||||
fn is_last(self) -> bool {
|
||||
Self::LIST.last() == Some(&self)
|
||||
}
|
||||
|
||||
fn previous(self) -> Self {
|
||||
let Some(index) =
|
||||
Self::LIST.iter().position(|&example| example == self)
|
||||
else {
|
||||
return self;
|
||||
};
|
||||
|
||||
Self::LIST
|
||||
.get(index.saturating_sub(1))
|
||||
.copied()
|
||||
.unwrap_or(self)
|
||||
}
|
||||
|
||||
fn next(self) -> Self {
|
||||
let Some(index) =
|
||||
Self::LIST.iter().position(|&example| example == self)
|
||||
else {
|
||||
return self;
|
||||
};
|
||||
|
||||
Self::LIST.get(index + 1).copied().unwrap_or(self)
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
(self.view)()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Example {
|
||||
fn default() -> Self {
|
||||
Self::LIST[0]
|
||||
}
|
||||
}
|
||||
|
||||
fn centered<'a>() -> Element<'a, Message> {
|
||||
container(text("I am centered!").size(50))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn column_<'a>() -> Element<'a, Message> {
|
||||
column![
|
||||
"A column can be used to",
|
||||
"lay out widgets vertically.",
|
||||
square(50),
|
||||
square(50),
|
||||
square(50),
|
||||
"The amount of space between",
|
||||
"elements can be configured!",
|
||||
]
|
||||
.spacing(40)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn row_<'a>() -> Element<'a, Message> {
|
||||
row![
|
||||
"A row works like a column...",
|
||||
square(50),
|
||||
square(50),
|
||||
square(50),
|
||||
"but lays out widgets horizontally!",
|
||||
]
|
||||
.spacing(40)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn space<'a>() -> Element<'a, Message> {
|
||||
row!["Left!", horizontal_space(Length::Fill), "Right!"].into()
|
||||
}
|
||||
|
||||
fn application<'a>() -> Element<'a, Message> {
|
||||
let header = container(
|
||||
row![
|
||||
square(40),
|
||||
horizontal_space(Length::Fill),
|
||||
"Header!",
|
||||
horizontal_space(Length::Fill),
|
||||
square(40),
|
||||
]
|
||||
.padding(10)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.extended_palette();
|
||||
|
||||
container::Appearance::default()
|
||||
.with_border(palette.background.strong.color, 1)
|
||||
});
|
||||
|
||||
let sidebar = container(
|
||||
column!["Sidebar!", square(50), square(50)]
|
||||
.spacing(40)
|
||||
.padding(10)
|
||||
.width(200)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.style(theme::Container::Box)
|
||||
.height(Length::Fill)
|
||||
.center_y();
|
||||
|
||||
let content = container(
|
||||
scrollable(
|
||||
column![
|
||||
"Content!",
|
||||
square(400),
|
||||
square(200),
|
||||
square(400),
|
||||
"The end"
|
||||
]
|
||||
.spacing(40)
|
||||
.align_items(Alignment::Center)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.padding(10);
|
||||
|
||||
column![header, row![sidebar, content]].into()
|
||||
}
|
||||
|
||||
fn nested_quotes<'a>() -> Element<'a, Message> {
|
||||
(1..5)
|
||||
.fold(column![text("Original text")].padding(10), |quotes, i| {
|
||||
column![
|
||||
container(
|
||||
row![vertical_rule(2), quotes].height(Length::Shrink)
|
||||
)
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.extended_palette();
|
||||
|
||||
container::Appearance::default().with_background(
|
||||
if palette.is_dark {
|
||||
Color {
|
||||
a: 0.01,
|
||||
..Color::WHITE
|
||||
}
|
||||
} else {
|
||||
Color {
|
||||
a: 0.08,
|
||||
..Color::BLACK
|
||||
}
|
||||
},
|
||||
)
|
||||
}),
|
||||
text(format!("Reply {i}"))
|
||||
]
|
||||
.spacing(10)
|
||||
.padding(10)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
fn square<'a>(size: impl Into<Length> + Copy) -> Element<'a, Message> {
|
||||
struct Square;
|
||||
|
||||
impl canvas::Program<Message> for Square {
|
||||
type State = ();
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Self::State,
|
||||
renderer: &Renderer,
|
||||
theme: &Theme,
|
||||
bounds: Rectangle,
|
||||
_cursor: mouse::Cursor,
|
||||
) -> Vec<canvas::Geometry> {
|
||||
let mut frame = canvas::Frame::new(renderer, bounds.size());
|
||||
|
||||
let palette = theme.extended_palette();
|
||||
|
||||
frame.fill_rectangle(
|
||||
Point::ORIGIN,
|
||||
bounds.size(),
|
||||
palette.background.strong.color,
|
||||
);
|
||||
|
||||
vec![frame.into_geometry()]
|
||||
}
|
||||
}
|
||||
|
||||
canvas(Square).width(size).height(size).into()
|
||||
}
|
||||
|
|
@ -178,35 +178,23 @@ impl Sandbox for App {
|
|||
}
|
||||
});
|
||||
|
||||
column(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
let button = button("Delete")
|
||||
.on_press(Message::DeleteItem(item.clone()))
|
||||
.style(theme::Button::Destructive);
|
||||
column(items.into_iter().map(|item| {
|
||||
let button = button("Delete")
|
||||
.on_press(Message::DeleteItem(item.clone()))
|
||||
.style(theme::Button::Destructive);
|
||||
|
||||
row![
|
||||
text(&item.name)
|
||||
.style(theme::Text::Color(item.color.into())),
|
||||
horizontal_space(Length::Fill),
|
||||
pick_list(
|
||||
Color::ALL,
|
||||
Some(item.color),
|
||||
move |color| {
|
||||
Message::ItemColorChanged(
|
||||
item.clone(),
|
||||
color,
|
||||
)
|
||||
}
|
||||
),
|
||||
button
|
||||
]
|
||||
.spacing(20)
|
||||
.into()
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
row![
|
||||
text(&item.name)
|
||||
.style(theme::Text::Color(item.color.into())),
|
||||
horizontal_space(Length::Fill),
|
||||
pick_list(Color::ALL, Some(item.color), move |color| {
|
||||
Message::ItemColorChanged(item.clone(), color)
|
||||
}),
|
||||
button
|
||||
]
|
||||
.spacing(20)
|
||||
.into()
|
||||
}))
|
||||
.spacing(10)
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -244,12 +244,11 @@ where
|
|||
tree::State::new(State::default())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
Length::Fixed(self.size)
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Fixed(self.size)
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Fixed(self.size),
|
||||
height: Length::Fixed(self.size),
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -258,10 +257,7 @@ where
|
|||
_renderer: &iced::Renderer<Theme>,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.size).height(self.size);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
layout::atomic(limits, self.size, self.size)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
|
|||
|
|
@ -165,12 +165,11 @@ where
|
|||
tree::State::new(State::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(
|
||||
|
|
@ -179,10 +178,7 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
layout::atomic(limits, self.width, self.height)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
|
|||
|
|
@ -96,15 +96,14 @@ impl Application for LoadingSpinners {
|
|||
|
||||
container(
|
||||
column.push(
|
||||
row(vec![
|
||||
text("Cycle duration:").into(),
|
||||
row![
|
||||
text("Cycle duration:"),
|
||||
slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| {
|
||||
Message::CycleDurationChanged(x / 100.0)
|
||||
})
|
||||
.width(200.0)
|
||||
.into(),
|
||||
text(format!("{:.2}s", self.cycle_duration)).into(),
|
||||
])
|
||||
.width(200.0),
|
||||
text(format!("{:.2}s", self.cycle_duration)),
|
||||
]
|
||||
.align_items(iced::Alignment::Center)
|
||||
.spacing(20.0),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -282,12 +282,8 @@ mod modal {
|
|||
tree.diff_children(&[&self.base, &self.modal]);
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.base.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.base.as_widget().height()
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.base.as_widget().size()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -421,17 +417,14 @@ mod modal {
|
|||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
let mut child = self
|
||||
let child = self
|
||||
.content
|
||||
.as_widget()
|
||||
.layout(self.tree, renderer, &limits);
|
||||
.layout(self.tree, renderer, &limits)
|
||||
.align(Alignment::Center, Alignment::Center, limits.max());
|
||||
|
||||
child.align(Alignment::Center, Alignment::Center, limits.max());
|
||||
|
||||
let mut node = layout::Node::with_children(self.size, vec![child]);
|
||||
node.move_to(position);
|
||||
|
||||
node
|
||||
layout::Node::with_children(self.size, vec![child])
|
||||
.move_to(position)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
|
|||
|
|
@ -297,7 +297,6 @@ fn view_content<'a>(
|
|||
text(format!("{}x{}", size.width, size.height)).size(24),
|
||||
controls,
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use iced::widget::{column, container, pick_list, scrollable, vertical_space};
|
||||
use iced::widget::{column, pick_list, scrollable, vertical_space};
|
||||
use iced::{Alignment, Element, Length, Sandbox, Settings};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
|
|
@ -52,12 +52,7 @@ impl Sandbox for Example {
|
|||
.align_items(Alignment::Center)
|
||||
.spacing(10);
|
||||
|
||||
container(scrollable(content))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into()
|
||||
scrollable(content).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,63 +147,54 @@ impl Application for ScrollableDemo {
|
|||
text("Scroller width:"),
|
||||
scroller_width_slider,
|
||||
]
|
||||
.spacing(10)
|
||||
.width(Length::Fill);
|
||||
.spacing(10);
|
||||
|
||||
let scroll_orientation_controls = column(vec![
|
||||
text("Scrollbar direction:").into(),
|
||||
let scroll_orientation_controls = column![
|
||||
text("Scrollbar direction:"),
|
||||
radio(
|
||||
"Vertical",
|
||||
Direction::Vertical,
|
||||
Some(self.scrollable_direction),
|
||||
Message::SwitchDirection,
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
radio(
|
||||
"Horizontal",
|
||||
Direction::Horizontal,
|
||||
Some(self.scrollable_direction),
|
||||
Message::SwitchDirection,
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
radio(
|
||||
"Both!",
|
||||
Direction::Multi,
|
||||
Some(self.scrollable_direction),
|
||||
Message::SwitchDirection,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(10)
|
||||
.width(Length::Fill);
|
||||
),
|
||||
]
|
||||
.spacing(10);
|
||||
|
||||
let scroll_alignment_controls = column(vec![
|
||||
text("Scrollable alignment:").into(),
|
||||
let scroll_alignment_controls = column![
|
||||
text("Scrollable alignment:"),
|
||||
radio(
|
||||
"Start",
|
||||
scrollable::Alignment::Start,
|
||||
Some(self.alignment),
|
||||
Message::AlignmentChanged,
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
radio(
|
||||
"End",
|
||||
scrollable::Alignment::End,
|
||||
Some(self.alignment),
|
||||
Message::AlignmentChanged,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(10)
|
||||
.width(Length::Fill);
|
||||
]
|
||||
.spacing(10);
|
||||
|
||||
let scroll_controls = row![
|
||||
scroll_slider_controls,
|
||||
scroll_orientation_controls,
|
||||
scroll_alignment_controls
|
||||
]
|
||||
.spacing(20)
|
||||
.width(Length::Fill);
|
||||
.spacing(20);
|
||||
|
||||
let scroll_to_end_button = || {
|
||||
button("Scroll to end")
|
||||
|
|
@ -229,11 +220,11 @@ impl Application for ScrollableDemo {
|
|||
text("End!"),
|
||||
scroll_to_beginning_button(),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.align_items(Alignment::Center)
|
||||
.padding([40, 0, 40, 0])
|
||||
.spacing(40),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.direction(scrollable::Direction::Vertical(
|
||||
Properties::new()
|
||||
|
|
@ -259,6 +250,7 @@ impl Application for ScrollableDemo {
|
|||
.padding([0, 40, 0, 40])
|
||||
.spacing(40),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.direction(scrollable::Direction::Horizontal(
|
||||
Properties::new()
|
||||
|
|
@ -301,6 +293,7 @@ impl Application for ScrollableDemo {
|
|||
.padding([0, 40, 0, 40])
|
||||
.spacing(40),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.direction({
|
||||
let properties = Properties::new()
|
||||
|
|
@ -341,20 +334,11 @@ impl Application for ScrollableDemo {
|
|||
|
||||
let content: Element<Message> =
|
||||
column![scroll_controls, scrollable_content, progress_bars]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10)
|
||||
.into();
|
||||
|
||||
Element::from(
|
||||
container(content)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(40)
|
||||
.center_x()
|
||||
.center_y(),
|
||||
)
|
||||
container(content).padding(20).center_x().center_y().into()
|
||||
}
|
||||
|
||||
fn theme(&self) -> Self::Theme {
|
||||
|
|
|
|||
|
|
@ -79,12 +79,10 @@ impl Application for SierpinskiEmulator {
|
|||
row![
|
||||
text(format!("Iteration: {:?}", self.graph.iteration)),
|
||||
slider(0..=10000, self.graph.iteration, Message::IterationSet)
|
||||
.width(Length::Fill)
|
||||
]
|
||||
.padding(10)
|
||||
.spacing(20),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.align_items(iced::Alignment::Center)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,13 +53,16 @@ impl Sandbox for Styling {
|
|||
self.theme = match theme {
|
||||
ThemeType::Light => Theme::Light,
|
||||
ThemeType::Dark => Theme::Dark,
|
||||
ThemeType::Custom => Theme::custom(theme::Palette {
|
||||
background: Color::from_rgb(1.0, 0.9, 1.0),
|
||||
text: Color::BLACK,
|
||||
primary: Color::from_rgb(0.5, 0.5, 0.0),
|
||||
success: Color::from_rgb(0.0, 1.0, 0.0),
|
||||
danger: Color::from_rgb(1.0, 0.0, 0.0),
|
||||
}),
|
||||
ThemeType::Custom => Theme::custom(
|
||||
String::from("Custom"),
|
||||
theme::Palette {
|
||||
background: Color::from_rgb(1.0, 0.9, 1.0),
|
||||
text: Color::BLACK,
|
||||
primary: Color::from_rgb(0.5, 0.5, 0.0),
|
||||
success: Color::from_rgb(0.0, 1.0, 0.0),
|
||||
danger: Color::from_rgb(1.0, 0.0, 0.0),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
Message::InputChanged(value) => self.input_value = value,
|
||||
|
|
@ -104,10 +107,11 @@ impl Sandbox for Styling {
|
|||
|
||||
let progress_bar = progress_bar(0.0..=100.0, self.slider_value);
|
||||
|
||||
let scrollable = scrollable(
|
||||
column!["Scroll me!", vertical_space(800), "You did it!"]
|
||||
.width(Length::Fill),
|
||||
)
|
||||
let scrollable = scrollable(column![
|
||||
"Scroll me!",
|
||||
vertical_space(800),
|
||||
"You did it!"
|
||||
])
|
||||
.width(Length::Fill)
|
||||
.height(100);
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ impl Sandbox for Tiger {
|
|||
container(apply_color_filter).width(Length::Fill).center_x()
|
||||
]
|
||||
.spacing(20)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
|
|
|
|||
|
|
@ -107,9 +107,7 @@ impl Application for App {
|
|||
|
||||
fn view<'a>(&'a self) -> Element<'a, Message> {
|
||||
let subtitle = |title, content: Element<'a, Message>| {
|
||||
column![text(title).size(14), content]
|
||||
.width(Length::Fill)
|
||||
.spacing(5)
|
||||
column![text(title).size(14), content].spacing(5)
|
||||
};
|
||||
|
||||
let mut add_toast = button("Add Toast");
|
||||
|
|
@ -154,14 +152,11 @@ impl Application for App {
|
|||
Message::Timeout
|
||||
)
|
||||
.step(1.0)
|
||||
.width(Length::Fill)
|
||||
]
|
||||
.spacing(5)
|
||||
.into()
|
||||
),
|
||||
column![add_toast]
|
||||
.width(Length::Fill)
|
||||
.align_items(Alignment::End)
|
||||
column![add_toast].align_items(Alignment::End)
|
||||
]
|
||||
.spacing(10)
|
||||
.max_width(200),
|
||||
|
|
@ -319,12 +314,8 @@ mod toast {
|
|||
}
|
||||
|
||||
impl<'a, Message> Widget<Message, Renderer> for Manager<'a, Message> {
|
||||
fn width(&self) -> Length {
|
||||
self.content.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.content.as_widget().height()
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.content.as_widget().size()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -514,14 +505,14 @@ mod toast {
|
|||
position: Point,
|
||||
_translation: Vector,
|
||||
) -> layout::Node {
|
||||
let limits = layout::Limits::new(Size::ZERO, bounds)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
let limits = layout::Limits::new(Size::ZERO, bounds);
|
||||
|
||||
layout::flex::resolve(
|
||||
layout::flex::Axis::Vertical,
|
||||
renderer,
|
||||
&limits,
|
||||
Length::Fill,
|
||||
Length::Fill,
|
||||
10.into(),
|
||||
10.0,
|
||||
Alignment::End,
|
||||
|
|
|
|||
|
|
@ -254,13 +254,7 @@ impl Application for Todos {
|
|||
.spacing(20)
|
||||
.max_width(800);
|
||||
|
||||
scrollable(
|
||||
container(content)
|
||||
.width(Length::Fill)
|
||||
.padding(40)
|
||||
.center_x(),
|
||||
)
|
||||
.into()
|
||||
scrollable(container(content).padding(40).center_x()).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -472,7 +466,6 @@ fn empty_message(message: &str) -> Element<'_, Message> {
|
|||
.horizontal_alignment(alignment::Horizontal::Center)
|
||||
.style(Color::from([0.7, 0.7, 0.7])),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(200)
|
||||
.center_y()
|
||||
.into()
|
||||
|
|
|
|||
|
|
@ -509,7 +509,6 @@ impl<'a> Step {
|
|||
)
|
||||
})
|
||||
.map(Element::from)
|
||||
.collect()
|
||||
)
|
||||
.spacing(10)
|
||||
]
|
||||
|
|
@ -692,11 +691,7 @@ fn ferris<'a>(
|
|||
}
|
||||
|
||||
fn button<'a, Message: Clone>(label: &str) -> Button<'a, Message> {
|
||||
iced::widget::button(
|
||||
text(label).horizontal_alignment(alignment::Horizontal::Center),
|
||||
)
|
||||
.padding(12)
|
||||
.width(100)
|
||||
iced::widget::button(text(label)).padding([12, 24])
|
||||
}
|
||||
|
||||
fn color_slider<'a>(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ mod echo;
|
|||
use iced::alignment::{self, Alignment};
|
||||
use iced::executor;
|
||||
use iced::widget::{
|
||||
button, column, container, row, scrollable, text, text_input, Column,
|
||||
button, column, container, row, scrollable, text, text_input,
|
||||
};
|
||||
use iced::{
|
||||
Application, Color, Command, Element, Length, Settings, Subscription, Theme,
|
||||
|
|
@ -108,15 +108,9 @@ impl Application for WebSocket {
|
|||
.into()
|
||||
} else {
|
||||
scrollable(
|
||||
Column::with_children(
|
||||
self.messages
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(text)
|
||||
.map(Element::from)
|
||||
.collect(),
|
||||
column(
|
||||
self.messages.iter().cloned().map(text).map(Element::from),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.spacing(10),
|
||||
)
|
||||
.id(MESSAGE_LOG.clone())
|
||||
|
|
@ -131,7 +125,7 @@ impl Application for WebSocket {
|
|||
|
||||
let mut button = button(
|
||||
text("Send")
|
||||
.height(Length::Fill)
|
||||
.height(40)
|
||||
.vertical_alignment(alignment::Vertical::Center),
|
||||
)
|
||||
.padding([0, 20]);
|
||||
|
|
@ -149,7 +143,6 @@ impl Application for WebSocket {
|
|||
};
|
||||
|
||||
column![message_log, new_message_input]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(20)
|
||||
.spacing(10)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,11 @@ impl<T: Damage> Damage for Primitive<T> {
|
|||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::RawText(raw) => {
|
||||
// TODO: Add `size` field to `raw` to compute more accurate
|
||||
// damage bounds (?)
|
||||
raw.clip_bounds.expand(1.5)
|
||||
}
|
||||
Self::Quad { bounds, .. }
|
||||
| Self::Image { bounds, .. }
|
||||
| Self::Svg { bounds, .. } => bounds.expand(1.0),
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ pub enum Primitive<T> {
|
|||
/// The clip bounds of the editor.
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// A raw `cosmic-text` primitive
|
||||
RawText(crate::text::Raw),
|
||||
/// A quad primitive
|
||||
Quad {
|
||||
/// The bounds of the quad
|
||||
|
|
|
|||
|
|
@ -9,14 +9,13 @@ pub use paragraph::Paragraph;
|
|||
|
||||
pub use cosmic_text;
|
||||
|
||||
use crate::color;
|
||||
use crate::core::font::{self, Font};
|
||||
use crate::core::text::Shaping;
|
||||
use crate::core::{Color, Size};
|
||||
use crate::core::{Color, Point, Rectangle, Size};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::{Arc, RwLock, Weak};
|
||||
|
||||
/// Returns the global [`FontSystem`].
|
||||
pub fn font_system() -> &'static RwLock<FontSystem> {
|
||||
|
|
@ -68,6 +67,29 @@ impl FontSystem {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct Version(u32);
|
||||
|
||||
/// A weak reference to a [`cosmic-text::Buffer`] that can be drawn.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Raw {
|
||||
/// A weak reference to a [`cosmic_text::Buffer`].
|
||||
pub buffer: Weak<cosmic_text::Buffer>,
|
||||
/// The position of the text.
|
||||
pub position: Point,
|
||||
/// The color of the text.
|
||||
pub color: Color,
|
||||
/// The clip bounds of the text.
|
||||
pub clip_bounds: Rectangle,
|
||||
}
|
||||
|
||||
impl PartialEq for Raw {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
// TODO: There is no proper way to compare raw buffers
|
||||
// For now, no two instances of `Raw` text will be equal.
|
||||
// This should be fine, but could trigger unnecessary redraws
|
||||
// in the future.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Measures the dimensions of the given [`cosmic_text::Buffer`].
|
||||
pub fn measure(buffer: &cosmic_text::Buffer) -> Size {
|
||||
let (width, total_lines) = buffer
|
||||
|
|
@ -150,12 +172,7 @@ pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping {
|
|||
|
||||
/// Converts some [`Color`] to a [`cosmic_text::Color`].
|
||||
pub fn to_color(color: Color) -> cosmic_text::Color {
|
||||
let [r, g, b, a] = color::pack(color).components();
|
||||
let [r, g, b, a] = color.into_rgba8();
|
||||
|
||||
cosmic_text::Color::rgba(
|
||||
(r * 255.0) as u8,
|
||||
(g * 255.0) as u8,
|
||||
(b * 255.0) as u8,
|
||||
(a * 255.0) as u8,
|
||||
)
|
||||
cosmic_text::Color::rgba(r, g, b, a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,38 +187,43 @@ impl core::text::Paragraph for Paragraph {
|
|||
}
|
||||
|
||||
fn grapheme_position(&self, line: usize, index: usize) -> Option<Point> {
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
let run = self.internal().buffer.layout_runs().nth(line)?;
|
||||
|
||||
// index represents a grapheme, not a glyph
|
||||
// Let's find the first glyph for the given grapheme cluster
|
||||
let mut last_start = None;
|
||||
let mut last_grapheme_count = 0;
|
||||
let mut graphemes_seen = 0;
|
||||
|
||||
let glyph = run
|
||||
.glyphs
|
||||
.iter()
|
||||
.find(|glyph| {
|
||||
if graphemes_seen == index {
|
||||
return true;
|
||||
}
|
||||
|
||||
if Some(glyph.start) != last_start {
|
||||
last_grapheme_count = run.text[glyph.start..glyph.end]
|
||||
.graphemes(false)
|
||||
.count();
|
||||
last_start = Some(glyph.start);
|
||||
graphemes_seen += 1;
|
||||
graphemes_seen += last_grapheme_count;
|
||||
}
|
||||
|
||||
false
|
||||
graphemes_seen >= index
|
||||
})
|
||||
.or_else(|| run.glyphs.last())?;
|
||||
|
||||
let advance_last = if index == run.glyphs.len() {
|
||||
glyph.w
|
||||
} else {
|
||||
let advance = if index == 0 {
|
||||
0.0
|
||||
} else {
|
||||
glyph.w
|
||||
* (1.0
|
||||
- graphemes_seen.saturating_sub(index) as f32
|
||||
/ last_grapheme_count.max(1) as f32)
|
||||
};
|
||||
|
||||
Some(Point::new(
|
||||
glyph.x + glyph.x_offset * glyph.font_size + advance_last,
|
||||
glyph.x + glyph.x_offset * glyph.font_size + advance,
|
||||
glyph.y - glyph.y_offset * glyph.font_size,
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
use crate::core::text;
|
||||
use crate::core::{Font, Point, Size};
|
||||
use crate::graphics::backend;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Backend {
|
||||
TinySkia(iced_tiny_skia::Backend),
|
||||
#[cfg(feature = "wgpu")]
|
||||
Wgpu(iced_wgpu::Backend),
|
||||
}
|
||||
|
||||
macro_rules! delegate {
|
||||
($backend:expr, $name:ident, $body:expr) => {
|
||||
match $backend {
|
||||
Self::TinySkia($name) => $body,
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu($name) => $body,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl backend::Text for Backend {
|
||||
const ICON_FONT: Font = Font::with_name("Iced-Icons");
|
||||
const CHECKMARK_ICON: char = '\u{f00c}';
|
||||
const ARROW_DOWN_ICON: char = '\u{e800}';
|
||||
|
||||
fn default_font(&self) -> Font {
|
||||
delegate!(self, backend, backend.default_font())
|
||||
}
|
||||
|
||||
fn default_size(&self) -> f32 {
|
||||
delegate!(self, backend, backend.default_size())
|
||||
}
|
||||
|
||||
fn measure(
|
||||
&self,
|
||||
contents: &str,
|
||||
size: f32,
|
||||
line_height: text::LineHeight,
|
||||
font: Font,
|
||||
bounds: Size,
|
||||
shaping: text::Shaping,
|
||||
) -> Size {
|
||||
delegate!(
|
||||
self,
|
||||
backend,
|
||||
backend.measure(contents, size, line_height, font, bounds, shaping)
|
||||
)
|
||||
}
|
||||
|
||||
fn hit_test(
|
||||
&self,
|
||||
contents: &str,
|
||||
size: f32,
|
||||
line_height: text::LineHeight,
|
||||
font: Font,
|
||||
bounds: Size,
|
||||
shaping: text::Shaping,
|
||||
position: Point,
|
||||
nearest_only: bool,
|
||||
) -> Option<text::Hit> {
|
||||
delegate!(
|
||||
self,
|
||||
backend,
|
||||
backend.hit_test(
|
||||
contents,
|
||||
size,
|
||||
line_height,
|
||||
font,
|
||||
bounds,
|
||||
shaping,
|
||||
position,
|
||||
nearest_only,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn load_font(&mut self, font: Cow<'static, [u8]>) {
|
||||
delegate!(self, backend, backend.load_font(font));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
impl backend::Image for Backend {
|
||||
fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
|
||||
delegate!(self, backend, backend.dimensions(handle))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
impl backend::Svg for Backend {
|
||||
fn viewport_dimensions(
|
||||
&self,
|
||||
handle: &crate::core::svg::Handle,
|
||||
) -> Size<u32> {
|
||||
delegate!(self, backend, backend.viewport_dimensions(handle))
|
||||
}
|
||||
}
|
||||
|
|
@ -65,11 +65,33 @@ pub fn fetch_size<Message>(
|
|||
Command::single(command::Action::Window(Action::FetchSize(id, Box::new(f))))
|
||||
}
|
||||
|
||||
/// Fetches if the window is maximized.
|
||||
pub fn fetch_maximized<Message>(
|
||||
id: Id,
|
||||
f: impl FnOnce(bool) -> Message + 'static,
|
||||
) -> Command<Message> {
|
||||
Command::single(command::Action::Window(Action::FetchMaximized(
|
||||
id,
|
||||
Box::new(f),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Maximizes the window.
|
||||
pub fn maximize<Message>(id: Id, maximized: bool) -> Command<Message> {
|
||||
Command::single(command::Action::Window(Action::Maximize(id, maximized)))
|
||||
}
|
||||
|
||||
/// Fetches if the window is minimized.
|
||||
pub fn fetch_minimized<Message>(
|
||||
id: Id,
|
||||
f: impl FnOnce(Option<bool>) -> Message + 'static,
|
||||
) -> Command<Message> {
|
||||
Command::single(command::Action::Window(Action::FetchMinimized(
|
||||
id,
|
||||
Box::new(f),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Minimizes the window.
|
||||
pub fn minimize<Message>(id: Id, minimized: bool) -> Command<Message> {
|
||||
Command::single(command::Action::Window(Action::Minimize(id, minimized)))
|
||||
|
|
|
|||
|
|
@ -21,8 +21,19 @@ pub enum Action<T> {
|
|||
Resize(Id, Size),
|
||||
/// Fetch the current logical dimensions of the window.
|
||||
FetchSize(Id, Box<dyn FnOnce(Size) -> T + 'static>),
|
||||
/// Fetch if the current window is maximized or not.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **iOS / Android / Web:** Unsupported.
|
||||
FetchMaximized(Id, Box<dyn FnOnce(bool) -> T + 'static>),
|
||||
/// Set the window to maximized or back
|
||||
Maximize(Id, bool),
|
||||
/// Fetch if the current window is minimized or not.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Wayland:** Always `None`.
|
||||
/// - **iOS / Android / Web:** Unsupported.
|
||||
FetchMinimized(Id, Box<dyn FnOnce(Option<bool>) -> T + 'static>),
|
||||
/// Set the window to minimized or back
|
||||
Minimize(Id, bool),
|
||||
/// Move the window to the given logical coordinates.
|
||||
|
|
@ -106,7 +117,13 @@ impl<T> Action<T> {
|
|||
Self::FetchSize(id, o) => {
|
||||
Action::FetchSize(id, Box::new(move |s| f(o(s))))
|
||||
}
|
||||
Self::FetchMaximized(id, o) => {
|
||||
Action::FetchMaximized(id, Box::new(move |s| f(o(s))))
|
||||
}
|
||||
Self::Maximize(id, maximized) => Action::Maximize(id, maximized),
|
||||
Self::FetchMinimized(id, o) => {
|
||||
Action::FetchMinimized(id, Box::new(move |s| f(o(s))))
|
||||
}
|
||||
Self::Minimize(id, minimized) => Action::Minimize(id, minimized),
|
||||
Self::Move(id, position) => Action::Move(id, position),
|
||||
Self::ChangeMode(id, mode) => Action::ChangeMode(id, mode),
|
||||
|
|
@ -144,9 +161,15 @@ impl<T> fmt::Debug for Action<T> {
|
|||
write!(f, "Action::Resize({id:?}, {size:?})")
|
||||
}
|
||||
Self::FetchSize(id, _) => write!(f, "Action::FetchSize({id:?})"),
|
||||
Self::FetchMaximized(id, _) => {
|
||||
write!(f, "Action::FetchMaximized({id:?})")
|
||||
}
|
||||
Self::Maximize(id, maximized) => {
|
||||
write!(f, "Action::Maximize({id:?}, {maximized})")
|
||||
}
|
||||
Self::FetchMinimized(id, _) => {
|
||||
write!(f, "Action::FetchMinimized({id:?})")
|
||||
}
|
||||
Self::Minimize(id, minimized) => {
|
||||
write!(f, "Action::Minimize({id:?}, {minimized}")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
//! Listen and react to time.
|
||||
pub use iced_core::time::{Duration, Instant};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use iced_futures::backend::default::time::*;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! Change the appearance of a container.
|
||||
use iced_core::{Background, BorderRadius, Color};
|
||||
use crate::core::{Background, BorderRadius, Color, Pixels};
|
||||
|
||||
/// The appearance of a container.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -16,6 +16,30 @@ pub struct Appearance {
|
|||
pub border_color: Color,
|
||||
}
|
||||
|
||||
impl Appearance {
|
||||
/// Derives a new [`Appearance`] with a border of the given [`Color`] and
|
||||
/// `width`.
|
||||
pub fn with_border(
|
||||
self,
|
||||
color: impl Into<Color>,
|
||||
width: impl Into<Pixels>,
|
||||
) -> Self {
|
||||
Self {
|
||||
border_color: color.into(),
|
||||
border_width: width.into().0,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Derives a new [`Appearance`] with the given [`Background`].
|
||||
pub fn with_background(self, background: impl Into<Background>) -> Self {
|
||||
Self {
|
||||
background: Some(background.into()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for Appearance {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use crate::toggler;
|
|||
|
||||
use iced_core::{Background, Color, Vector};
|
||||
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A built-in theme.
|
||||
|
|
@ -38,18 +39,22 @@ pub enum Theme {
|
|||
}
|
||||
|
||||
impl Theme {
|
||||
/// A list with all the defined themes.
|
||||
pub const ALL: &'static [Self] = &[Self::Light, Self::Dark];
|
||||
|
||||
/// Creates a new custom [`Theme`] from the given [`Palette`].
|
||||
pub fn custom(palette: Palette) -> Self {
|
||||
Self::custom_with_fn(palette, palette::Extended::generate)
|
||||
pub fn custom(name: String, palette: Palette) -> Self {
|
||||
Self::custom_with_fn(name, palette, palette::Extended::generate)
|
||||
}
|
||||
|
||||
/// Creates a new custom [`Theme`] from the given [`Palette`], with
|
||||
/// a custom generator of a [`palette::Extended`].
|
||||
pub fn custom_with_fn(
|
||||
name: String,
|
||||
palette: Palette,
|
||||
generate: impl FnOnce(Palette) -> palette::Extended,
|
||||
) -> Self {
|
||||
Self::Custom(Box::new(Custom::with_fn(palette, generate)))
|
||||
Self::Custom(Box::new(Custom::with_fn(name, palette, generate)))
|
||||
}
|
||||
|
||||
/// Returns the [`Palette`] of the [`Theme`].
|
||||
|
|
@ -71,32 +76,51 @@ impl Theme {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Theme {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Light => write!(f, "Light"),
|
||||
Self::Dark => write!(f, "Dark"),
|
||||
Self::Custom(custom) => custom.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Theme`] with a customized [`Palette`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Custom {
|
||||
name: String,
|
||||
palette: Palette,
|
||||
extended: palette::Extended,
|
||||
}
|
||||
|
||||
impl Custom {
|
||||
/// Creates a [`Custom`] theme from the given [`Palette`].
|
||||
pub fn new(palette: Palette) -> Self {
|
||||
Self::with_fn(palette, palette::Extended::generate)
|
||||
pub fn new(name: String, palette: Palette) -> Self {
|
||||
Self::with_fn(name, palette, palette::Extended::generate)
|
||||
}
|
||||
|
||||
/// Creates a [`Custom`] theme from the given [`Palette`] with
|
||||
/// a custom generator of a [`palette::Extended`].
|
||||
pub fn with_fn(
|
||||
name: String,
|
||||
palette: Palette,
|
||||
generate: impl FnOnce(Palette) -> palette::Extended,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
palette,
|
||||
extended: generate(palette),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Custom {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
/// The style of an application.
|
||||
#[derive(Default)]
|
||||
pub enum Application {
|
||||
|
|
@ -383,6 +407,12 @@ pub enum Container {
|
|||
Custom(Box<dyn container::StyleSheet<Style = Theme>>),
|
||||
}
|
||||
|
||||
impl From<container::Appearance> for Container {
|
||||
fn from(appearance: container::Appearance) -> Self {
|
||||
Self::Custom(Box::new(move |_: &_| appearance))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Fn(&Theme) -> container::Appearance + 'static> From<T> for Container {
|
||||
fn from(f: T) -> Self {
|
||||
Self::Custom(Box::new(f))
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ pub struct Extended {
|
|||
pub success: Success,
|
||||
/// The set of danger colors.
|
||||
pub danger: Danger,
|
||||
/// Whether the palette is dark or not.
|
||||
pub is_dark: bool,
|
||||
}
|
||||
|
||||
/// The built-in light variant of an [`Extended`] palette.
|
||||
|
|
@ -113,6 +115,7 @@ impl Extended {
|
|||
palette.background,
|
||||
palette.text,
|
||||
),
|
||||
is_dark: is_dark(palette.background),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::core::{Background, Color, Gradient, Rectangle, Vector};
|
||||
use crate::graphics::backend;
|
||||
use crate::graphics::text;
|
||||
use crate::graphics::Viewport;
|
||||
use crate::primitive::{self, Primitive};
|
||||
|
||||
|
|
@ -444,6 +445,35 @@ impl Backend {
|
|||
clip_mask,
|
||||
);
|
||||
}
|
||||
Primitive::RawText(text::Raw {
|
||||
buffer,
|
||||
position,
|
||||
color,
|
||||
clip_bounds: text_clip_bounds,
|
||||
}) => {
|
||||
let Some(buffer) = buffer.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let physical_bounds =
|
||||
(*text_clip_bounds + translation) * scale_factor;
|
||||
|
||||
if !clip_bounds.intersects(&physical_bounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
let clip_mask = (!physical_bounds.is_within(&clip_bounds))
|
||||
.then_some(clip_mask as &_);
|
||||
|
||||
self.text_pipeline.draw_raw(
|
||||
&buffer,
|
||||
*position + translation,
|
||||
*color,
|
||||
scale_factor,
|
||||
pixels,
|
||||
clip_mask,
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "image")]
|
||||
Primitive::Image {
|
||||
handle,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use crate::core::alignment;
|
||||
use crate::core::text::{LineHeight, Shaping};
|
||||
use crate::core::{Color, Font, Pixels, Point, Rectangle};
|
||||
use crate::graphics::color;
|
||||
use crate::core::{Color, Font, Pixels, Point, Rectangle, Size};
|
||||
use crate::graphics::text::cache::{self, Cache};
|
||||
use crate::graphics::text::editor;
|
||||
use crate::graphics::text::font_system;
|
||||
|
|
@ -149,6 +148,33 @@ impl Pipeline {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn draw_raw(
|
||||
&mut self,
|
||||
buffer: &cosmic_text::Buffer,
|
||||
position: Point,
|
||||
color: Color,
|
||||
scale_factor: f32,
|
||||
pixels: &mut tiny_skia::PixmapMut<'_>,
|
||||
clip_mask: Option<&tiny_skia::Mask>,
|
||||
) {
|
||||
let mut font_system = font_system().write().expect("Write font system");
|
||||
|
||||
let (width, height) = buffer.size();
|
||||
|
||||
draw(
|
||||
font_system.raw(),
|
||||
&mut self.glyph_cache,
|
||||
buffer,
|
||||
Rectangle::new(position, Size::new(width, height)),
|
||||
color,
|
||||
alignment::Horizontal::Left,
|
||||
alignment::Vertical::Top,
|
||||
scale_factor,
|
||||
pixels,
|
||||
clip_mask,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn trim_cache(&mut self) {
|
||||
self.cache.get_mut().trim();
|
||||
self.glyph_cache.trim();
|
||||
|
|
@ -217,18 +243,7 @@ fn draw(
|
|||
fn from_color(color: cosmic_text::Color) -> Color {
|
||||
let [r, g, b, a] = color.as_rgba();
|
||||
|
||||
if color::GAMMA_CORRECTION {
|
||||
// `cosmic_text::Color` is linear RGB in this case, so we
|
||||
// need to convert back to sRGB
|
||||
Color::from_linear_rgba(
|
||||
r as f32 / 255.0,
|
||||
g as f32 / 255.0,
|
||||
b as f32 / 255.0,
|
||||
a as f32 / 255.0,
|
||||
)
|
||||
} else {
|
||||
Color::from_rgba8(r, g, b, a as f32 / 255.0)
|
||||
}
|
||||
Color::from_rgba8(r, g, b, a as f32 / 255.0)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
|
|||
|
|
@ -177,6 +177,21 @@ impl<'a> Layer<'a> {
|
|||
clip_bounds: *clip_bounds + translation,
|
||||
}));
|
||||
}
|
||||
graphics::Primitive::RawText(graphics::text::Raw {
|
||||
buffer,
|
||||
position,
|
||||
color,
|
||||
clip_bounds,
|
||||
}) => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
layer.text.push(Text::Raw(graphics::text::Raw {
|
||||
buffer: buffer.clone(),
|
||||
position: *position + translation,
|
||||
color: *color,
|
||||
clip_bounds: *clip_bounds + translation,
|
||||
}));
|
||||
}
|
||||
Primitive::Quad {
|
||||
bounds,
|
||||
background,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::core::alignment;
|
||||
use crate::core::text;
|
||||
use crate::core::{Color, Font, Pixels, Point, Rectangle};
|
||||
use crate::graphics;
|
||||
use crate::graphics::text::editor;
|
||||
use crate::graphics::text::paragraph;
|
||||
|
||||
|
|
@ -23,8 +24,10 @@ pub enum Text<'a> {
|
|||
color: Color,
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// A cached text.
|
||||
/// Some cached text.
|
||||
Cached(Cached<'a>),
|
||||
/// Some raw text.
|
||||
Raw(graphics::text::Raw),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ impl<Theme> Renderer for crate::Renderer<Theme> {
|
|||
/// Stores custom, user-provided pipelines.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Storage {
|
||||
pipelines: HashMap<TypeId, Box<dyn Any>>,
|
||||
pipelines: HashMap<TypeId, Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
|
|
@ -92,7 +92,7 @@ impl Storage {
|
|||
}
|
||||
|
||||
/// Inserts the pipeline `T` in to [`Storage`].
|
||||
pub fn store<T: 'static>(&mut self, pipeline: T) {
|
||||
pub fn store<T: 'static + Send>(&mut self, pipeline: T) {
|
||||
let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::layer::Text;
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Pipeline {
|
||||
|
|
@ -76,6 +77,7 @@ impl Pipeline {
|
|||
Paragraph(Paragraph),
|
||||
Editor(Editor),
|
||||
Cache(cache::KeyHash),
|
||||
Raw(Arc<glyphon::Buffer>),
|
||||
}
|
||||
|
||||
let allocations: Vec<_> = sections
|
||||
|
|
@ -107,6 +109,7 @@ impl Pipeline {
|
|||
|
||||
Some(Allocation::Cache(key))
|
||||
}
|
||||
Text::Raw(text) => text.buffer.upgrade().map(Allocation::Raw),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -185,6 +188,25 @@ impl Pipeline {
|
|||
text.clip_bounds,
|
||||
)
|
||||
}
|
||||
Text::Raw(text) => {
|
||||
let Some(Allocation::Raw(buffer)) = allocation else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (width, height) = buffer.size();
|
||||
|
||||
(
|
||||
buffer.as_ref(),
|
||||
Rectangle::new(
|
||||
text.position,
|
||||
Size::new(width, height),
|
||||
),
|
||||
alignment::Horizontal::Left,
|
||||
alignment::Vertical::Top,
|
||||
text.color,
|
||||
text.clip_bounds,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let bounds = bounds * scale_factor;
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use crate::core::touch;
|
|||
use crate::core::widget::tree::{self, Tree};
|
||||
use crate::core::widget::Operation;
|
||||
use crate::core::{
|
||||
Background, Clipboard, Color, Element, Layout, Length, Padding, Point,
|
||||
Rectangle, Shell, Vector, Widget,
|
||||
Background, Clipboard, Color, Element, Layout, Length, Padding, Rectangle,
|
||||
Shell, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
pub use iced_style::button::{Appearance, StyleSheet};
|
||||
|
|
@ -71,11 +71,14 @@ where
|
|||
{
|
||||
/// Creates a new [`Button`] with the given content.
|
||||
pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
|
||||
let content = content.into();
|
||||
let size = content.as_widget().size_hint();
|
||||
|
||||
Button {
|
||||
content: content.into(),
|
||||
content,
|
||||
on_press: None,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
width: size.width.fluid(),
|
||||
height: size.height.fluid(),
|
||||
padding: Padding::new(5.0),
|
||||
style: <Renderer::Theme as StyleSheet>::Style::default(),
|
||||
}
|
||||
|
|
@ -149,12 +152,11 @@ where
|
|||
tree.diff_children(std::slice::from_ref(&self.content));
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -431,15 +433,7 @@ pub fn layout(
|
|||
padding: Padding,
|
||||
layout_content: impl FnOnce(&layout::Limits) -> layout::Node,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(width).height(height);
|
||||
|
||||
let mut content = layout_content(&limits.pad(padding));
|
||||
let padding = padding.fit(content.size(), limits.max());
|
||||
let size = limits.pad(padding).resolve(content.size()).pad(padding);
|
||||
|
||||
content.move_to(Point::new(padding.left, padding.top));
|
||||
|
||||
layout::Node::with_children(size, vec![content])
|
||||
layout::padded(limits, width, height, padding, layout_content)
|
||||
}
|
||||
|
||||
/// Returns the [`mouse::Interaction`] of a [`Button`].
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ use crate::core::layout::{self, Layout};
|
|||
use crate::core::mouse;
|
||||
use crate::core::renderer;
|
||||
use crate::core::widget::tree::{self, Tree};
|
||||
use crate::core::{Clipboard, Element, Shell, Widget};
|
||||
use crate::core::{Length, Rectangle, Size, Vector};
|
||||
use crate::core::{
|
||||
Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,
|
||||
};
|
||||
use crate::graphics::geometry;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
|
@ -119,12 +120,11 @@ where
|
|||
tree::State::new(P::State::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(
|
||||
|
|
@ -133,10 +133,7 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
layout::atomic(limits, self.width, self.height)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
|
|||
|
|
@ -174,12 +174,11 @@ where
|
|||
tree::State::new(widget::text::State::<Renderer::Paragraph>::default())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use crate::core::renderer;
|
|||
use crate::core::widget::{Operation, Tree};
|
||||
use crate::core::{
|
||||
Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Rectangle,
|
||||
Shell, Widget,
|
||||
Shell, Size, Widget,
|
||||
};
|
||||
|
||||
/// A container that distributes its contents vertically.
|
||||
|
|
@ -22,16 +22,12 @@ pub struct Column<'a, Message, Renderer = crate::Renderer> {
|
|||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
|
||||
impl<'a, Message, Renderer> Column<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::core::Renderer,
|
||||
{
|
||||
/// Creates an empty [`Column`].
|
||||
pub fn new() -> Self {
|
||||
Self::with_children(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates a [`Column`] with the given elements.
|
||||
pub fn with_children(
|
||||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
Column {
|
||||
spacing: 0.0,
|
||||
padding: Padding::ZERO,
|
||||
|
|
@ -39,10 +35,17 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
|
|||
height: Length::Shrink,
|
||||
max_width: f32::INFINITY,
|
||||
align_items: Alignment::Start,
|
||||
children,
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`Column`] with the given elements.
|
||||
pub fn with_children(
|
||||
children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
children.into_iter().fold(Self::new(), Self::push)
|
||||
}
|
||||
|
||||
/// Sets the vertical spacing _between_ elements.
|
||||
///
|
||||
/// Custom margins per element do not exist in iced. You should use this
|
||||
|
|
@ -88,12 +91,26 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
|
|||
mut self,
|
||||
child: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
self.children.push(child.into());
|
||||
let child = child.into();
|
||||
let size = child.as_widget().size_hint();
|
||||
|
||||
if size.width.is_fill() {
|
||||
self.width = Length::Fill;
|
||||
}
|
||||
|
||||
if size.height.is_fill() {
|
||||
self.height = Length::Fill;
|
||||
}
|
||||
|
||||
self.children.push(child);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer> {
|
||||
impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::core::Renderer,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
|
@ -112,12 +129,11 @@ where
|
|||
tree.diff_children(&self.children);
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -126,15 +142,14 @@ where
|
|||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits
|
||||
.max_width(self.max_width)
|
||||
.width(self.width)
|
||||
.height(self.height);
|
||||
let limits = limits.max_width(self.max_width);
|
||||
|
||||
layout::flex::resolve(
|
||||
layout::flex::Axis::Vertical,
|
||||
renderer,
|
||||
&limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.padding,
|
||||
self.spacing,
|
||||
self.align_items,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ use crate::core::renderer;
|
|||
use crate::core::text;
|
||||
use crate::core::time::Instant;
|
||||
use crate::core::widget::{self, Widget};
|
||||
use crate::core::{Clipboard, Element, Length, Padding, Rectangle, Shell};
|
||||
use crate::core::{
|
||||
Clipboard, Element, Length, Padding, Rectangle, Shell, Size,
|
||||
};
|
||||
use crate::overlay::menu;
|
||||
use crate::text::LineHeight;
|
||||
use crate::{container, scrollable, text_input, TextInput};
|
||||
|
|
@ -297,12 +299,8 @@ where
|
|||
+ scrollable::StyleSheet
|
||||
+ menu::StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
Widget::<TextInputEvent, Renderer>::width(&self.text_input)
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Widget::<TextInputEvent, Renderer>::height(&self.text_input)
|
||||
fn size(&self) -> Size<Length> {
|
||||
Widget::<TextInputEvent, Renderer>::size(&self.text_input)
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -46,17 +46,20 @@ where
|
|||
where
|
||||
T: Into<Element<'a, Message, Renderer>>,
|
||||
{
|
||||
let content = content.into();
|
||||
let size = content.as_widget().size_hint();
|
||||
|
||||
Container {
|
||||
id: None,
|
||||
padding: Padding::ZERO,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
width: size.width.fluid(),
|
||||
height: size.height.fluid(),
|
||||
max_width: f32::INFINITY,
|
||||
max_height: f32::INFINITY,
|
||||
horizontal_alignment: alignment::Horizontal::Left,
|
||||
vertical_alignment: alignment::Vertical::Top,
|
||||
style: Default::default(),
|
||||
content: content.into(),
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,12 +155,11 @@ where
|
|||
self.content.as_widget().diff(tree);
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -311,25 +313,20 @@ pub fn layout(
|
|||
vertical_alignment: alignment::Vertical,
|
||||
layout_content: impl FnOnce(&layout::Limits) -> layout::Node,
|
||||
) -> layout::Node {
|
||||
let limits = limits
|
||||
.loose()
|
||||
.max_width(max_width)
|
||||
.max_height(max_height)
|
||||
.width(width)
|
||||
.height(height);
|
||||
|
||||
let mut content = layout_content(&limits.pad(padding).loose());
|
||||
let padding = padding.fit(content.size(), limits.max());
|
||||
let size = limits.pad(padding).resolve(content.size());
|
||||
|
||||
content.move_to(Point::new(padding.left, padding.top));
|
||||
content.align(
|
||||
Alignment::from(horizontal_alignment),
|
||||
Alignment::from(vertical_alignment),
|
||||
size,
|
||||
);
|
||||
|
||||
layout::Node::with_children(size.pad(padding), vec![content])
|
||||
layout::positioned(
|
||||
&limits.max_width(max_width).max_height(max_height),
|
||||
width,
|
||||
height,
|
||||
padding,
|
||||
|limits| layout_content(&limits.loose()),
|
||||
|content, size| {
|
||||
content.align(
|
||||
Alignment::from(horizontal_alignment),
|
||||
Alignment::from(vertical_alignment),
|
||||
size,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Draws the background of a [`Container`] given its [`Appearance`] and its `bounds`.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ macro_rules! column {
|
|||
$crate::Column::new()
|
||||
);
|
||||
($($x:expr),+ $(,)?) => (
|
||||
$crate::Column::with_children(vec![$($crate::core::Element::from($x)),+])
|
||||
$crate::Column::with_children([$($crate::core::Element::from($x)),+])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ macro_rules! row {
|
|||
$crate::Row::new()
|
||||
);
|
||||
($($x:expr),+ $(,)?) => (
|
||||
$crate::Row::with_children(vec![$($crate::core::Element::from($x)),+])
|
||||
$crate::Row::with_children([$($crate::core::Element::from($x)),+])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -65,9 +65,12 @@ where
|
|||
}
|
||||
|
||||
/// Creates a new [`Column`] with the given children.
|
||||
pub fn column<Message, Renderer>(
|
||||
children: Vec<Element<'_, Message, Renderer>>,
|
||||
) -> Column<'_, Message, Renderer> {
|
||||
pub fn column<'a, Message, Renderer>(
|
||||
children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
|
||||
) -> Column<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: core::Renderer,
|
||||
{
|
||||
Column::with_children(children)
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +80,7 @@ pub fn keyed_column<'a, Key, Message, Renderer>(
|
|||
) -> keyed::Column<'a, Key, Message, Renderer>
|
||||
where
|
||||
Key: Copy + PartialEq,
|
||||
Renderer: core::Renderer,
|
||||
{
|
||||
keyed::Column::with_children(children)
|
||||
}
|
||||
|
|
@ -84,9 +88,12 @@ where
|
|||
/// Creates a new [`Row`] with the given children.
|
||||
///
|
||||
/// [`Row`]: crate::Row
|
||||
pub fn row<Message, Renderer>(
|
||||
children: Vec<Element<'_, Message, Renderer>>,
|
||||
) -> Row<'_, Message, Renderer> {
|
||||
pub fn row<'a, Message, Renderer>(
|
||||
children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
|
||||
) -> Row<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: core::Renderer,
|
||||
{
|
||||
Row::with_children(children)
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +271,7 @@ pub fn pick_list<'a, Message, Renderer, T>(
|
|||
on_selected: impl Fn(T) -> Message + 'a,
|
||||
) -> PickList<'a, T, Message, Renderer>
|
||||
where
|
||||
T: ToString + Eq + 'static,
|
||||
T: ToString + PartialEq + 'static,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Renderer: core::text::Renderer,
|
||||
Renderer::Theme: pick_list::StyleSheet
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ where
|
|||
};
|
||||
|
||||
// The size to be available to the widget prior to `Shrink`ing
|
||||
let raw_size = limits.width(width).height(height).resolve(image_size);
|
||||
let raw_size = limits.resolve(width, height, image_size);
|
||||
|
||||
// The uncropped size of the image when fit to the bounds above
|
||||
let full_size = content_fit.fit(image_size, raw_size);
|
||||
|
|
@ -164,12 +164,11 @@ where
|
|||
Renderer: image::Renderer<Handle = Handle>,
|
||||
Handle: Clone + Hash,
|
||||
{
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -97,12 +97,11 @@ where
|
|||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -113,10 +112,11 @@ where
|
|||
) -> layout::Node {
|
||||
let Size { width, height } = renderer.dimensions(&self.handle);
|
||||
|
||||
let mut size = limits
|
||||
.width(self.width)
|
||||
.height(self.height)
|
||||
.resolve(Size::new(width as f32, height as f32));
|
||||
let mut size = limits.resolve(
|
||||
self.width,
|
||||
self.height,
|
||||
Size::new(width as f32, height as f32),
|
||||
);
|
||||
|
||||
let expansion_size = if height > width {
|
||||
self.width
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::core::widget::tree::{self, Tree};
|
|||
use crate::core::widget::Operation;
|
||||
use crate::core::{
|
||||
Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Rectangle,
|
||||
Shell, Widget,
|
||||
Shell, Size, Widget,
|
||||
};
|
||||
|
||||
/// A container that distributes its contents vertically.
|
||||
|
|
@ -30,26 +30,10 @@ where
|
|||
impl<'a, Key, Message, Renderer> Column<'a, Key, Message, Renderer>
|
||||
where
|
||||
Key: Copy + PartialEq,
|
||||
Renderer: crate::core::Renderer,
|
||||
{
|
||||
/// Creates an empty [`Column`].
|
||||
pub fn new() -> Self {
|
||||
Self::with_children(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates a [`Column`] with the given elements.
|
||||
pub fn with_children(
|
||||
children: impl IntoIterator<Item = (Key, Element<'a, Message, Renderer>)>,
|
||||
) -> Self {
|
||||
let (keys, children) = children.into_iter().fold(
|
||||
(Vec::new(), Vec::new()),
|
||||
|(mut keys, mut children), (key, child)| {
|
||||
keys.push(key);
|
||||
children.push(child);
|
||||
|
||||
(keys, children)
|
||||
},
|
||||
);
|
||||
|
||||
Column {
|
||||
spacing: 0.0,
|
||||
padding: Padding::ZERO,
|
||||
|
|
@ -57,11 +41,20 @@ where
|
|||
height: Length::Shrink,
|
||||
max_width: f32::INFINITY,
|
||||
align_items: Alignment::Start,
|
||||
keys,
|
||||
children,
|
||||
keys: Vec::new(),
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`Column`] with the given elements.
|
||||
pub fn with_children(
|
||||
children: impl IntoIterator<Item = (Key, Element<'a, Message, Renderer>)>,
|
||||
) -> Self {
|
||||
children
|
||||
.into_iter()
|
||||
.fold(Self::new(), |column, (key, child)| column.push(key, child))
|
||||
}
|
||||
|
||||
/// Sets the vertical spacing _between_ elements.
|
||||
///
|
||||
/// Custom margins per element do not exist in iced. You should use this
|
||||
|
|
@ -108,8 +101,19 @@ where
|
|||
key: Key,
|
||||
child: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
let child = child.into();
|
||||
let size = child.as_widget().size_hint();
|
||||
|
||||
if size.width.is_fill() {
|
||||
self.width = Length::Fill;
|
||||
}
|
||||
|
||||
if size.height.is_fill() {
|
||||
self.height = Length::Fill;
|
||||
}
|
||||
|
||||
self.keys.push(key);
|
||||
self.children.push(child.into());
|
||||
self.children.push(child);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
@ -117,6 +121,7 @@ where
|
|||
impl<'a, Key, Message, Renderer> Default for Column<'a, Key, Message, Renderer>
|
||||
where
|
||||
Key: Copy + PartialEq,
|
||||
Renderer: crate::core::Renderer,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
|
@ -173,12 +178,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -196,6 +200,8 @@ where
|
|||
layout::flex::Axis::Vertical,
|
||||
renderer,
|
||||
&limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.padding,
|
||||
self.spacing,
|
||||
self.align_items,
|
||||
|
|
|
|||
|
|
@ -142,12 +142,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.with_element(|element| element.as_widget().width())
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.with_element(|element| element.as_widget().size())
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.with_element(|element| element.as_widget().height())
|
||||
fn size_hint(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -244,12 +244,15 @@ where
|
|||
self.rebuild_element_if_necessary();
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.with_element(|element| element.as_widget().width())
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.with_element(|element| element.as_widget().size())
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.with_element(|element| element.as_widget().height())
|
||||
fn size_hint(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use std::hash::Hash;
|
|||
|
||||
/// Creates a new [`Lazy`] widget with the given data `Dependency` and a
|
||||
/// closure that can turn this data into a widget tree.
|
||||
#[cfg(feature = "lazy")]
|
||||
pub fn lazy<'a, Message, Renderer, Dependency, View>(
|
||||
dependency: Dependency,
|
||||
view: impl Fn(&Dependency) -> View + 'a,
|
||||
|
|
@ -19,6 +20,7 @@ where
|
|||
|
||||
/// Turns an implementor of [`Component`] into an [`Element`] that can be
|
||||
/// embedded in any application.
|
||||
#[cfg(feature = "lazy")]
|
||||
pub fn component<'a, C, Message, Renderer>(
|
||||
component: C,
|
||||
) -> Element<'a, Message, Renderer>
|
||||
|
|
@ -37,6 +39,7 @@ where
|
|||
/// The `view` closure will be provided with the current [`Size`] of
|
||||
/// the [`Responsive`] widget and, therefore, can be used to build the
|
||||
/// contents of the widget in a responsive way.
|
||||
#[cfg(feature = "lazy")]
|
||||
pub fn responsive<'a, Message, Renderer>(
|
||||
f: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a,
|
||||
) -> Responsive<'a, Message, Renderer>
|
||||
|
|
|
|||
|
|
@ -135,12 +135,11 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
Length::Fill
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Fill
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Fill,
|
||||
height: Length::Fill,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::core::renderer;
|
|||
use crate::core::touch;
|
||||
use crate::core::widget::{tree, Operation, Tree};
|
||||
use crate::core::{
|
||||
Clipboard, Element, Layout, Length, Rectangle, Shell, Widget,
|
||||
Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Widget,
|
||||
};
|
||||
|
||||
/// Emit messages on mouse events.
|
||||
|
|
@ -110,12 +110,8 @@ where
|
|||
tree.diff_children(std::slice::from_ref(&self.content));
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.content.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.content.as_widget().height()
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.content.as_widget().size()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -254,15 +254,14 @@ where
|
|||
)
|
||||
.width(self.width);
|
||||
|
||||
let mut node = self.container.layout(self.state, renderer, &limits);
|
||||
let node = self.container.layout(self.state, renderer, &limits);
|
||||
let size = node.size();
|
||||
|
||||
node.move_to(if space_below > space_above {
|
||||
position + Vector::new(0.0, self.target_height)
|
||||
} else {
|
||||
position - Vector::new(0.0, node.size().height)
|
||||
});
|
||||
|
||||
node
|
||||
position - Vector::new(0.0, size.height)
|
||||
})
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
@ -343,12 +342,11 @@ where
|
|||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
Length::Fill
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Fill,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -359,7 +357,6 @@ where
|
|||
) -> layout::Node {
|
||||
use std::f32;
|
||||
|
||||
let limits = limits.width(Length::Fill).height(Length::Shrink);
|
||||
let text_size =
|
||||
self.text_size.unwrap_or_else(|| renderer.default_size());
|
||||
|
||||
|
|
@ -372,7 +369,7 @@ where
|
|||
* self.options.len() as f32,
|
||||
);
|
||||
|
||||
limits.resolve(intrinsic)
|
||||
limits.resolve(Length::Fill, Length::Shrink, intrinsic)
|
||||
};
|
||||
|
||||
layout::Node::new(size)
|
||||
|
|
|
|||
|
|
@ -265,12 +265,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -490,8 +489,7 @@ pub fn layout<Renderer, T>(
|
|||
&layout::Limits,
|
||||
) -> layout::Node,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(width).height(height);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
let size = limits.resolve(width, height, Size::ZERO);
|
||||
|
||||
let regions = node.pane_regions(spacing, size);
|
||||
let children = contents
|
||||
|
|
@ -500,16 +498,14 @@ pub fn layout<Renderer, T>(
|
|||
let region = regions.get(&pane)?;
|
||||
let size = Size::new(region.width, region.height);
|
||||
|
||||
let mut node = layout_content(
|
||||
let node = layout_content(
|
||||
content,
|
||||
tree,
|
||||
renderer,
|
||||
&layout::Limits::new(size, size),
|
||||
);
|
||||
|
||||
node.move_to(Point::new(region.x, region.y));
|
||||
|
||||
Some(node)
|
||||
Some(node.move_to(Point::new(region.x, region.y)))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -531,6 +527,8 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||
on_resize: &Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>,
|
||||
) -> event::Status {
|
||||
const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
|
||||
|
||||
let mut event_status = event::Status::Ignored;
|
||||
|
||||
match event {
|
||||
|
|
@ -572,7 +570,6 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
shell,
|
||||
contents,
|
||||
on_click,
|
||||
on_drag,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -584,7 +581,6 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
shell,
|
||||
contents,
|
||||
on_click,
|
||||
on_drag,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -637,7 +633,49 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
}
|
||||
Event::Mouse(mouse::Event::CursorMoved { .. })
|
||||
| Event::Touch(touch::Event::FingerMoved { .. }) => {
|
||||
if let Some((_, on_resize)) = on_resize {
|
||||
if let Some((_, origin)) = action.clicked_pane() {
|
||||
if let Some(on_drag) = &on_drag {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
if let Some(cursor_position) = cursor.position_over(bounds)
|
||||
{
|
||||
let mut clicked_region = contents
|
||||
.zip(layout.children())
|
||||
.filter(|(_, layout)| {
|
||||
layout.bounds().contains(cursor_position)
|
||||
});
|
||||
|
||||
if let Some(((pane, content), layout)) =
|
||||
clicked_region.next()
|
||||
{
|
||||
if content
|
||||
.can_be_dragged_at(layout, cursor_position)
|
||||
{
|
||||
let pane_position = layout.position();
|
||||
|
||||
let new_origin = cursor_position
|
||||
- Vector::new(
|
||||
pane_position.x,
|
||||
pane_position.y,
|
||||
);
|
||||
|
||||
if new_origin.distance(origin)
|
||||
> DRAG_DEADBAND_DISTANCE
|
||||
{
|
||||
*action = state::Action::Dragging {
|
||||
pane,
|
||||
origin,
|
||||
};
|
||||
|
||||
shell.publish(on_drag(DragEvent::Picked {
|
||||
pane,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some((_, on_resize)) = on_resize {
|
||||
if let Some((split, _)) = action.picked_split() {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
|
|
@ -712,7 +750,6 @@ fn click_pane<'a, Message, T>(
|
|||
shell: &mut Shell<'_, Message>,
|
||||
contents: impl Iterator<Item = (Pane, T)>,
|
||||
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
||||
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||
) where
|
||||
T: Draggable,
|
||||
{
|
||||
|
|
@ -720,23 +757,15 @@ fn click_pane<'a, Message, T>(
|
|||
.zip(layout.children())
|
||||
.filter(|(_, layout)| layout.bounds().contains(cursor_position));
|
||||
|
||||
if let Some(((pane, content), layout)) = clicked_region.next() {
|
||||
if let Some(((pane, _), layout)) = clicked_region.next() {
|
||||
if let Some(on_click) = &on_click {
|
||||
shell.publish(on_click(pane));
|
||||
}
|
||||
|
||||
if let Some(on_drag) = &on_drag {
|
||||
if content.can_be_dragged_at(layout, cursor_position) {
|
||||
let pane_position = layout.position();
|
||||
|
||||
let origin = cursor_position
|
||||
- Vector::new(pane_position.x, pane_position.y);
|
||||
|
||||
*action = state::Action::Dragging { pane, origin };
|
||||
|
||||
shell.publish(on_drag(DragEvent::Picked { pane }));
|
||||
}
|
||||
}
|
||||
let pane_position = layout.position();
|
||||
let origin =
|
||||
cursor_position - Vector::new(pane_position.x, pane_position.y);
|
||||
*action = state::Action::Clicking { pane, origin };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -749,7 +778,7 @@ pub fn mouse_interaction(
|
|||
spacing: f32,
|
||||
resize_leeway: Option<f32>,
|
||||
) -> Option<mouse::Interaction> {
|
||||
if action.picked_pane().is_some() {
|
||||
if action.clicked_pane().is_some() || action.picked_pane().is_some() {
|
||||
return Some(mouse::Interaction::Grabbing);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ where
|
|||
|
||||
let title_bar_size = title_bar_layout.size();
|
||||
|
||||
let mut body_layout = self.body.as_widget().layout(
|
||||
let body_layout = self.body.as_widget().layout(
|
||||
&mut tree.children[0],
|
||||
renderer,
|
||||
&layout::Limits::new(
|
||||
|
|
@ -177,11 +177,12 @@ where
|
|||
),
|
||||
);
|
||||
|
||||
body_layout.move_to(Point::new(0.0, title_bar_size.height));
|
||||
|
||||
layout::Node::with_children(
|
||||
max_size,
|
||||
vec![title_bar_layout, body_layout],
|
||||
vec![
|
||||
title_bar_layout,
|
||||
body_layout.move_to(Point::new(0.0, title_bar_size.height)),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
self.body.as_widget().layout(
|
||||
|
|
|
|||
|
|
@ -403,6 +403,15 @@ pub enum Action {
|
|||
///
|
||||
/// [`PaneGrid`]: super::PaneGrid
|
||||
Idle,
|
||||
/// A [`Pane`] in the [`PaneGrid`] is being clicked.
|
||||
///
|
||||
/// [`PaneGrid`]: super::PaneGrid
|
||||
Clicking {
|
||||
/// The [`Pane`] being clicked.
|
||||
pane: Pane,
|
||||
/// The starting [`Point`] of the click interaction.
|
||||
origin: Point,
|
||||
},
|
||||
/// A [`Pane`] in the [`PaneGrid`] is being dragged.
|
||||
///
|
||||
/// [`PaneGrid`]: super::PaneGrid
|
||||
|
|
@ -432,6 +441,14 @@ impl Action {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the current [`Pane`] that is being clicked, if any.
|
||||
pub fn clicked_pane(&self) -> Option<(Pane, Point)> {
|
||||
match *self {
|
||||
Action::Clicking { pane, origin, .. } => Some((pane, origin)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current [`Split`] that is being dragged, if any.
|
||||
pub fn picked_split(&self) -> Option<(Split, Axis)> {
|
||||
match *self {
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ where
|
|||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.pad(self.padding);
|
||||
let limits = limits.shrink(self.padding);
|
||||
let max_size = limits.max();
|
||||
|
||||
let title_layout = self.content.as_widget().layout(
|
||||
|
|
@ -228,8 +228,8 @@ where
|
|||
|
||||
let title_size = title_layout.size();
|
||||
|
||||
let mut node = if let Some(controls) = &self.controls {
|
||||
let mut controls_layout = controls.as_widget().layout(
|
||||
let node = if let Some(controls) = &self.controls {
|
||||
let controls_layout = controls.as_widget().layout(
|
||||
&mut tree.children[1],
|
||||
renderer,
|
||||
&layout::Limits::new(Size::ZERO, max_size),
|
||||
|
|
@ -240,11 +240,13 @@ where
|
|||
|
||||
let height = title_size.height.max(controls_size.height);
|
||||
|
||||
controls_layout.move_to(Point::new(space_before_controls, 0.0));
|
||||
|
||||
layout::Node::with_children(
|
||||
Size::new(max_size.width, height),
|
||||
vec![title_layout, controls_layout],
|
||||
vec![
|
||||
title_layout,
|
||||
controls_layout
|
||||
.move_to(Point::new(space_before_controls, 0.0)),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
layout::Node::with_children(
|
||||
|
|
@ -253,9 +255,7 @@ where
|
|||
)
|
||||
};
|
||||
|
||||
node.move_to(Point::new(self.padding.left, self.padding.top));
|
||||
|
||||
layout::Node::with_children(node.size().pad(self.padding), vec![node])
|
||||
layout::Node::container(node, self.padding)
|
||||
}
|
||||
|
||||
pub(crate) fn operate(
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ where
|
|||
|
||||
impl<'a, T: 'a, Message, Renderer> PickList<'a, T, Message, Renderer>
|
||||
where
|
||||
T: ToString + Eq,
|
||||
T: ToString + PartialEq,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet
|
||||
|
|
@ -145,7 +145,7 @@ where
|
|||
impl<'a, T: 'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for PickList<'a, T, Message, Renderer>
|
||||
where
|
||||
T: Clone + ToString + Eq + 'static,
|
||||
T: Clone + ToString + PartialEq + 'static,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
|
|
@ -164,12 +164,11 @@ where
|
|||
tree::State::new(State::<Renderer::Paragraph>::new())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -282,7 +281,7 @@ where
|
|||
impl<'a, T: 'a, Message, Renderer> From<PickList<'a, T, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
T: Clone + ToString + Eq + 'static,
|
||||
T: Clone + ToString + PartialEq + 'static,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
|
|
@ -393,7 +392,6 @@ where
|
|||
{
|
||||
use std::f32;
|
||||
|
||||
let limits = limits.width(width).height(Length::Shrink).pad(padding);
|
||||
let font = font.unwrap_or_else(|| renderer.default_font());
|
||||
let text_size = text_size.unwrap_or_else(|| renderer.default_size());
|
||||
|
||||
|
|
@ -451,7 +449,11 @@ where
|
|||
f32::from(text_line_height.to_absolute(text_size)),
|
||||
);
|
||||
|
||||
limits.resolve(intrinsic).pad(padding)
|
||||
limits
|
||||
.width(width)
|
||||
.shrink(padding)
|
||||
.resolve(width, Length::Shrink, intrinsic)
|
||||
.expand(padding)
|
||||
};
|
||||
|
||||
layout::Node::new(size)
|
||||
|
|
|
|||
|
|
@ -85,12 +85,11 @@ where
|
|||
Renderer: crate::core::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT))
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -99,13 +98,11 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits
|
||||
.width(self.width)
|
||||
.height(self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)));
|
||||
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
layout::atomic(
|
||||
limits,
|
||||
self.width,
|
||||
self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
|
|
|
|||
|
|
@ -50,12 +50,11 @@ impl<'a> QRCode<'a> {
|
|||
}
|
||||
|
||||
impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> {
|
||||
fn width(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -201,12 +201,11 @@ where
|
|||
tree::State::new(widget::text::State::<Renderer::Paragraph>::default())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use crate::core::renderer;
|
|||
use crate::core::widget::{Operation, Tree};
|
||||
use crate::core::{
|
||||
Alignment, Clipboard, Element, Length, Padding, Pixels, Rectangle, Shell,
|
||||
Widget,
|
||||
Size, Widget,
|
||||
};
|
||||
|
||||
/// A container that distributes its contents horizontally.
|
||||
|
|
@ -21,26 +21,29 @@ pub struct Row<'a, Message, Renderer = crate::Renderer> {
|
|||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
|
||||
impl<'a, Message, Renderer> Row<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::core::Renderer,
|
||||
{
|
||||
/// Creates an empty [`Row`].
|
||||
pub fn new() -> Self {
|
||||
Self::with_children(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates a [`Row`] with the given elements.
|
||||
pub fn with_children(
|
||||
children: Vec<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
Row {
|
||||
spacing: 0.0,
|
||||
padding: Padding::ZERO,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
align_items: Alignment::Start,
|
||||
children,
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`Row`] with the given elements.
|
||||
pub fn with_children(
|
||||
children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
children.into_iter().fold(Self::new(), Self::push)
|
||||
}
|
||||
|
||||
/// Sets the horizontal spacing _between_ elements.
|
||||
///
|
||||
/// Custom margins per element do not exist in iced. You should use this
|
||||
|
|
@ -80,12 +83,26 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
|
|||
mut self,
|
||||
child: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
self.children.push(child.into());
|
||||
let child = child.into();
|
||||
let size = child.as_widget().size_hint();
|
||||
|
||||
if size.width.is_fill() {
|
||||
self.width = Length::Fill;
|
||||
}
|
||||
|
||||
if size.height.is_fill() {
|
||||
self.height = Length::Fill;
|
||||
}
|
||||
|
||||
self.children.push(child);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> {
|
||||
impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: crate::core::Renderer,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
|
@ -104,12 +121,11 @@ where
|
|||
tree.diff_children(&self.children);
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -118,12 +134,12 @@ where
|
|||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
|
||||
layout::flex::resolve(
|
||||
layout::flex::Axis::Horizontal,
|
||||
renderer,
|
||||
&limits,
|
||||
limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.padding,
|
||||
self.spacing,
|
||||
self.align_items,
|
||||
|
|
|
|||
|
|
@ -62,12 +62,11 @@ where
|
|||
Renderer: crate::core::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
{
|
||||
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(
|
||||
|
|
@ -76,9 +75,7 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
|
||||
layout::Node::new(limits.resolve(Size::ZERO))
|
||||
layout::atomic(limits, self.width, self.height)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
|
|
|
|||
|
|
@ -220,12 +220,11 @@ where
|
|||
tree.diff_children(std::slice::from_ref(&self.content));
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -470,28 +469,25 @@ pub fn layout<Renderer>(
|
|||
direction: &Direction,
|
||||
layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(width).height(height);
|
||||
layout::contained(limits, width, height, |limits| {
|
||||
let child_limits = layout::Limits::new(
|
||||
Size::new(limits.min().width, limits.min().height),
|
||||
Size::new(
|
||||
if direction.horizontal().is_some() {
|
||||
f32::INFINITY
|
||||
} else {
|
||||
limits.max().width
|
||||
},
|
||||
if direction.vertical().is_some() {
|
||||
f32::MAX
|
||||
} else {
|
||||
limits.max().height
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let child_limits = layout::Limits::new(
|
||||
Size::new(limits.min().width, limits.min().height),
|
||||
Size::new(
|
||||
if direction.horizontal().is_some() {
|
||||
f32::INFINITY
|
||||
} else {
|
||||
limits.max().width
|
||||
},
|
||||
if direction.vertical().is_some() {
|
||||
f32::MAX
|
||||
} else {
|
||||
limits.max().height
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let content = layout_content(renderer, &child_limits);
|
||||
let size = limits.resolve(content.size());
|
||||
|
||||
layout::Node::with_children(size, vec![content])
|
||||
layout_content(renderer, &child_limits)
|
||||
})
|
||||
}
|
||||
|
||||
/// Processes an [`Event`] and updates the [`State`] of a [`Scrollable`]
|
||||
|
|
|
|||
|
|
@ -70,12 +70,11 @@ where
|
|||
tree::State::new(P::State::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(
|
||||
|
|
@ -84,10 +83,7 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
layout::atomic(limits, self.width, self.height)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
|
|||
|
|
@ -159,12 +159,11 @@ where
|
|||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -173,10 +172,7 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
layout::atomic(limits, self.width, self.height)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
|
|||
|
|
@ -45,12 +45,11 @@ impl<Message, Renderer> Widget<Message, Renderer> for Space
|
|||
where
|
||||
Renderer: core::Renderer,
|
||||
{
|
||||
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(
|
||||
|
|
@ -59,9 +58,7 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
|
||||
layout::Node::new(limits.resolve(Size::ZERO))
|
||||
layout::atomic(limits, self.width, self.height)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
|
|
|
|||
|
|
@ -96,12 +96,11 @@ where
|
|||
Renderer: svg::Renderer,
|
||||
Renderer::Theme: iced_style::svg::StyleSheet,
|
||||
{
|
||||
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(
|
||||
|
|
@ -115,10 +114,7 @@ where
|
|||
let image_size = Size::new(width as f32, height as f32);
|
||||
|
||||
// The size to be available to the widget prior to `Shrink`ing
|
||||
let raw_size = limits
|
||||
.width(self.width)
|
||||
.height(self.height)
|
||||
.resolve(image_size);
|
||||
let raw_size = limits.resolve(self.width, self.height, image_size);
|
||||
|
||||
// The uncropped size of the image when fit to the bounds above
|
||||
let full_size = self.content_fit.fit(image_size, raw_size);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::core::text::highlighter::{self, Highlighter};
|
|||
use crate::core::text::{self, LineHeight};
|
||||
use crate::core::widget::{self, Widget};
|
||||
use crate::core::{
|
||||
Clipboard, Color, Element, Length, Padding, Pixels, Rectangle, Shell,
|
||||
Clipboard, Color, Element, Length, Padding, Pixels, Rectangle, Shell, Size,
|
||||
Vector,
|
||||
};
|
||||
|
||||
|
|
@ -316,12 +316,11 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -350,7 +349,7 @@ where
|
|||
}
|
||||
|
||||
internal.editor.update(
|
||||
limits.pad(self.padding).max(),
|
||||
limits.shrink(self.padding).max(),
|
||||
self.font.unwrap_or_else(|| renderer.default_font()),
|
||||
self.text_size.unwrap_or_else(|| renderer.default_size()),
|
||||
self.line_height,
|
||||
|
|
|
|||
|
|
@ -283,12 +283,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -506,14 +505,11 @@ where
|
|||
{
|
||||
let font = font.unwrap_or_else(|| renderer.default_font());
|
||||
let text_size = size.unwrap_or_else(|| renderer.default_size());
|
||||
|
||||
let padding = padding.fit(Size::ZERO, limits.max());
|
||||
let limits = limits
|
||||
.width(width)
|
||||
.pad(padding)
|
||||
.height(line_height.to_absolute(text_size));
|
||||
let height = line_height.to_absolute(text_size);
|
||||
|
||||
let text_bounds = limits.resolve(Size::ZERO);
|
||||
let limits = limits.width(width).shrink(padding);
|
||||
let text_bounds = limits.resolve(width, height, Size::ZERO);
|
||||
|
||||
let placeholder_text = Text {
|
||||
font,
|
||||
|
|
@ -552,41 +548,41 @@ where
|
|||
|
||||
let icon_width = state.icon.min_width();
|
||||
|
||||
let mut text_node = layout::Node::new(
|
||||
text_bounds - Size::new(icon_width + icon.spacing, 0.0),
|
||||
);
|
||||
|
||||
let mut icon_node =
|
||||
layout::Node::new(Size::new(icon_width, text_bounds.height));
|
||||
|
||||
match icon.side {
|
||||
Side::Left => {
|
||||
text_node.move_to(Point::new(
|
||||
let (text_position, icon_position) = match icon.side {
|
||||
Side::Left => (
|
||||
Point::new(
|
||||
padding.left + icon_width + icon.spacing,
|
||||
padding.top,
|
||||
));
|
||||
|
||||
icon_node.move_to(Point::new(padding.left, padding.top));
|
||||
}
|
||||
Side::Right => {
|
||||
text_node.move_to(Point::new(padding.left, padding.top));
|
||||
|
||||
icon_node.move_to(Point::new(
|
||||
),
|
||||
Point::new(padding.left, padding.top),
|
||||
),
|
||||
Side::Right => (
|
||||
Point::new(padding.left, padding.top),
|
||||
Point::new(
|
||||
padding.left + text_bounds.width - icon_width,
|
||||
padding.top,
|
||||
));
|
||||
}
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
let text_node = layout::Node::new(
|
||||
text_bounds - Size::new(icon_width + icon.spacing, 0.0),
|
||||
)
|
||||
.move_to(text_position);
|
||||
|
||||
let icon_node =
|
||||
layout::Node::new(Size::new(icon_width, text_bounds.height))
|
||||
.move_to(icon_position);
|
||||
|
||||
layout::Node::with_children(
|
||||
text_bounds.pad(padding),
|
||||
text_bounds.expand(padding),
|
||||
vec![text_node, icon_node],
|
||||
)
|
||||
} else {
|
||||
let mut text = layout::Node::new(text_bounds);
|
||||
text.move_to(Point::new(padding.left, padding.top));
|
||||
let text = layout::Node::new(text_bounds)
|
||||
.move_to(Point::new(padding.left, padding.top));
|
||||
|
||||
layout::Node::with_children(text_bounds.pad(padding), vec![text])
|
||||
layout::Node::with_children(text_bounds.expand(padding), vec![text])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1192,31 +1188,39 @@ pub fn draw<Renderer>(
|
|||
(None, 0.0)
|
||||
};
|
||||
|
||||
if let Some((cursor, color)) = cursor {
|
||||
renderer.with_translation(Vector::new(-offset, 0.0), |renderer| {
|
||||
renderer.fill_quad(cursor, color);
|
||||
});
|
||||
} else {
|
||||
renderer.with_translation(Vector::ZERO, |_| {});
|
||||
}
|
||||
let draw = |renderer: &mut Renderer, viewport| {
|
||||
if let Some((cursor, color)) = cursor {
|
||||
renderer.with_translation(Vector::new(-offset, 0.0), |renderer| {
|
||||
renderer.fill_quad(cursor, color);
|
||||
});
|
||||
} else {
|
||||
renderer.with_translation(Vector::ZERO, |_| {});
|
||||
}
|
||||
|
||||
renderer.fill_paragraph(
|
||||
if text.is_empty() {
|
||||
&state.placeholder
|
||||
} else {
|
||||
&state.value
|
||||
},
|
||||
Point::new(text_bounds.x, text_bounds.center_y())
|
||||
- Vector::new(offset, 0.0),
|
||||
if text.is_empty() {
|
||||
theme.placeholder_color(style)
|
||||
} else if is_disabled {
|
||||
theme.disabled_color(style)
|
||||
} else {
|
||||
theme.value_color(style)
|
||||
},
|
||||
text_bounds,
|
||||
);
|
||||
renderer.fill_paragraph(
|
||||
if text.is_empty() {
|
||||
&state.placeholder
|
||||
} else {
|
||||
&state.value
|
||||
},
|
||||
Point::new(text_bounds.x, text_bounds.center_y())
|
||||
- Vector::new(offset, 0.0),
|
||||
if text.is_empty() {
|
||||
theme.placeholder_color(style)
|
||||
} else if is_disabled {
|
||||
theme.disabled_color(style)
|
||||
} else {
|
||||
theme.value_color(style)
|
||||
},
|
||||
viewport,
|
||||
);
|
||||
};
|
||||
|
||||
if cursor.is_some() {
|
||||
renderer.with_layer(text_bounds, |renderer| draw(renderer, *viewport));
|
||||
} else {
|
||||
draw(renderer, text_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the current [`mouse::Interaction`] of the [`TextInput`].
|
||||
|
|
|
|||
|
|
@ -168,12 +168,11 @@ where
|
|||
tree::State::new(widget::text::State::<Renderer::Paragraph>::default())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the [`text::Shaping`] strategy of the [`Tooltip`].
|
||||
pub fn text_shaping(mut self, shaping: text::Shaping) -> Self {
|
||||
self.tooltip = self.tooltip.shaping(shaping);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the font of the [`Tooltip`].
|
||||
///
|
||||
/// [`Font`]: Renderer::Font
|
||||
|
|
@ -125,12 +131,8 @@ where
|
|||
widget::tree::Tag::of::<State>()
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.content.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.content.as_widget().height()
|
||||
fn size(&self) -> Size<Length> {
|
||||
self.content.as_widget().size()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -347,7 +349,7 @@ where
|
|||
.then(|| viewport.size())
|
||||
.unwrap_or(Size::INFINITY),
|
||||
)
|
||||
.pad(Padding::new(self.padding)),
|
||||
.shrink(Padding::new(self.padding)),
|
||||
);
|
||||
|
||||
let text_bounds = text_layout.bounds();
|
||||
|
|
|
|||
|
|
@ -156,12 +156,11 @@ where
|
|||
tree::State::new(State::new())
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.height
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Shrink,
|
||||
height: self.height,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -170,10 +169,7 @@ where
|
|||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
layout::Node::new(size)
|
||||
layout::atomic(limits, self.width, self.height)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
|
|||
|
|
@ -717,9 +717,19 @@ pub fn run_command<A, C, E>(
|
|||
)))
|
||||
.expect("Send message to event loop");
|
||||
}
|
||||
window::Action::FetchMaximized(_id, callback) => {
|
||||
proxy
|
||||
.send_event(callback(window.is_maximized()))
|
||||
.expect("Send message to event loop");
|
||||
}
|
||||
window::Action::Maximize(_id, maximized) => {
|
||||
window.set_maximized(maximized);
|
||||
}
|
||||
window::Action::FetchMinimized(_id, callback) => {
|
||||
proxy
|
||||
.send_event(callback(window.is_minimized()))
|
||||
.expect("Send message to event loop");
|
||||
}
|
||||
window::Action::Minimize(_id, minimized) => {
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -915,11 +915,25 @@ fn run_command<A, C, E>(
|
|||
.expect("Send message to event loop");
|
||||
}
|
||||
}
|
||||
window::Action::FetchMaximized(id, callback) => {
|
||||
if let Some(window) = window_manager.get_mut(id) {
|
||||
proxy
|
||||
.send_event(callback(window.raw.is_maximized()))
|
||||
.expect("Send message to event loop");
|
||||
}
|
||||
}
|
||||
window::Action::Maximize(id, maximized) => {
|
||||
if let Some(window) = window_manager.get_mut(id) {
|
||||
window.raw.set_maximized(maximized);
|
||||
}
|
||||
}
|
||||
window::Action::FetchMinimized(id, callback) => {
|
||||
if let Some(window) = window_manager.get_mut(id) {
|
||||
proxy
|
||||
.send_event(callback(window.raw.is_minimized()))
|
||||
.expect("Send message to event loop");
|
||||
}
|
||||
}
|
||||
window::Action::Minimize(id, minimized) => {
|
||||
if let Some(window) = window_manager.get_mut(id) {
|
||||
window.raw.set_minimized(minimized);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue