Merge pull request #52 from hecrj/custom-layout-engine
Custom layout engine
This commit is contained in:
commit
bc8d347736
53 changed files with 1270 additions and 758 deletions
|
|
@ -15,7 +15,4 @@ pub enum Align {
|
||||||
|
|
||||||
/// Align at the end of the cross axis.
|
/// Align at the end of the cross axis.
|
||||||
End,
|
End,
|
||||||
|
|
||||||
/// Stretch over the cross axis.
|
|
||||||
Stretch,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
/// Distribution on the main axis of a container.
|
|
||||||
///
|
|
||||||
/// * On a [`Column`], it describes __vertical__ distribution.
|
|
||||||
/// * On a [`Row`], it describes __horizontal__ distribution.
|
|
||||||
///
|
|
||||||
/// [`Column`]: widget/struct.Column.html
|
|
||||||
/// [`Row`]: widget/struct.Row.html
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Justify {
|
|
||||||
/// Place items at the start of the main axis.
|
|
||||||
Start,
|
|
||||||
|
|
||||||
/// Place items at the center of the main axis.
|
|
||||||
Center,
|
|
||||||
|
|
||||||
/// Place items at the end of the main axis.
|
|
||||||
End,
|
|
||||||
|
|
||||||
/// Place items with space between.
|
|
||||||
SpaceBetween,
|
|
||||||
|
|
||||||
/// Place items with space around.
|
|
||||||
SpaceAround,
|
|
||||||
|
|
||||||
/// Place items with evenly distributed space.
|
|
||||||
SpaceEvenly,
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
/// The strategy used to fill space in a specific dimension.
|
/// The strategy used to fill space in a specific dimension.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
|
||||||
pub enum Length {
|
pub enum Length {
|
||||||
Fill,
|
Fill,
|
||||||
Shrink,
|
Shrink,
|
||||||
Units(u16),
|
Units(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Length {
|
||||||
|
pub fn fill_factor(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Length::Fill => 1,
|
||||||
|
Length::Shrink => 0,
|
||||||
|
Length::Units(_) => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ pub mod widget;
|
||||||
mod align;
|
mod align;
|
||||||
mod background;
|
mod background;
|
||||||
mod color;
|
mod color;
|
||||||
mod justify;
|
|
||||||
mod length;
|
mod length;
|
||||||
mod point;
|
mod point;
|
||||||
mod rectangle;
|
mod rectangle;
|
||||||
|
|
@ -12,7 +11,6 @@ mod vector;
|
||||||
pub use align::Align;
|
pub use align::Align;
|
||||||
pub use background::Background;
|
pub use background::Background;
|
||||||
pub use color::Color;
|
pub use color::Color;
|
||||||
pub use justify::Justify;
|
|
||||||
pub use length::Length;
|
pub use length::Length;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
pub use rectangle::Rectangle;
|
pub use rectangle::Rectangle;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::Point;
|
use crate::Point;
|
||||||
|
|
||||||
/// A rectangle.
|
/// A rectangle.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub struct Rectangle<T = f32> {
|
pub struct Rectangle<T = f32> {
|
||||||
/// X coordinate of the top-left corner.
|
/// X coordinate of the top-left corner.
|
||||||
pub x: T,
|
pub x: T,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
//! ```
|
//! ```
|
||||||
mod checkbox;
|
mod checkbox;
|
||||||
mod column;
|
mod column;
|
||||||
|
mod container;
|
||||||
mod image;
|
mod image;
|
||||||
mod radio;
|
mod radio;
|
||||||
mod row;
|
mod row;
|
||||||
|
|
@ -32,6 +33,7 @@ pub use text_input::TextInput;
|
||||||
|
|
||||||
pub use checkbox::Checkbox;
|
pub use checkbox::Checkbox;
|
||||||
pub use column::Column;
|
pub use column::Column;
|
||||||
|
pub use container::Container;
|
||||||
pub use image::Image;
|
pub use image::Image;
|
||||||
pub use radio::Radio;
|
pub use radio::Radio;
|
||||||
pub use row::Row;
|
pub use row::Row;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
//! [`Button`]: struct.Button.html
|
//! [`Button`]: struct.Button.html
|
||||||
//! [`State`]: struct.State.html
|
//! [`State`]: struct.State.html
|
||||||
|
|
||||||
use crate::{Align, Background, Length};
|
use crate::{Background, Length};
|
||||||
|
|
||||||
/// A generic widget that produces a message when clicked.
|
/// A generic widget that produces a message when clicked.
|
||||||
pub struct Button<'a, Message, Element> {
|
pub struct Button<'a, Message, Element> {
|
||||||
|
|
@ -24,8 +24,6 @@ pub struct Button<'a, Message, Element> {
|
||||||
pub background: Option<Background>,
|
pub background: Option<Background>,
|
||||||
|
|
||||||
pub border_radius: u16,
|
pub border_radius: u16,
|
||||||
|
|
||||||
pub align_self: Option<Align>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Element> std::fmt::Debug for Button<'a, Message, Element>
|
impl<'a, Message, Element> std::fmt::Debug for Button<'a, Message, Element>
|
||||||
|
|
@ -57,7 +55,6 @@ impl<'a, Message, Element> Button<'a, Message, Element> {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
background: None,
|
background: None,
|
||||||
border_radius: 0,
|
border_radius: 0,
|
||||||
align_self: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,17 +81,6 @@ impl<'a, Message, Element> Button<'a, Message, Element> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the alignment of the [`Button`] itself.
|
|
||||||
///
|
|
||||||
/// This is useful if you want to override the default alignment given by
|
|
||||||
/// the parent container.
|
|
||||||
///
|
|
||||||
/// [`Button`]: struct.Button.html
|
|
||||||
pub fn align_self(mut self, align: Align) -> Self {
|
|
||||||
self.align_self = Some(align);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the message that will be produced when the [`Button`] is pressed.
|
/// Sets the message that will be produced when the [`Button`] is pressed.
|
||||||
///
|
///
|
||||||
/// [`Button`]: struct.Button.html
|
/// [`Button`]: struct.Button.html
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::{Align, Justify, Length};
|
use crate::{Align, Length};
|
||||||
|
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
/// A container that distributes its contents vertically.
|
/// A container that distributes its contents vertically.
|
||||||
///
|
///
|
||||||
|
|
@ -10,11 +12,9 @@ pub struct Column<Element> {
|
||||||
pub padding: u16,
|
pub padding: u16,
|
||||||
pub width: Length,
|
pub width: Length,
|
||||||
pub height: Length,
|
pub height: Length,
|
||||||
pub max_width: Length,
|
pub max_width: u32,
|
||||||
pub max_height: Length,
|
pub max_height: u32,
|
||||||
pub align_self: Option<Align>,
|
|
||||||
pub align_items: Align,
|
pub align_items: Align,
|
||||||
pub justify_content: Justify,
|
|
||||||
pub children: Vec<Element>,
|
pub children: Vec<Element>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,11 +28,9 @@ impl<Element> Column<Element> {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
max_width: Length::Shrink,
|
max_width: u32::MAX,
|
||||||
max_height: Length::Shrink,
|
max_height: u32::MAX,
|
||||||
align_self: None,
|
|
||||||
align_items: Align::Start,
|
align_items: Align::Start,
|
||||||
justify_content: Justify::Start,
|
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +72,7 @@ impl<Element> Column<Element> {
|
||||||
/// Sets the maximum width of the [`Column`].
|
/// Sets the maximum width of the [`Column`].
|
||||||
///
|
///
|
||||||
/// [`Column`]: struct.Column.html
|
/// [`Column`]: struct.Column.html
|
||||||
pub fn max_width(mut self, max_width: Length) -> Self {
|
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||||
self.max_width = max_width;
|
self.max_width = max_width;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -82,22 +80,11 @@ impl<Element> Column<Element> {
|
||||||
/// Sets the maximum height of the [`Column`] in pixels.
|
/// Sets the maximum height of the [`Column`] in pixels.
|
||||||
///
|
///
|
||||||
/// [`Column`]: struct.Column.html
|
/// [`Column`]: struct.Column.html
|
||||||
pub fn max_height(mut self, max_height: Length) -> Self {
|
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||||
self.max_height = max_height;
|
self.max_height = max_height;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the alignment of the [`Column`] itself.
|
|
||||||
///
|
|
||||||
/// This is useful if you want to override the default alignment given by
|
|
||||||
/// the parent container.
|
|
||||||
///
|
|
||||||
/// [`Column`]: struct.Column.html
|
|
||||||
pub fn align_self(mut self, align: Align) -> Self {
|
|
||||||
self.align_self = Some(align);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the horizontal alignment of the contents of the [`Column`] .
|
/// Sets the horizontal alignment of the contents of the [`Column`] .
|
||||||
///
|
///
|
||||||
/// [`Column`]: struct.Column.html
|
/// [`Column`]: struct.Column.html
|
||||||
|
|
@ -106,15 +93,6 @@ impl<Element> Column<Element> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the vertical distribution strategy for the contents of the
|
|
||||||
/// [`Column`] .
|
|
||||||
///
|
|
||||||
/// [`Column`]: struct.Column.html
|
|
||||||
pub fn justify_content(mut self, justify: Justify) -> Self {
|
|
||||||
self.justify_content = justify;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds an element to the [`Column`].
|
/// Adds an element to the [`Column`].
|
||||||
///
|
///
|
||||||
/// [`Column`]: struct.Column.html
|
/// [`Column`]: struct.Column.html
|
||||||
|
|
|
||||||
84
core/src/widget/container.rs
Normal file
84
core/src/widget/container.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use crate::{Align, Length};
|
||||||
|
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Container<Element> {
|
||||||
|
pub width: Length,
|
||||||
|
pub height: Length,
|
||||||
|
pub max_width: u32,
|
||||||
|
pub max_height: u32,
|
||||||
|
pub horizontal_alignment: Align,
|
||||||
|
pub vertical_alignment: Align,
|
||||||
|
pub content: Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Element> Container<Element> {
|
||||||
|
/// Creates an empty [`Container`].
|
||||||
|
///
|
||||||
|
/// [`Container`]: struct.Container.html
|
||||||
|
pub fn new<T>(content: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<Element>,
|
||||||
|
{
|
||||||
|
Container {
|
||||||
|
width: Length::Shrink,
|
||||||
|
height: Length::Shrink,
|
||||||
|
max_width: u32::MAX,
|
||||||
|
max_height: u32::MAX,
|
||||||
|
horizontal_alignment: Align::Start,
|
||||||
|
vertical_alignment: Align::Start,
|
||||||
|
content: content.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the width of the [`Container`].
|
||||||
|
///
|
||||||
|
/// [`Container`]: struct.Container.html
|
||||||
|
pub fn width(mut self, width: Length) -> Self {
|
||||||
|
self.width = width;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height of the [`Container`].
|
||||||
|
///
|
||||||
|
/// [`Container`]: struct.Container.html
|
||||||
|
pub fn height(mut self, height: Length) -> Self {
|
||||||
|
self.height = height;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum width of the [`Container`].
|
||||||
|
///
|
||||||
|
/// [`Container`]: struct.Container.html
|
||||||
|
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||||
|
self.max_width = max_width;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum height of the [`Container`] in pixels.
|
||||||
|
///
|
||||||
|
/// [`Container`]: struct.Container.html
|
||||||
|
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||||
|
self.max_height = max_height;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Centers the contents in the horizontal axis of the [`Container`].
|
||||||
|
///
|
||||||
|
/// [`Container`]: struct.Container.html
|
||||||
|
pub fn center_x(mut self) -> Self {
|
||||||
|
self.horizontal_alignment = Align::Center;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Centers the contents in the vertical axis of the [`Container`].
|
||||||
|
///
|
||||||
|
/// [`Container`]: struct.Container.html
|
||||||
|
pub fn center_y(mut self) -> Self {
|
||||||
|
self.vertical_alignment = Align::Center;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Display images in your user interface.
|
//! Display images in your user interface.
|
||||||
|
|
||||||
use crate::{Align, Length, Rectangle};
|
use crate::{Length, Rectangle};
|
||||||
|
|
||||||
/// A frame that displays an image while keeping aspect ratio.
|
/// A frame that displays an image while keeping aspect ratio.
|
||||||
///
|
///
|
||||||
|
|
@ -24,8 +24,6 @@ pub struct Image {
|
||||||
|
|
||||||
/// The height of the image
|
/// The height of the image
|
||||||
pub height: Length,
|
pub height: Length,
|
||||||
|
|
||||||
pub align_self: Option<Align>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
|
|
@ -38,7 +36,6 @@ impl Image {
|
||||||
clip: None,
|
clip: None,
|
||||||
width: Length::Shrink,
|
width: Length::Shrink,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
align_self: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,15 +62,4 @@ impl Image {
|
||||||
self.height = height;
|
self.height = height;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the alignment of the [`Image`] itself.
|
|
||||||
///
|
|
||||||
/// This is useful if you want to override the default alignment given by
|
|
||||||
/// the parent container.
|
|
||||||
///
|
|
||||||
/// [`Image`]: struct.Image.html
|
|
||||||
pub fn align_self(mut self, align: Align) -> Self {
|
|
||||||
self.align_self = Some(align);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::{Align, Justify, Length};
|
use crate::{Align, Length};
|
||||||
|
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
/// A container that distributes its contents horizontally.
|
/// A container that distributes its contents horizontally.
|
||||||
///
|
///
|
||||||
|
|
@ -10,11 +12,9 @@ pub struct Row<Element> {
|
||||||
pub padding: u16,
|
pub padding: u16,
|
||||||
pub width: Length,
|
pub width: Length,
|
||||||
pub height: Length,
|
pub height: Length,
|
||||||
pub max_width: Length,
|
pub max_width: u32,
|
||||||
pub max_height: Length,
|
pub max_height: u32,
|
||||||
pub align_self: Option<Align>,
|
|
||||||
pub align_items: Align,
|
pub align_items: Align,
|
||||||
pub justify_content: Justify,
|
|
||||||
pub children: Vec<Element>,
|
pub children: Vec<Element>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,11 +28,9 @@ impl<Element> Row<Element> {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
max_width: Length::Shrink,
|
max_width: u32::MAX,
|
||||||
max_height: Length::Shrink,
|
max_height: u32::MAX,
|
||||||
align_self: None,
|
|
||||||
align_items: Align::Start,
|
align_items: Align::Start,
|
||||||
justify_content: Justify::Start,
|
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +72,7 @@ impl<Element> Row<Element> {
|
||||||
/// Sets the maximum width of the [`Row`].
|
/// Sets the maximum width of the [`Row`].
|
||||||
///
|
///
|
||||||
/// [`Row`]: struct.Row.html
|
/// [`Row`]: struct.Row.html
|
||||||
pub fn max_width(mut self, max_width: Length) -> Self {
|
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||||
self.max_width = max_width;
|
self.max_width = max_width;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -82,22 +80,11 @@ impl<Element> Row<Element> {
|
||||||
/// Sets the maximum height of the [`Row`].
|
/// Sets the maximum height of the [`Row`].
|
||||||
///
|
///
|
||||||
/// [`Row`]: struct.Row.html
|
/// [`Row`]: struct.Row.html
|
||||||
pub fn max_height(mut self, max_height: Length) -> Self {
|
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||||
self.max_height = max_height;
|
self.max_height = max_height;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the alignment of the [`Row`] itself.
|
|
||||||
///
|
|
||||||
/// This is useful if you want to override the default alignment given by
|
|
||||||
/// the parent container.
|
|
||||||
///
|
|
||||||
/// [`Row`]: struct.Row.html
|
|
||||||
pub fn align_self(mut self, align: Align) -> Self {
|
|
||||||
self.align_self = Some(align);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the vertical alignment of the contents of the [`Row`] .
|
/// Sets the vertical alignment of the contents of the [`Row`] .
|
||||||
///
|
///
|
||||||
/// [`Row`]: struct.Row.html
|
/// [`Row`]: struct.Row.html
|
||||||
|
|
@ -106,15 +93,6 @@ impl<Element> Row<Element> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the horizontal distribution strategy for the contents of the
|
|
||||||
/// [`Row`] .
|
|
||||||
///
|
|
||||||
/// [`Row`]: struct.Row.html
|
|
||||||
pub fn justify_content(mut self, justify: Justify) -> Self {
|
|
||||||
self.justify_content = justify;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds an [`Element`] to the [`Row`].
|
/// Adds an [`Element`] to the [`Row`].
|
||||||
///
|
///
|
||||||
/// [`Element`]: ../struct.Element.html
|
/// [`Element`]: ../struct.Element.html
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::{Align, Column, Length, Point, Rectangle};
|
use crate::{Align, Column, Length, Point, Rectangle};
|
||||||
|
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scrollable<'a, Element> {
|
pub struct Scrollable<'a, Element> {
|
||||||
pub state: &'a mut State,
|
pub state: &'a mut State,
|
||||||
pub height: Length,
|
pub height: Length,
|
||||||
pub max_height: Length,
|
pub max_height: u32,
|
||||||
pub align_self: Option<Align>,
|
|
||||||
pub content: Column<Element>,
|
pub content: Column<Element>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,8 +15,7 @@ impl<'a, Element> Scrollable<'a, Element> {
|
||||||
Scrollable {
|
Scrollable {
|
||||||
state,
|
state,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
max_height: Length::Shrink,
|
max_height: u32::MAX,
|
||||||
align_self: None,
|
|
||||||
content: Column::new(),
|
content: Column::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ impl<'a, Element> Scrollable<'a, Element> {
|
||||||
/// Sets the maximum width of the [`Scrollable`].
|
/// Sets the maximum width of the [`Scrollable`].
|
||||||
///
|
///
|
||||||
/// [`Scrollable`]: struct.Scrollable.html
|
/// [`Scrollable`]: struct.Scrollable.html
|
||||||
pub fn max_width(mut self, max_width: Length) -> Self {
|
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||||
self.content = self.content.max_width(max_width);
|
self.content = self.content.max_width(max_width);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -65,22 +65,11 @@ impl<'a, Element> Scrollable<'a, Element> {
|
||||||
/// Sets the maximum height of the [`Scrollable`] in pixels.
|
/// Sets the maximum height of the [`Scrollable`] in pixels.
|
||||||
///
|
///
|
||||||
/// [`Scrollable`]: struct.Scrollable.html
|
/// [`Scrollable`]: struct.Scrollable.html
|
||||||
pub fn max_height(mut self, max_height: Length) -> Self {
|
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||||
self.max_height = max_height;
|
self.max_height = max_height;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the alignment of the [`Scrollable`] itself.
|
|
||||||
///
|
|
||||||
/// This is useful if you want to override the default alignment given by
|
|
||||||
/// the parent container.
|
|
||||||
///
|
|
||||||
/// [`Scrollable`]: struct.Scrollable.html
|
|
||||||
pub fn align_self(mut self, align: Align) -> Self {
|
|
||||||
self.align_self = Some(align);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the horizontal alignment of the contents of the [`Scrollable`] .
|
/// Sets the horizontal alignment of the contents of the [`Scrollable`] .
|
||||||
///
|
///
|
||||||
/// [`Scrollable`]: struct.Scrollable.html
|
/// [`Scrollable`]: struct.Scrollable.html
|
||||||
|
|
@ -140,9 +129,9 @@ impl State {
|
||||||
|
|
||||||
pub fn offset(&self, bounds: Rectangle, content_bounds: Rectangle) -> u32 {
|
pub fn offset(&self, bounds: Rectangle, content_bounds: Rectangle) -> u32 {
|
||||||
let hidden_content =
|
let hidden_content =
|
||||||
(content_bounds.height - bounds.height).round() as u32;
|
(content_bounds.height - bounds.height).max(0.0).round() as u32;
|
||||||
|
|
||||||
self.offset.min(hidden_content).max(0)
|
self.offset.min(hidden_content)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_scrollbar_grabbed(&self) -> bool {
|
pub fn is_scrollbar_grabbed(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use iced::{
|
use iced::{
|
||||||
button, scrollable, Align, Application, Button, Column, Element, Image,
|
button, scrollable, Align, Application, Button, Container, Element, Image,
|
||||||
Justify, Length, Scrollable, Text,
|
Length, Scrollable, Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
|
@ -65,11 +65,10 @@ impl Application for Example {
|
||||||
.border_radius(5),
|
.border_radius(5),
|
||||||
);
|
);
|
||||||
|
|
||||||
Column::new()
|
Container::new(content)
|
||||||
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.justify_content(Justify::Center)
|
.center_y()
|
||||||
.padding(20)
|
|
||||||
.push(content)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use iced::{
|
use iced::{
|
||||||
scrollable, text::HorizontalAlignment, text_input, Align, Application,
|
scrollable, text::HorizontalAlignment, text_input, Application, Checkbox,
|
||||||
Checkbox, Color, Column, Element, Length, Scrollable, Text, TextInput,
|
Color, Column, Container, Element, Length, Scrollable, Text, TextInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
|
@ -77,8 +77,7 @@ impl Application for Todos {
|
||||||
);
|
);
|
||||||
|
|
||||||
let content = Column::new()
|
let content = Column::new()
|
||||||
.max_width(Length::Units(800))
|
.max_width(800)
|
||||||
.align_self(Align::Center)
|
|
||||||
.spacing(20)
|
.spacing(20)
|
||||||
.push(title)
|
.push(title)
|
||||||
.push(input)
|
.push(input)
|
||||||
|
|
@ -86,7 +85,7 @@ impl Application for Todos {
|
||||||
|
|
||||||
Scrollable::new(&mut self.scroll)
|
Scrollable::new(&mut self.scroll)
|
||||||
.padding(40)
|
.padding(40)
|
||||||
.push(content)
|
.push(Container::new(content).width(Length::Fill).center_x())
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use iced::{
|
use iced::{
|
||||||
button, scrollable, slider, text::HorizontalAlignment, text_input, Align,
|
button, scrollable, slider, text::HorizontalAlignment, text_input,
|
||||||
Application, Background, Button, Checkbox, Color, Column, Element, Image,
|
Application, Background, Button, Checkbox, Color, Column, Container,
|
||||||
Justify, Length, Radio, Row, Scrollable, Slider, Text, TextInput,
|
Element, Image, Length, Radio, Row, Scrollable, Slider, Text, TextInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
|
@ -25,7 +25,7 @@ impl Tour {
|
||||||
scroll: scrollable::State::new(),
|
scroll: scrollable::State::new(),
|
||||||
back_button: button::State::new(),
|
back_button: button::State::new(),
|
||||||
next_button: button::State::new(),
|
next_button: button::State::new(),
|
||||||
debug: false,
|
debug: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,28 +78,26 @@ impl Application for Tour {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let element: Element<_> = Column::new()
|
let content: Element<_> = Column::new()
|
||||||
.max_width(Length::Units(540))
|
.max_width(540)
|
||||||
.spacing(20)
|
.spacing(20)
|
||||||
.padding(20)
|
.padding(20)
|
||||||
.push(steps.view(self.debug).map(Message::StepMessage))
|
.push(steps.view(self.debug).map(Message::StepMessage))
|
||||||
.push(controls)
|
.push(controls)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let element = if self.debug {
|
let content = if self.debug {
|
||||||
element.explain(Color::BLACK)
|
content.explain(Color::BLACK)
|
||||||
} else {
|
} else {
|
||||||
element
|
content
|
||||||
};
|
};
|
||||||
|
|
||||||
Column::new()
|
let scrollable = Scrollable::new(scroll)
|
||||||
|
.push(Container::new(content).width(Length::Fill).center_x());
|
||||||
|
|
||||||
|
Container::new(scrollable)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.justify_content(Justify::Center)
|
.center_y()
|
||||||
.push(
|
|
||||||
Scrollable::new(scroll)
|
|
||||||
.align_items(Align::Center)
|
|
||||||
.push(element),
|
|
||||||
)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -340,10 +338,7 @@ impl<'a> Step {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn container(title: &str) -> Column<'a, StepMessage> {
|
fn container(title: &str) -> Column<'a, StepMessage> {
|
||||||
Column::new()
|
Column::new().spacing(20).push(Text::new(title).size(50))
|
||||||
.spacing(20)
|
|
||||||
.align_items(Align::Stretch)
|
|
||||||
.push(Text::new(title).size(50))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn welcome() -> Column<'a, StepMessage> {
|
fn welcome() -> Column<'a, StepMessage> {
|
||||||
|
|
@ -646,19 +641,22 @@ impl<'a> Step {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ferris(width: u16) -> Image {
|
fn ferris<'a>(width: u16) -> Container<'a, StepMessage> {
|
||||||
// This should go away once we unify resource loading on native
|
Container::new(
|
||||||
// platforms
|
// This should go away once we unify resource loading on native
|
||||||
if cfg!(target_arch = "wasm32") {
|
// platforms
|
||||||
Image::new("resources/ferris.png")
|
if cfg!(target_arch = "wasm32") {
|
||||||
} else {
|
Image::new("resources/ferris.png")
|
||||||
Image::new(format!(
|
} else {
|
||||||
"{}/examples/resources/ferris.png",
|
Image::new(format!(
|
||||||
env!("CARGO_MANIFEST_DIR")
|
"{}/examples/resources/ferris.png",
|
||||||
))
|
env!("CARGO_MANIFEST_DIR")
|
||||||
}
|
))
|
||||||
.width(Length::Units(width))
|
}
|
||||||
.align_self(Align::Center)
|
.width(Length::Units(width)),
|
||||||
|
)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.center_x()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button<'a, Message>(
|
fn button<'a, Message>(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,5 @@ repository = "https://github.com/hecrj/iced"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_core = { version = "0.1.0-alpha", path = "../core" }
|
iced_core = { version = "0.1.0-alpha", path = "../core" }
|
||||||
stretch = "0.2"
|
|
||||||
twox-hash = "1.5"
|
twox-hash = "1.5"
|
||||||
raw-window-handle = "0.3"
|
raw-window-handle = "0.3"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use stretch::{geometry, result};
|
use crate::{
|
||||||
|
layout, renderer, Color, Event, Hasher, Layout, Length, Point, Widget,
|
||||||
use crate::{renderer, Color, Event, Hasher, Layout, Node, Point, Widget};
|
};
|
||||||
|
|
||||||
/// A generic [`Widget`].
|
/// A generic [`Widget`].
|
||||||
///
|
///
|
||||||
|
|
@ -41,8 +41,20 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node(&self, renderer: &Renderer) -> Node {
|
pub fn width(&self) -> Length {
|
||||||
self.widget.node(renderer)
|
self.widget.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> Length {
|
||||||
|
self.widget.height()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn layout(
|
||||||
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
self.widget.layout(renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(
|
pub fn draw(
|
||||||
|
|
@ -116,7 +128,7 @@ where
|
||||||
/// #
|
/// #
|
||||||
/// # mod iced_wgpu {
|
/// # mod iced_wgpu {
|
||||||
/// # use iced_native::{
|
/// # use iced_native::{
|
||||||
/// # text, row, Text, Node, Point, Rectangle, Style, Layout, Row
|
/// # text, row, layout, Text, Size, Point, Rectangle, Layout, Row
|
||||||
/// # };
|
/// # };
|
||||||
/// # pub struct Renderer;
|
/// # pub struct Renderer;
|
||||||
/// #
|
/// #
|
||||||
|
|
@ -132,8 +144,12 @@ where
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
/// # impl text::Renderer for Renderer {
|
/// # impl text::Renderer for Renderer {
|
||||||
/// # fn node(&self, _text: &Text) -> Node {
|
/// # fn layout(
|
||||||
/// # Node::new(Style::default())
|
/// # &self,
|
||||||
|
/// # _text: &Text,
|
||||||
|
/// # _limits: &layout::Limits,
|
||||||
|
/// # ) -> layout::Node {
|
||||||
|
/// # layout::Node::new(Size::ZERO)
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
/// # fn draw(
|
/// # fn draw(
|
||||||
|
|
@ -247,12 +263,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_layout(&self, renderer: &Renderer) -> result::Layout {
|
|
||||||
let node = self.widget.node(renderer);
|
|
||||||
|
|
||||||
node.0.compute_layout(geometry::Size::undefined()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn hash_layout(&self, state: &mut Hasher) {
|
pub(crate) fn hash_layout(&self, state: &mut Hasher) {
|
||||||
self.widget.hash_layout(state);
|
self.widget.hash_layout(state);
|
||||||
}
|
}
|
||||||
|
|
@ -289,8 +299,12 @@ where
|
||||||
A: Clone,
|
A: Clone,
|
||||||
Renderer: crate::Renderer,
|
Renderer: crate::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn layout(
|
||||||
self.widget.node(renderer)
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
self.widget.layout(renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -361,8 +375,12 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: crate::Renderer + renderer::Debugger,
|
Renderer: crate::Renderer + renderer::Debugger,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn layout(
|
||||||
self.element.widget.node(renderer)
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
self.element.widget.layout(renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,50 @@
|
||||||
use stretch::result;
|
mod limits;
|
||||||
|
mod node;
|
||||||
|
|
||||||
|
pub mod flex;
|
||||||
|
|
||||||
|
pub use limits::Limits;
|
||||||
|
pub use node::Node;
|
||||||
|
|
||||||
use crate::{Point, Rectangle, Vector};
|
use crate::{Point, Rectangle, Vector};
|
||||||
|
|
||||||
/// The computed bounds of a [`Node`] and its children.
|
|
||||||
///
|
|
||||||
/// This type is provided by the GUI runtime to [`Widget::on_event`] and
|
|
||||||
/// [`Widget::draw`], describing the layout of the [`Node`] produced by
|
|
||||||
/// [`Widget::node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
/// [`Widget::on_event`]: widget/trait.Widget.html#method.on_event
|
|
||||||
/// [`Widget::draw`]: widget/trait.Widget.html#tymethod.draw
|
|
||||||
/// [`Widget::node`]: widget/trait.Widget.html#tymethod.node
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Layout<'a> {
|
pub struct Layout<'a> {
|
||||||
layout: &'a result::Layout,
|
|
||||||
position: Point,
|
position: Point,
|
||||||
|
node: &'a Node,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Layout<'a> {
|
impl<'a> Layout<'a> {
|
||||||
pub(crate) fn new(layout: &'a result::Layout) -> Self {
|
pub(crate) fn new(node: &'a Node) -> Self {
|
||||||
Self::with_parent_position(layout, Point::new(0.0, 0.0))
|
Self::with_offset(Vector::new(0.0, 0.0), node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_parent_position(
|
pub(crate) fn with_offset(offset: Vector, node: &'a Node) -> Self {
|
||||||
layout: &'a result::Layout,
|
let bounds = node.bounds();
|
||||||
parent_position: Point,
|
|
||||||
) -> Self {
|
|
||||||
let position =
|
|
||||||
parent_position + Vector::new(layout.location.x, layout.location.y);
|
|
||||||
|
|
||||||
Layout { layout, position }
|
Self {
|
||||||
}
|
position: Point::new(bounds.x, bounds.y) + offset,
|
||||||
|
node,
|
||||||
/// Gets the bounds of the [`Layout`].
|
}
|
||||||
///
|
}
|
||||||
/// The returned [`Rectangle`] describes the position and size of a
|
|
||||||
/// [`Node`].
|
pub fn bounds(&self) -> Rectangle {
|
||||||
///
|
let bounds = self.node.bounds();
|
||||||
/// [`Layout`]: struct.Layout.html
|
|
||||||
/// [`Rectangle`]: struct.Rectangle.html
|
Rectangle {
|
||||||
/// [`Node`]: struct.Node.html
|
x: self.position.x,
|
||||||
pub fn bounds(&self) -> Rectangle {
|
y: self.position.y,
|
||||||
Rectangle {
|
width: bounds.width,
|
||||||
x: self.position.x,
|
height: bounds.height,
|
||||||
y: self.position.y,
|
|
||||||
width: self.layout.size.width,
|
|
||||||
height: self.layout.size.height,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the [`Layout`] of the children of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Layout`]: struct.Layout.html
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn children(&'a self) -> impl Iterator<Item = Layout<'a>> {
|
pub fn children(&'a self) -> impl Iterator<Item = Layout<'a>> {
|
||||||
self.layout.children.iter().map(move |layout| {
|
self.node.children().iter().map(move |node| {
|
||||||
Layout::with_parent_position(layout, self.position)
|
Layout::with_offset(
|
||||||
|
Vector::new(self.position.x, self.position.y),
|
||||||
|
node,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
202
native/src/layout/DRUID_LICENSE
Normal file
202
native/src/layout/DRUID_LICENSE
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
168
native/src/layout/flex.rs
Normal file
168
native/src/layout/flex.rs
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
// This code is heavily inspired by the [`druid`] codebase.
|
||||||
|
//
|
||||||
|
// [`druid`]: https://github.com/xi-editor/druid
|
||||||
|
//
|
||||||
|
// Copyright 2018 The xi-editor Authors, Héctor Ramón
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
use crate::{
|
||||||
|
layout::{Limits, Node},
|
||||||
|
Align, Element, Size,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Axis {
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Axis {
|
||||||
|
fn main(&self, size: Size) -> f32 {
|
||||||
|
match self {
|
||||||
|
Axis::Horizontal => size.width,
|
||||||
|
Axis::Vertical => size.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cross(&self, size: Size) -> f32 {
|
||||||
|
match self {
|
||||||
|
Axis::Horizontal => size.height,
|
||||||
|
Axis::Vertical => size.width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pack(&self, main: f32, cross: f32) -> (f32, f32) {
|
||||||
|
match self {
|
||||||
|
Axis::Horizontal => (main, cross),
|
||||||
|
Axis::Vertical => (cross, main),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove `Message` type parameter
|
||||||
|
pub fn resolve<Message, Renderer>(
|
||||||
|
axis: Axis,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &Limits,
|
||||||
|
padding: f32,
|
||||||
|
spacing: f32,
|
||||||
|
align_items: Align,
|
||||||
|
children: &[Element<'_, Message, Renderer>],
|
||||||
|
) -> Node
|
||||||
|
where
|
||||||
|
Renderer: crate::Renderer,
|
||||||
|
{
|
||||||
|
let limits = limits.pad(padding);
|
||||||
|
|
||||||
|
let mut total_non_fill =
|
||||||
|
spacing as f32 * (children.len() as i32 - 1).max(0) as f32;
|
||||||
|
let mut fill_sum = 0;
|
||||||
|
let mut cross = axis.cross(limits.min());
|
||||||
|
|
||||||
|
let mut nodes: Vec<Node> = Vec::with_capacity(children.len());
|
||||||
|
nodes.resize(children.len(), Node::default());
|
||||||
|
|
||||||
|
for (i, child) in children.iter().enumerate() {
|
||||||
|
let fill_factor = match axis {
|
||||||
|
Axis::Horizontal => child.width(),
|
||||||
|
Axis::Vertical => child.height(),
|
||||||
|
}
|
||||||
|
.fill_factor();
|
||||||
|
|
||||||
|
if fill_factor == 0 {
|
||||||
|
let child_limits = Limits::new(Size::ZERO, limits.max());
|
||||||
|
|
||||||
|
let layout = child.layout(renderer, &child_limits);
|
||||||
|
let size = layout.size();
|
||||||
|
|
||||||
|
total_non_fill += axis.main(size);
|
||||||
|
cross = cross.max(axis.cross(size));
|
||||||
|
|
||||||
|
nodes[i] = layout;
|
||||||
|
} else {
|
||||||
|
fill_sum += fill_factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let available = axis.main(limits.max());
|
||||||
|
let remaining = (available - total_non_fill).max(0.0);
|
||||||
|
|
||||||
|
for (i, child) in children.iter().enumerate() {
|
||||||
|
let fill_factor = match axis {
|
||||||
|
Axis::Horizontal => child.width(),
|
||||||
|
Axis::Vertical => child.height(),
|
||||||
|
}
|
||||||
|
.fill_factor();
|
||||||
|
|
||||||
|
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_main, min_cross) =
|
||||||
|
axis.pack(min_main, axis.cross(limits.min()));
|
||||||
|
|
||||||
|
let (max_main, max_cross) =
|
||||||
|
axis.pack(max_main, axis.cross(limits.max()));
|
||||||
|
|
||||||
|
let child_limits = Limits::new(
|
||||||
|
Size::new(min_main, min_cross),
|
||||||
|
Size::new(max_main, max_cross),
|
||||||
|
);
|
||||||
|
|
||||||
|
let layout = child.layout(renderer, &child_limits);
|
||||||
|
cross = cross.max(axis.cross(layout.size()));
|
||||||
|
|
||||||
|
nodes[i] = layout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut main = padding;
|
||||||
|
|
||||||
|
for (i, node) in nodes.iter_mut().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
main += spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (x, y) = axis.pack(main, padding);
|
||||||
|
|
||||||
|
node.bounds.x = x;
|
||||||
|
node.bounds.y = y;
|
||||||
|
|
||||||
|
match axis {
|
||||||
|
Axis::Horizontal => {
|
||||||
|
node.align(Align::Start, align_items, Size::new(0.0, cross));
|
||||||
|
}
|
||||||
|
Axis::Vertical => {
|
||||||
|
node.align(align_items, Align::Start, Size::new(cross, 0.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = node.size();
|
||||||
|
|
||||||
|
main += axis.main(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (width, height) = axis.pack(main, cross);
|
||||||
|
let size = limits.resolve(Size::new(width, height));
|
||||||
|
|
||||||
|
let (padding_x, padding_y) = axis.pack(padding, padding * 2.0);
|
||||||
|
|
||||||
|
Node::with_children(
|
||||||
|
Size::new(size.width + padding_x, size.height + padding_y),
|
||||||
|
nodes,
|
||||||
|
)
|
||||||
|
}
|
||||||
139
native/src/layout/limits.rs
Normal file
139
native/src/layout/limits.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
use crate::{Length, Size};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Limits {
|
||||||
|
min: Size,
|
||||||
|
max: Size,
|
||||||
|
fill: Size,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Limits {
|
||||||
|
pub const NONE: Limits = Limits {
|
||||||
|
min: Size::ZERO,
|
||||||
|
max: Size::INFINITY,
|
||||||
|
fill: Size::INFINITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn new(min: Size, max: Size) -> Limits {
|
||||||
|
Limits {
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
fill: Size::INFINITY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min(&self) -> Size {
|
||||||
|
self.min
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max(&self) -> Size {
|
||||||
|
self.max
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(mut self, width: Length) -> Limits {
|
||||||
|
match width {
|
||||||
|
Length::Shrink => {
|
||||||
|
self.fill.width = self.min.width;
|
||||||
|
}
|
||||||
|
Length::Fill => {
|
||||||
|
self.fill.width = self.fill.width.min(self.max.width);
|
||||||
|
}
|
||||||
|
Length::Units(units) => {
|
||||||
|
let new_width =
|
||||||
|
(units as f32).min(self.max.width).max(self.min.width);
|
||||||
|
|
||||||
|
self.min.width = new_width;
|
||||||
|
self.max.width = new_width;
|
||||||
|
self.fill.width = new_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(mut self, height: Length) -> Limits {
|
||||||
|
match height {
|
||||||
|
Length::Shrink => {
|
||||||
|
self.fill.height = self.min.height;
|
||||||
|
}
|
||||||
|
Length::Fill => {
|
||||||
|
self.fill.height = self.fill.height.min(self.max.height);
|
||||||
|
}
|
||||||
|
Length::Units(units) => {
|
||||||
|
let new_height =
|
||||||
|
(units as f32).min(self.max.height).max(self.min.height);
|
||||||
|
|
||||||
|
self.min.height = new_height;
|
||||||
|
self.max.height = new_height;
|
||||||
|
self.fill.height = new_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min_width(mut self, min_width: u32) -> Limits {
|
||||||
|
self.min.width =
|
||||||
|
self.min.width.max(min_width as f32).min(self.max.width);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_width(mut self, max_width: u32) -> Limits {
|
||||||
|
self.max.width =
|
||||||
|
self.max.width.min(max_width as f32).max(self.min.width);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_height(mut self, max_height: u32) -> Limits {
|
||||||
|
self.max.height =
|
||||||
|
self.max.height.min(max_height as f32).max(self.min.height);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pad(&self, padding: f32) -> Limits {
|
||||||
|
self.shrink(Size::new(padding * 2.0, padding * 2.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrink(&self, size: Size) -> Limits {
|
||||||
|
let min = Size::new(
|
||||||
|
(self.min().width - size.width).max(0.0),
|
||||||
|
(self.min().height - size.height).max(0.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let max = Size::new(
|
||||||
|
(self.max().width - size.width).max(0.0),
|
||||||
|
(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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loose(&self) -> Limits {
|
||||||
|
Limits {
|
||||||
|
min: Size::ZERO,
|
||||||
|
max: self.max,
|
||||||
|
fill: self.fill,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(&self, intrinsic_size: Size) -> Size {
|
||||||
|
Size::new(
|
||||||
|
intrinsic_size
|
||||||
|
.width
|
||||||
|
.min(self.max.width)
|
||||||
|
.max(self.fill.width),
|
||||||
|
intrinsic_size
|
||||||
|
.height
|
||||||
|
.min(self.max.height)
|
||||||
|
.max(self.fill.height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
64
native/src/layout/node.rs
Normal file
64
native/src/layout/node.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
use crate::{Align, Rectangle, Size};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Node {
|
||||||
|
pub bounds: Rectangle,
|
||||||
|
children: Vec<Node>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
pub fn new(size: Size) -> Self {
|
||||||
|
Self::with_children(size, Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_children(size: Size, children: Vec<Node>) -> Self {
|
||||||
|
Node {
|
||||||
|
bounds: Rectangle {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
},
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> Size {
|
||||||
|
Size::new(self.bounds.width, self.bounds.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bounds(&self) -> Rectangle {
|
||||||
|
self.bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children(&self) -> &[Node] {
|
||||||
|
&self.children
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align(
|
||||||
|
&mut self,
|
||||||
|
horizontal_alignment: Align,
|
||||||
|
vertical_alignment: Align,
|
||||||
|
space: Size,
|
||||||
|
) {
|
||||||
|
match horizontal_alignment {
|
||||||
|
Align::Start => {}
|
||||||
|
Align::Center => {
|
||||||
|
self.bounds.x += (space.width - self.bounds.width) / 2.0;
|
||||||
|
}
|
||||||
|
Align::End => {
|
||||||
|
self.bounds.x += space.width - self.bounds.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match vertical_alignment {
|
||||||
|
Align::Start => {}
|
||||||
|
Align::Center => {
|
||||||
|
self.bounds.y += (space.height - self.bounds.height) / 2.0;
|
||||||
|
}
|
||||||
|
Align::End => {
|
||||||
|
self.bounds.y += space.height - self.bounds.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -77,7 +77,7 @@
|
||||||
//! #
|
//! #
|
||||||
//! # mod iced_wgpu {
|
//! # mod iced_wgpu {
|
||||||
//! # use iced_native::{
|
//! # use iced_native::{
|
||||||
//! # button, text, Button, Text, Node, Point, Rectangle, Style, Color, Layout
|
//! # button, text, layout, Button, Text, Point, Rectangle, Color, Layout, Size
|
||||||
//! # };
|
//! # };
|
||||||
//! #
|
//! #
|
||||||
//! # pub struct Renderer {}
|
//! # pub struct Renderer {}
|
||||||
|
|
@ -87,11 +87,12 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! #
|
//! #
|
||||||
//! # impl button::Renderer for Renderer {
|
//! # impl button::Renderer for Renderer {
|
||||||
//! # fn node<Message>(
|
//! # fn layout<Message>(
|
||||||
//! # &self,
|
//! # &self,
|
||||||
//! # _button: &Button<'_, Message, Self>
|
//! # _button: &Button<'_, Message, Self>,
|
||||||
//! # ) -> Node {
|
//! # _limits: &layout::Limits,
|
||||||
//! # Node::new(Style::default())
|
//! # ) -> layout::Node {
|
||||||
|
//! # layout::Node::new(Size::ZERO)
|
||||||
//! # }
|
//! # }
|
||||||
//! #
|
//! #
|
||||||
//! # fn draw<Message>(
|
//! # fn draw<Message>(
|
||||||
|
|
@ -103,8 +104,12 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! #
|
//! #
|
||||||
//! # impl text::Renderer for Renderer {
|
//! # impl text::Renderer for Renderer {
|
||||||
//! # fn node(&self, _text: &Text) -> Node {
|
//! # fn layout(
|
||||||
//! # Node::new(Style::default())
|
//! # &self,
|
||||||
|
//! # _text: &Text,
|
||||||
|
//! # _limits: &layout::Limits,
|
||||||
|
//! # ) -> layout::Node {
|
||||||
|
//! # layout::Node::new(Size::ZERO)
|
||||||
//! # }
|
//! # }
|
||||||
//! #
|
//! #
|
||||||
//! # fn draw(
|
//! # fn draw(
|
||||||
|
|
@ -199,32 +204,27 @@
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![deny(rust_2018_idioms)]
|
#![deny(rust_2018_idioms)]
|
||||||
pub mod input;
|
pub mod input;
|
||||||
|
pub mod layout;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
|
|
||||||
mod element;
|
mod element;
|
||||||
mod event;
|
mod event;
|
||||||
mod hasher;
|
mod hasher;
|
||||||
mod layout;
|
|
||||||
mod mouse_cursor;
|
mod mouse_cursor;
|
||||||
mod node;
|
mod size;
|
||||||
mod style;
|
|
||||||
mod user_interface;
|
mod user_interface;
|
||||||
|
|
||||||
pub use iced_core::{
|
pub use iced_core::{
|
||||||
Align, Background, Color, Justify, Length, Point, Rectangle, Vector,
|
Align, Background, Color, Length, Point, Rectangle, Vector,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use stretch::{geometry::Size, number::Number};
|
|
||||||
|
|
||||||
pub use element::Element;
|
pub use element::Element;
|
||||||
pub use event::Event;
|
pub use event::Event;
|
||||||
pub use hasher::Hasher;
|
pub use hasher::Hasher;
|
||||||
pub use layout::Layout;
|
pub use layout::Layout;
|
||||||
pub use mouse_cursor::MouseCursor;
|
pub use mouse_cursor::MouseCursor;
|
||||||
pub use node::Node;
|
|
||||||
pub use renderer::Renderer;
|
pub use renderer::Renderer;
|
||||||
pub use style::Style;
|
pub use size::Size;
|
||||||
pub use user_interface::{Cache, UserInterface};
|
pub use user_interface::{Cache, UserInterface};
|
||||||
pub use widget::*;
|
pub use widget::*;
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
use stretch::node;
|
|
||||||
|
|
||||||
use crate::{Number, Size, Style};
|
|
||||||
|
|
||||||
/// The visual requirements of a [`Widget`] and its children.
|
|
||||||
///
|
|
||||||
/// When there have been changes and the [`Layout`] needs to be recomputed, the
|
|
||||||
/// runtime obtains a [`Node`] by calling [`Widget::node`].
|
|
||||||
///
|
|
||||||
/// [`Style`]: struct.Style.html
|
|
||||||
/// [`Widget`]: widget/trait.Widget.html
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
/// [`Widget::node`]: widget/trait.Widget.html#tymethod.node
|
|
||||||
/// [`Layout`]: struct.Layout.html
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Node(pub(crate) node::Node);
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
/// Creates a new [`Node`] with the given [`Style`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
/// [`Style`]: struct.Style.html
|
|
||||||
pub fn new(style: Style) -> Node {
|
|
||||||
Self::with_children(style, Vec::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`Node`] with the given [`Style`] and a measure function.
|
|
||||||
///
|
|
||||||
/// This type of node cannot have any children.
|
|
||||||
///
|
|
||||||
/// You should use this when your [`Widget`] can adapt its contents to the
|
|
||||||
/// size of its container. The measure function will receive the container
|
|
||||||
/// size as a parameter and must compute the size of the [`Node`] inside
|
|
||||||
/// the given bounds (if the `Number` for a dimension is `Undefined` it
|
|
||||||
/// means that it has no boundary).
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
/// [`Style`]: struct.Style.html
|
|
||||||
/// [`Widget`]: widget/trait.Widget.html
|
|
||||||
pub fn with_measure<F>(style: Style, measure: F) -> Node
|
|
||||||
where
|
|
||||||
F: 'static + Fn(Size<Number>) -> Size<f32>,
|
|
||||||
{
|
|
||||||
Node(node::Node::new_leaf(
|
|
||||||
style.0,
|
|
||||||
Box::new(move |size| Ok(measure(size))),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`Node`] with the given [`Style`] and children.
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
/// [`Style`]: struct.Style.html
|
|
||||||
pub fn with_children(style: Style, children: Vec<Node>) -> Node {
|
|
||||||
Node(node::Node::new(
|
|
||||||
style.0,
|
|
||||||
children.iter().map(|c| &c.0).collect(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -26,6 +26,15 @@ mod windowed;
|
||||||
pub use debugger::Debugger;
|
pub use debugger::Debugger;
|
||||||
pub use windowed::{Target, Windowed};
|
pub use windowed::{Target, Windowed};
|
||||||
|
|
||||||
pub trait Renderer {
|
use crate::{layout, Element};
|
||||||
|
|
||||||
|
pub trait Renderer: Sized {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
|
fn layout<'a, Message>(
|
||||||
|
&mut self,
|
||||||
|
element: &Element<'a, Message, Self>,
|
||||||
|
) -> layout::Node {
|
||||||
|
element.layout(self, &layout::Limits::NONE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
native/src/size.rs
Normal file
25
native/src/size.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use std::f32;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Size {
|
||||||
|
/// The width.
|
||||||
|
pub width: f32,
|
||||||
|
/// The height.
|
||||||
|
pub height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Size {
|
||||||
|
pub const ZERO: Size = Size::new(0., 0.);
|
||||||
|
pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY);
|
||||||
|
|
||||||
|
pub const fn new(width: f32, height: f32) -> Self {
|
||||||
|
Size { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pad(&self, padding: f32) -> Self {
|
||||||
|
Size {
|
||||||
|
width: self.width + padding * 2.0,
|
||||||
|
height: self.height + padding * 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
use crate::{Align, Justify, Length};
|
|
||||||
|
|
||||||
use stretch::style;
|
|
||||||
|
|
||||||
/// The appearance of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct Style(pub(crate) style::Style);
|
|
||||||
|
|
||||||
impl Default for Style {
|
|
||||||
fn default() -> Style {
|
|
||||||
Style::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Style {
|
|
||||||
/// Creates a new [`Style`].
|
|
||||||
///
|
|
||||||
/// [`Style`]: struct.Style.html
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Style(style::Style {
|
|
||||||
align_items: style::AlignItems::FlexStart,
|
|
||||||
justify_content: style::JustifyContent::FlexStart,
|
|
||||||
..style::Style::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the width of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn width(mut self, width: Length) -> Self {
|
|
||||||
self.0.size.width = into_dimension(width);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the height of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn height(mut self, height: Length) -> Self {
|
|
||||||
self.0.size.height = into_dimension(height);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the minimum width of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn min_width(mut self, min_width: Length) -> Self {
|
|
||||||
self.0.min_size.width = into_dimension(min_width);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the maximum width of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn max_width(mut self, max_width: Length) -> Self {
|
|
||||||
self.0.max_size.width = into_dimension(max_width);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the minimum height of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn min_height(mut self, min_height: Length) -> Self {
|
|
||||||
self.0.min_size.height = into_dimension(min_height);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the maximum height of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn max_height(mut self, max_height: Length) -> Self {
|
|
||||||
self.0.max_size.height = into_dimension(max_height);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn align_items(mut self, align: Align) -> Self {
|
|
||||||
self.0.align_items = into_align_items(align);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn justify_content(mut self, justify: Justify) -> Self {
|
|
||||||
self.0.justify_content = into_justify_content(justify);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the alignment of a [`Node`].
|
|
||||||
///
|
|
||||||
/// If the [`Node`] is inside a...
|
|
||||||
///
|
|
||||||
/// * [`Column`], this setting will affect its __horizontal__ alignment.
|
|
||||||
/// * [`Row`], this setting will affect its __vertical__ alignment.
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
/// [`Column`]: widget/struct.Column.html
|
|
||||||
/// [`Row`]: widget/struct.Row.html
|
|
||||||
pub fn align_self(mut self, align: Option<Align>) -> Self {
|
|
||||||
self.0.align_self = match align {
|
|
||||||
Some(align) => into_align_self(align),
|
|
||||||
None => stretch::style::AlignSelf::Auto,
|
|
||||||
};
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the padding of a [`Node`].
|
|
||||||
///
|
|
||||||
/// [`Node`]: struct.Node.html
|
|
||||||
pub fn padding(mut self, units: u16) -> Self {
|
|
||||||
self.0.padding = stretch::geometry::Rect {
|
|
||||||
start: style::Dimension::Points(units as f32),
|
|
||||||
end: style::Dimension::Points(units as f32),
|
|
||||||
top: style::Dimension::Points(units as f32),
|
|
||||||
bottom: style::Dimension::Points(units as f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_dimension(length: Length) -> style::Dimension {
|
|
||||||
match length {
|
|
||||||
Length::Shrink => style::Dimension::Undefined,
|
|
||||||
Length::Fill => style::Dimension::Percent(1.0),
|
|
||||||
Length::Units(units) => style::Dimension::Points(units as f32),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_align_items(align: Align) -> style::AlignItems {
|
|
||||||
match align {
|
|
||||||
Align::Start => style::AlignItems::FlexStart,
|
|
||||||
Align::Center => style::AlignItems::Center,
|
|
||||||
Align::End => style::AlignItems::FlexEnd,
|
|
||||||
Align::Stretch => style::AlignItems::Stretch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_align_self(align: Align) -> style::AlignSelf {
|
|
||||||
match align {
|
|
||||||
Align::Start => style::AlignSelf::FlexStart,
|
|
||||||
Align::Center => style::AlignSelf::Center,
|
|
||||||
Align::End => style::AlignSelf::FlexEnd,
|
|
||||||
Align::Stretch => style::AlignSelf::Stretch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_justify_content(justify: Justify) -> style::JustifyContent {
|
|
||||||
match justify {
|
|
||||||
Justify::Start => style::JustifyContent::FlexStart,
|
|
||||||
Justify::Center => style::JustifyContent::Center,
|
|
||||||
Justify::End => style::JustifyContent::FlexEnd,
|
|
||||||
Justify::SpaceBetween => style::JustifyContent::SpaceBetween,
|
|
||||||
Justify::SpaceAround => style::JustifyContent::SpaceAround,
|
|
||||||
Justify::SpaceEvenly => style::JustifyContent::SpaceEvenly,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::{input::mouse, Element, Event, Layout, Point};
|
use crate::{input::mouse, layout, Element, Event, Layout, Point, Size};
|
||||||
|
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
use stretch::{geometry, result};
|
|
||||||
|
|
||||||
/// A set of interactive graphical elements with a specific [`Layout`].
|
/// A set of interactive graphical elements with a specific [`Layout`].
|
||||||
///
|
///
|
||||||
|
|
@ -15,7 +14,7 @@ use stretch::{geometry, result};
|
||||||
pub struct UserInterface<'a, Message, Renderer> {
|
pub struct UserInterface<'a, Message, Renderer> {
|
||||||
hash: u64,
|
hash: u64,
|
||||||
root: Element<'a, Message, Renderer>,
|
root: Element<'a, Message, Renderer>,
|
||||||
layout: result::Layout,
|
layout: layout::Node,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,7 +97,7 @@ where
|
||||||
pub fn build<E: Into<Element<'a, Message, Renderer>>>(
|
pub fn build<E: Into<Element<'a, Message, Renderer>>>(
|
||||||
root: E,
|
root: E,
|
||||||
cache: Cache,
|
cache: Cache,
|
||||||
renderer: &Renderer,
|
renderer: &mut Renderer,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let root = root.into();
|
let root = root.into();
|
||||||
|
|
||||||
|
|
@ -110,7 +109,11 @@ where
|
||||||
let layout = if hash == cache.hash {
|
let layout = if hash == cache.hash {
|
||||||
cache.layout
|
cache.layout
|
||||||
} else {
|
} else {
|
||||||
root.compute_layout(renderer)
|
let layout_start = std::time::Instant::now();
|
||||||
|
let layout = renderer.layout(&root);
|
||||||
|
dbg!(std::time::Instant::now() - layout_start);
|
||||||
|
|
||||||
|
layout
|
||||||
};
|
};
|
||||||
|
|
||||||
UserInterface {
|
UserInterface {
|
||||||
|
|
@ -326,7 +329,7 @@ where
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
hash: u64,
|
hash: u64,
|
||||||
layout: result::Layout,
|
layout: layout::Node,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,16 +342,9 @@ impl Cache {
|
||||||
/// [`Cache`]: struct.Cache.html
|
/// [`Cache`]: struct.Cache.html
|
||||||
/// [`UserInterface`]: struct.UserInterface.html
|
/// [`UserInterface`]: struct.UserInterface.html
|
||||||
pub fn new() -> Cache {
|
pub fn new() -> Cache {
|
||||||
use crate::{Node, Style};
|
|
||||||
|
|
||||||
let empty_node = Node::new(Style::default());
|
|
||||||
|
|
||||||
Cache {
|
Cache {
|
||||||
hash: 0,
|
hash: 0,
|
||||||
layout: empty_node
|
layout: layout::Node::new(Size::new(0.0, 0.0)),
|
||||||
.0
|
|
||||||
.compute_layout(geometry::Size::undefined())
|
|
||||||
.unwrap(),
|
|
||||||
cursor_position: Point::new(-1.0, -1.0),
|
cursor_position: Point::new(-1.0, -1.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ pub mod slider;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod text_input;
|
pub mod text_input;
|
||||||
|
|
||||||
|
mod container;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use button::Button;
|
pub use button::Button;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
|
@ -38,6 +40,8 @@ pub use checkbox::Checkbox;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use column::Column;
|
pub use column::Column;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
pub use container::Container;
|
||||||
|
#[doc(no_inline)]
|
||||||
pub use image::Image;
|
pub use image::Image;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use radio::Radio;
|
pub use radio::Radio;
|
||||||
|
|
@ -52,7 +56,7 @@ pub use text::Text;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use text_input::TextInput;
|
pub use text_input::TextInput;
|
||||||
|
|
||||||
use crate::{Event, Hasher, Layout, Node, Point};
|
use crate::{layout, Event, Hasher, Layout, Length, Point};
|
||||||
|
|
||||||
/// A component that displays information and allows interaction.
|
/// A component that displays information and allows interaction.
|
||||||
///
|
///
|
||||||
|
|
@ -73,7 +77,19 @@ where
|
||||||
/// [`Node`]: ../struct.Node.html
|
/// [`Node`]: ../struct.Node.html
|
||||||
/// [`Widget`]: trait.Widget.html
|
/// [`Widget`]: trait.Widget.html
|
||||||
/// [`Layout`]: ../struct.Layout.html
|
/// [`Layout`]: ../struct.Layout.html
|
||||||
fn node(&self, renderer: &Renderer) -> Node;
|
fn layout(
|
||||||
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node;
|
||||||
|
|
||||||
|
fn width(&self) -> Length {
|
||||||
|
Length::Shrink
|
||||||
|
}
|
||||||
|
|
||||||
|
fn height(&self) -> Length {
|
||||||
|
Length::Shrink
|
||||||
|
}
|
||||||
|
|
||||||
/// Draws the [`Widget`] using the associated `Renderer`.
|
/// Draws the [`Widget`] using the associated `Renderer`.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
//! [`Class`]: enum.Class.html
|
//! [`Class`]: enum.Class.html
|
||||||
|
|
||||||
use crate::input::{mouse, ButtonState};
|
use crate::input::{mouse, ButtonState};
|
||||||
use crate::{Element, Event, Hasher, Layout, Node, Point, Widget};
|
use crate::{layout, Element, Event, Hasher, Layout, Point, Widget};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
pub use iced_core::button::State;
|
pub use iced_core::button::State;
|
||||||
|
|
@ -21,8 +21,12 @@ where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
Message: Clone + std::fmt::Debug,
|
Message: Clone + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn layout(
|
||||||
renderer.node(&self)
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
renderer.layout(&self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -74,7 +78,6 @@ where
|
||||||
|
|
||||||
fn hash_layout(&self, state: &mut Hasher) {
|
fn hash_layout(&self, state: &mut Hasher) {
|
||||||
self.width.hash(state);
|
self.width.hash(state);
|
||||||
self.align_self.hash(state);
|
|
||||||
self.content.hash_layout(state);
|
self.content.hash_layout(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +94,11 @@ pub trait Renderer: crate::Renderer + Sized {
|
||||||
///
|
///
|
||||||
/// [`Node`]: ../../struct.Node.html
|
/// [`Node`]: ../../struct.Node.html
|
||||||
/// [`Button`]: struct.Button.html
|
/// [`Button`]: struct.Button.html
|
||||||
fn node<Message>(&self, button: &Button<'_, Message, Self>) -> Node;
|
fn layout<Message>(
|
||||||
|
&self,
|
||||||
|
button: &Button<'_, Message, Self>,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node;
|
||||||
|
|
||||||
/// Draws a [`Button`].
|
/// Draws a [`Button`].
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::input::{mouse, ButtonState};
|
use crate::input::{mouse, ButtonState};
|
||||||
use crate::{Element, Event, Hasher, Layout, Node, Point, Widget};
|
use crate::{layout, Element, Event, Hasher, Layout, Point, Widget};
|
||||||
|
|
||||||
pub use iced_core::Checkbox;
|
pub use iced_core::Checkbox;
|
||||||
|
|
||||||
|
|
@ -10,8 +10,12 @@ impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>
|
||||||
where
|
where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn layout(
|
||||||
renderer.node(&self)
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
renderer.layout(&self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -63,7 +67,11 @@ pub trait Renderer: crate::Renderer {
|
||||||
///
|
///
|
||||||
/// [`Node`]: ../../struct.Node.html
|
/// [`Node`]: ../../struct.Node.html
|
||||||
/// [`Checkbox`]: struct.Checkbox.html
|
/// [`Checkbox`]: struct.Checkbox.html
|
||||||
fn node<Message>(&self, checkbox: &Checkbox<Message>) -> Node;
|
fn layout<Message>(
|
||||||
|
&self,
|
||||||
|
checkbox: &Checkbox<Message>,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node;
|
||||||
|
|
||||||
/// Draws a [`Checkbox`].
|
/// Draws a [`Checkbox`].
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{Element, Event, Hasher, Layout, Node, Point, Style, Widget};
|
use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
|
||||||
|
|
||||||
/// A container that distributes its contents vertically.
|
/// A container that distributes its contents vertically.
|
||||||
pub type Column<'a, Message, Renderer> =
|
pub type Column<'a, Message, Renderer> =
|
||||||
|
|
@ -11,42 +11,30 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn width(&self) -> Length {
|
||||||
let mut children: Vec<Node> = self
|
self.width
|
||||||
.children
|
}
|
||||||
.iter()
|
|
||||||
.map(|child| {
|
|
||||||
let mut node = child.widget.node(renderer);
|
|
||||||
|
|
||||||
let mut style = node.0.style();
|
fn layout(
|
||||||
style.margin.bottom =
|
&self,
|
||||||
stretch::style::Dimension::Points(f32::from(self.spacing));
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
node.0.set_style(style);
|
) -> layout::Node {
|
||||||
node
|
let limits = limits
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Some(node) = children.last_mut() {
|
|
||||||
let mut style = node.0.style();
|
|
||||||
style.margin.bottom = stretch::style::Dimension::Undefined;
|
|
||||||
|
|
||||||
node.0.set_style(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut style = Style::default()
|
|
||||||
.width(self.width)
|
|
||||||
.height(self.height)
|
|
||||||
.max_width(self.max_width)
|
.max_width(self.max_width)
|
||||||
.max_height(self.max_height)
|
.max_height(self.max_height)
|
||||||
.padding(self.padding)
|
.width(self.width)
|
||||||
.align_self(self.align_self)
|
.height(self.height);
|
||||||
.align_items(self.align_items)
|
|
||||||
.justify_content(self.justify_content);
|
|
||||||
|
|
||||||
style.0.flex_direction = stretch::style::FlexDirection::Column;
|
layout::flex::resolve(
|
||||||
|
layout::flex::Axis::Vertical,
|
||||||
Node::with_children(style, children)
|
renderer,
|
||||||
|
&limits,
|
||||||
|
self.padding as f32,
|
||||||
|
self.spacing as f32,
|
||||||
|
self.align_items,
|
||||||
|
&self.children,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -85,9 +73,7 @@ where
|
||||||
self.height.hash(state);
|
self.height.hash(state);
|
||||||
self.max_width.hash(state);
|
self.max_width.hash(state);
|
||||||
self.max_height.hash(state);
|
self.max_height.hash(state);
|
||||||
self.align_self.hash(state);
|
|
||||||
self.align_items.hash(state);
|
self.align_items.hash(state);
|
||||||
self.justify_content.hash(state);
|
|
||||||
self.spacing.hash(state);
|
self.spacing.hash(state);
|
||||||
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
|
|
|
||||||
90
native/src/widget/container.rs
Normal file
90
native/src/widget/container.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
|
||||||
|
|
||||||
|
/// A container that distributes its contents vertically.
|
||||||
|
pub type Container<'a, Message, Renderer> =
|
||||||
|
iced_core::Container<Element<'a, Message, Renderer>>;
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
|
for Container<'a, Message, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: crate::Renderer,
|
||||||
|
{
|
||||||
|
fn width(&self) -> Length {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
let limits = limits
|
||||||
|
.loose()
|
||||||
|
.max_width(self.max_width)
|
||||||
|
.max_height(self.max_height)
|
||||||
|
.width(self.width)
|
||||||
|
.height(self.height);
|
||||||
|
|
||||||
|
let mut content = self.content.layout(renderer, &limits);
|
||||||
|
let size = limits.resolve(content.size());
|
||||||
|
|
||||||
|
content.align(self.horizontal_alignment, self.vertical_alignment, size);
|
||||||
|
|
||||||
|
layout::Node::with_children(size, vec![content])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(
|
||||||
|
&mut self,
|
||||||
|
event: Event,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: Point,
|
||||||
|
messages: &mut Vec<Message>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
) {
|
||||||
|
self.content.widget.on_event(
|
||||||
|
event,
|
||||||
|
layout.children().next().unwrap(),
|
||||||
|
cursor_position,
|
||||||
|
messages,
|
||||||
|
renderer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: Point,
|
||||||
|
) -> Renderer::Output {
|
||||||
|
self.content.draw(
|
||||||
|
renderer,
|
||||||
|
layout.children().next().unwrap(),
|
||||||
|
cursor_position,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_layout(&self, state: &mut Hasher) {
|
||||||
|
0.hash(state);
|
||||||
|
self.width.hash(state);
|
||||||
|
self.height.hash(state);
|
||||||
|
self.max_width.hash(state);
|
||||||
|
self.max_height.hash(state);
|
||||||
|
|
||||||
|
self.content.hash_layout(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
|
||||||
|
for Element<'a, Message, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: 'a + crate::Renderer,
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(
|
||||||
|
column: Container<'a, Message, Renderer>,
|
||||||
|
) -> Element<'a, Message, Renderer> {
|
||||||
|
Element::new(column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Display images in your user interface.
|
//! Display images in your user interface.
|
||||||
|
|
||||||
use crate::{Element, Hasher, Layout, Node, Point, Widget};
|
use crate::{layout, Element, Hasher, Layout, Point, Widget};
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
|
@ -10,8 +10,12 @@ impl<Message, Renderer> Widget<Message, Renderer> for Image
|
||||||
where
|
where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn layout(
|
||||||
renderer.node(&self)
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
renderer.layout(&self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
|
|
@ -26,7 +30,6 @@ where
|
||||||
fn hash_layout(&self, state: &mut Hasher) {
|
fn hash_layout(&self, state: &mut Hasher) {
|
||||||
self.width.hash(state);
|
self.width.hash(state);
|
||||||
self.height.hash(state);
|
self.height.hash(state);
|
||||||
self.align_self.hash(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,7 +47,7 @@ pub trait Renderer: crate::Renderer {
|
||||||
///
|
///
|
||||||
/// [`Node`]: ../../struct.Node.html
|
/// [`Node`]: ../../struct.Node.html
|
||||||
/// [`Image`]: struct.Image.html
|
/// [`Image`]: struct.Image.html
|
||||||
fn node(&self, image: &Image) -> Node;
|
fn layout(&self, image: &Image, limits: &layout::Limits) -> layout::Node;
|
||||||
|
|
||||||
/// Draws an [`Image`].
|
/// Draws an [`Image`].
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Create choices using radio buttons.
|
//! Create choices using radio buttons.
|
||||||
use crate::input::{mouse, ButtonState};
|
use crate::input::{mouse, ButtonState};
|
||||||
use crate::{Element, Event, Hasher, Layout, Node, Point, Widget};
|
use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
|
@ -11,8 +11,16 @@ where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
Message: Clone + std::fmt::Debug,
|
Message: Clone + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn width(&self) -> Length {
|
||||||
renderer.node(&self)
|
Length::Fill
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
renderer.layout(&self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -62,7 +70,11 @@ pub trait Renderer: crate::Renderer {
|
||||||
///
|
///
|
||||||
/// [`Node`]: ../../struct.Node.html
|
/// [`Node`]: ../../struct.Node.html
|
||||||
/// [`Radio`]: struct.Radio.html
|
/// [`Radio`]: struct.Radio.html
|
||||||
fn node<Message>(&self, radio: &Radio<Message>) -> Node;
|
fn layout<Message>(
|
||||||
|
&self,
|
||||||
|
radio: &Radio<Message>,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node;
|
||||||
|
|
||||||
/// Draws a [`Radio`] button.
|
/// Draws a [`Radio`] button.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{Element, Event, Hasher, Layout, Node, Point, Style, Widget};
|
use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
|
||||||
|
|
||||||
/// A container that distributes its contents horizontally.
|
/// A container that distributes its contents horizontally.
|
||||||
pub type Row<'a, Message, Renderer> =
|
pub type Row<'a, Message, Renderer> =
|
||||||
|
|
@ -11,42 +11,30 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn width(&self) -> Length {
|
||||||
let mut children: Vec<Node> = self
|
self.width
|
||||||
.children
|
}
|
||||||
.iter()
|
|
||||||
.map(|child| {
|
|
||||||
let mut node = child.widget.node(renderer);
|
|
||||||
|
|
||||||
let mut style = node.0.style();
|
fn layout(
|
||||||
style.margin.end =
|
&self,
|
||||||
stretch::style::Dimension::Points(f32::from(self.spacing));
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
node.0.set_style(style);
|
) -> layout::Node {
|
||||||
node
|
let limits = limits
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Some(node) = children.last_mut() {
|
|
||||||
let mut style = node.0.style();
|
|
||||||
style.margin.end = stretch::style::Dimension::Undefined;
|
|
||||||
|
|
||||||
node.0.set_style(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut style = Style::default()
|
|
||||||
.width(self.width)
|
|
||||||
.height(self.height)
|
|
||||||
.max_width(self.max_width)
|
.max_width(self.max_width)
|
||||||
.max_height(self.max_height)
|
.max_height(self.max_height)
|
||||||
.padding(self.padding)
|
.width(self.width)
|
||||||
.align_self(self.align_self)
|
.height(self.height);
|
||||||
.align_items(self.align_items)
|
|
||||||
.justify_content(self.justify_content);
|
|
||||||
|
|
||||||
style.0.flex_direction = stretch::style::FlexDirection::Row;
|
layout::flex::resolve(
|
||||||
|
layout::flex::Axis::Horizontal,
|
||||||
Node::with_children(style, children)
|
renderer,
|
||||||
|
&limits,
|
||||||
|
self.padding as f32,
|
||||||
|
self.spacing as f32,
|
||||||
|
self.align_items,
|
||||||
|
&self.children,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -85,9 +73,7 @@ where
|
||||||
self.height.hash(state);
|
self.height.hash(state);
|
||||||
self.max_width.hash(state);
|
self.max_width.hash(state);
|
||||||
self.max_height.hash(state);
|
self.max_height.hash(state);
|
||||||
self.align_self.hash(state);
|
|
||||||
self.align_items.hash(state);
|
self.align_items.hash(state);
|
||||||
self.justify_content.hash(state);
|
|
||||||
self.spacing.hash(state);
|
self.spacing.hash(state);
|
||||||
self.spacing.hash(state);
|
self.spacing.hash(state);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
column,
|
column,
|
||||||
input::{mouse, ButtonState},
|
input::{mouse, ButtonState},
|
||||||
Element, Event, Hasher, Layout, Node, Point, Rectangle, Style, Widget,
|
layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
|
||||||
|
Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use iced_core::scrollable::State;
|
pub use iced_core::scrollable::State;
|
||||||
|
|
||||||
|
use std::f32;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
/// A scrollable [`Column`].
|
/// A scrollable [`Column`].
|
||||||
|
|
@ -19,26 +21,25 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: self::Renderer + column::Renderer,
|
Renderer: self::Renderer + column::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn layout(
|
||||||
let mut content = self.content.node(renderer);
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
{
|
limits: &layout::Limits,
|
||||||
let mut style = content.0.style();
|
) -> layout::Node {
|
||||||
style.flex_shrink = 0.0;
|
let limits = limits
|
||||||
|
|
||||||
content.0.set_style(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut style = Style::default()
|
|
||||||
.width(self.content.width)
|
|
||||||
.max_width(self.content.max_width)
|
|
||||||
.height(self.height)
|
|
||||||
.max_height(self.max_height)
|
.max_height(self.max_height)
|
||||||
.align_self(self.align_self);
|
.width(Length::Fill)
|
||||||
|
.height(self.height);
|
||||||
|
|
||||||
style.0.flex_direction = stretch::style::FlexDirection::Column;
|
let child_limits = layout::Limits::new(
|
||||||
|
Size::new(limits.min().width, 0.0),
|
||||||
|
Size::new(limits.max().width, f32::INFINITY),
|
||||||
|
);
|
||||||
|
|
||||||
Node::with_children(style, vec![content])
|
let content = self.content.layout(renderer, &child_limits);
|
||||||
|
let size = limits.resolve(content.size());
|
||||||
|
|
||||||
|
layout::Node::with_children(size, vec![content])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -167,7 +168,6 @@ where
|
||||||
|
|
||||||
self.height.hash(state);
|
self.height.hash(state);
|
||||||
self.max_height.hash(state);
|
self.max_height.hash(state);
|
||||||
self.align_self.hash(state);
|
|
||||||
|
|
||||||
self.content.hash_layout(state)
|
self.content.hash_layout(state)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::input::{mouse, ButtonState};
|
use crate::input::{mouse, ButtonState};
|
||||||
use crate::{Element, Event, Hasher, Layout, Node, Point, Widget};
|
use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
|
||||||
|
|
||||||
pub use iced_core::slider::*;
|
pub use iced_core::slider::*;
|
||||||
|
|
||||||
|
|
@ -15,8 +15,16 @@ impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message>
|
||||||
where
|
where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn width(&self) -> Length {
|
||||||
renderer.node(&self)
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
renderer.layout(&self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -93,7 +101,11 @@ pub trait Renderer: crate::Renderer {
|
||||||
///
|
///
|
||||||
/// [`Node`]: ../../struct.Node.html
|
/// [`Node`]: ../../struct.Node.html
|
||||||
/// [`Radio`]: struct.Radio.html
|
/// [`Radio`]: struct.Radio.html
|
||||||
fn node<Message>(&self, slider: &Slider<'_, Message>) -> Node;
|
fn layout<Message>(
|
||||||
|
&self,
|
||||||
|
slider: &Slider<'_, Message>,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node;
|
||||||
|
|
||||||
/// Draws a [`Slider`].
|
/// Draws a [`Slider`].
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//! Write some text for your users to read.
|
//! Write some text for your users to read.
|
||||||
use crate::{Element, Hasher, Layout, Node, Point, Widget};
|
use crate::{layout, Element, Hasher, Layout, Length, Point, Widget};
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
|
@ -9,8 +9,16 @@ impl<Message, Renderer> Widget<Message, Renderer> for Text
|
||||||
where
|
where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn width(&self) -> Length {
|
||||||
renderer.node(&self)
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
renderer.layout(&self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
|
|
@ -49,7 +57,7 @@ pub trait Renderer: crate::Renderer {
|
||||||
/// [`Style`]: ../../struct.Style.html
|
/// [`Style`]: ../../struct.Style.html
|
||||||
/// [`Text`]: struct.Text.html
|
/// [`Text`]: struct.Text.html
|
||||||
/// [`Node::with_measure`]: ../../struct.Node.html#method.with_measure
|
/// [`Node::with_measure`]: ../../struct.Node.html#method.with_measure
|
||||||
fn node(&self, text: &Text) -> Node;
|
fn layout(&self, text: &Text, limits: &layout::Limits) -> layout::Node;
|
||||||
|
|
||||||
/// Draws a [`Text`] fragment.
|
/// Draws a [`Text`] fragment.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
input::{keyboard, mouse, ButtonState},
|
input::{keyboard, mouse, ButtonState},
|
||||||
Element, Event, Hasher, Layout, Length, Node, Point, Rectangle, Style,
|
layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
|
||||||
Widget,
|
Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -11,19 +11,24 @@ where
|
||||||
Renderer: self::Renderer,
|
Renderer: self::Renderer,
|
||||||
Message: Clone + std::fmt::Debug,
|
Message: Clone + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
fn node(&self, renderer: &Renderer) -> Node {
|
fn layout(
|
||||||
let text_bounds =
|
&self,
|
||||||
Node::new(Style::default().width(Length::Fill).height(
|
renderer: &Renderer,
|
||||||
Length::Units(self.size.unwrap_or(renderer.default_size())),
|
limits: &layout::Limits,
|
||||||
));
|
) -> layout::Node {
|
||||||
|
let padding = self.padding as f32;
|
||||||
|
let text_size = self.size.unwrap_or(renderer.default_size());
|
||||||
|
|
||||||
Node::with_children(
|
let limits = limits
|
||||||
Style::default()
|
.pad(padding)
|
||||||
.width(self.width)
|
.width(self.width)
|
||||||
.max_width(self.width)
|
.height(Length::Units(text_size));
|
||||||
.padding(self.padding),
|
|
||||||
vec![text_bounds],
|
let mut text = layout::Node::new(limits.resolve(Size::ZERO));
|
||||||
)
|
text.bounds.x = padding;
|
||||||
|
text.bounds.y = padding;
|
||||||
|
|
||||||
|
layout::Node::with_children(text.size().pad(padding), vec![text])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ pub use iced_wgpu::{Primitive, Renderer};
|
||||||
|
|
||||||
pub use iced_winit::{
|
pub use iced_winit::{
|
||||||
button, scrollable, slider, text, text_input, winit, Align, Background,
|
button, scrollable, slider, text, text_input, winit, Align, Background,
|
||||||
Checkbox, Color, Image, Justify, Length, Radio, Scrollable, Slider, Text,
|
Checkbox, Color, Image, Length, Radio, Scrollable, Slider, Text, TextInput,
|
||||||
TextInput,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Element<'a, Message> = iced_winit::Element<'a, Message, Renderer>;
|
pub type Element<'a, Message> = iced_winit::Element<'a, Message, Renderer>;
|
||||||
|
pub type Container<'a, Message> = iced_winit::Container<'a, Message, Renderer>;
|
||||||
pub type Row<'a, Message> = iced_winit::Row<'a, Message, Renderer>;
|
pub type Row<'a, Message> = iced_winit::Row<'a, Message, Renderer>;
|
||||||
pub type Column<'a, Message> = iced_winit::Column<'a, Message, Renderer>;
|
pub type Column<'a, Message> = iced_winit::Column<'a, Message, Renderer>;
|
||||||
pub type Button<'a, Message> = iced_winit::Button<'a, Message, Renderer>;
|
pub type Button<'a, Message> = iced_winit::Button<'a, Message, Renderer>;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub mod widget;
|
||||||
|
|
||||||
pub use bus::Bus;
|
pub use bus::Bus;
|
||||||
pub use element::Element;
|
pub use element::Element;
|
||||||
pub use iced_core::{Align, Background, Color, Justify, Length};
|
pub use iced_core::{Align, Background, Color, Length};
|
||||||
pub use widget::*;
|
pub use widget::*;
|
||||||
|
|
||||||
pub trait Application {
|
pub trait Application {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ repository = "https://github.com/hecrj/iced"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_native = { version = "0.1.0-alpha", path = "../native" }
|
iced_native = { version = "0.1.0-alpha", path = "../native" }
|
||||||
wgpu = "0.4"
|
wgpu = "0.4"
|
||||||
|
glyph_brush = "0.6"
|
||||||
wgpu_glyph = { version = "0.5", git = "https://github.com/hecrj/wgpu_glyph", branch = "feature/scissoring" }
|
wgpu_glyph = { version = "0.5", git = "https://github.com/hecrj/wgpu_glyph", branch = "feature/scissoring" }
|
||||||
raw-window-handle = "0.3"
|
raw-window-handle = "0.3"
|
||||||
image = "0.22"
|
image = "0.22"
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,8 @@ use wgpu::{
|
||||||
Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions,
|
Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions,
|
||||||
TextureFormat,
|
TextureFormat,
|
||||||
};
|
};
|
||||||
use wgpu_glyph::{GlyphBrush, GlyphBrushBuilder, Section};
|
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::cell::RefCell;
|
||||||
|
|
||||||
mod target;
|
mod target;
|
||||||
mod widget;
|
mod widget;
|
||||||
|
|
@ -23,8 +22,8 @@ pub struct Renderer {
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
quad_pipeline: quad::Pipeline,
|
quad_pipeline: quad::Pipeline,
|
||||||
image_pipeline: crate::image::Pipeline,
|
image_pipeline: crate::image::Pipeline,
|
||||||
|
text_pipeline: wgpu_glyph::GlyphBrush<'static, ()>,
|
||||||
glyph_brush: Rc<RefCell<GlyphBrush<'static, ()>>>,
|
text_measurements: RefCell<glyph_brush::GlyphBrush<'static, ()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Layer<'a> {
|
pub struct Layer<'a> {
|
||||||
|
|
@ -72,10 +71,17 @@ impl Renderer {
|
||||||
.load(&[font::Family::Monospace])
|
.load(&[font::Family::Monospace])
|
||||||
.expect("Find monospace font");
|
.expect("Find monospace font");
|
||||||
|
|
||||||
let glyph_brush =
|
let text_pipeline =
|
||||||
GlyphBrushBuilder::using_fonts_bytes(vec![default_font, mono_font])
|
wgpu_glyph::GlyphBrushBuilder::using_fonts_bytes(vec![
|
||||||
.initial_cache_size((2048, 2048))
|
default_font.clone(),
|
||||||
.build(&mut device, TextureFormat::Bgra8UnormSrgb);
|
mono_font,
|
||||||
|
])
|
||||||
|
.initial_cache_size((2048, 2048))
|
||||||
|
.build(&mut device, TextureFormat::Bgra8UnormSrgb);
|
||||||
|
|
||||||
|
let text_measurements =
|
||||||
|
glyph_brush::GlyphBrushBuilder::using_font_bytes(default_font)
|
||||||
|
.build();
|
||||||
|
|
||||||
let quad_pipeline = quad::Pipeline::new(&mut device);
|
let quad_pipeline = quad::Pipeline::new(&mut device);
|
||||||
let image_pipeline = crate::image::Pipeline::new(&mut device);
|
let image_pipeline = crate::image::Pipeline::new(&mut device);
|
||||||
|
|
@ -85,8 +91,8 @@ impl Renderer {
|
||||||
queue,
|
queue,
|
||||||
quad_pipeline,
|
quad_pipeline,
|
||||||
image_pipeline,
|
image_pipeline,
|
||||||
|
text_pipeline,
|
||||||
glyph_brush: Rc::new(RefCell::new(glyph_brush)),
|
text_measurements: RefCell::new(text_measurements),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +196,7 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
layer.text.push(Section {
|
layer.text.push(wgpu_glyph::Section {
|
||||||
text: &content,
|
text: &content,
|
||||||
screen_position: (
|
screen_position: (
|
||||||
x - layer.offset.x as f32,
|
x - layer.offset.x as f32,
|
||||||
|
|
@ -292,27 +298,26 @@ impl Renderer {
|
||||||
let first = layers.first().unwrap();
|
let first = layers.first().unwrap();
|
||||||
let mut overlay = Layer::new(first.bounds, Vector::new(0, 0));
|
let mut overlay = Layer::new(first.bounds, Vector::new(0, 0));
|
||||||
|
|
||||||
let font_id =
|
let font_id = wgpu_glyph::FontId(self.text_pipeline.fonts().len() - 1);
|
||||||
wgpu_glyph::FontId(self.glyph_brush.borrow().fonts().len() - 1);
|
|
||||||
let scale = wgpu_glyph::Scale { x: 20.0, y: 20.0 };
|
let scale = wgpu_glyph::Scale { x: 20.0, y: 20.0 };
|
||||||
|
|
||||||
for (i, line) in lines.iter().enumerate() {
|
for (i, line) in lines.iter().enumerate() {
|
||||||
overlay.text.push(Section {
|
overlay.text.push(wgpu_glyph::Section {
|
||||||
text: line.as_ref(),
|
text: line.as_ref(),
|
||||||
screen_position: (11.0, 11.0 + 25.0 * i as f32),
|
screen_position: (11.0, 11.0 + 25.0 * i as f32),
|
||||||
color: [0.9, 0.9, 0.9, 1.0],
|
color: [0.9, 0.9, 0.9, 1.0],
|
||||||
scale,
|
scale,
|
||||||
font_id,
|
font_id,
|
||||||
..Section::default()
|
..wgpu_glyph::Section::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
overlay.text.push(Section {
|
overlay.text.push(wgpu_glyph::Section {
|
||||||
text: line.as_ref(),
|
text: line.as_ref(),
|
||||||
screen_position: (10.0, 10.0 + 25.0 * i as f32),
|
screen_position: (10.0, 10.0 + 25.0 * i as f32),
|
||||||
color: [0.0, 0.0, 0.0, 1.0],
|
color: [0.0, 0.0, 0.0, 1.0],
|
||||||
scale,
|
scale,
|
||||||
font_id,
|
font_id,
|
||||||
..Section::default()
|
..wgpu_glyph::Section::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,11 +365,9 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if layer.text.len() > 0 {
|
if layer.text.len() > 0 {
|
||||||
let mut glyph_brush = self.glyph_brush.borrow_mut();
|
|
||||||
|
|
||||||
for text in layer.text.iter() {
|
for text in layer.text.iter() {
|
||||||
// Target physical coordinates directly to avoid blurry text
|
// Target physical coordinates directly to avoid blurry text
|
||||||
let text = Section {
|
let text = wgpu_glyph::Section {
|
||||||
screen_position: (
|
screen_position: (
|
||||||
(text.screen_position.0 * dpi).round(),
|
(text.screen_position.0 * dpi).round(),
|
||||||
(text.screen_position.1 * dpi).round(),
|
(text.screen_position.1 * dpi).round(),
|
||||||
|
|
@ -377,10 +380,10 @@ impl Renderer {
|
||||||
..*text
|
..*text
|
||||||
};
|
};
|
||||||
|
|
||||||
glyph_brush.queue(text);
|
self.text_pipeline.queue(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
glyph_brush
|
self.text_pipeline
|
||||||
.draw_queued_with_transform_and_scissoring(
|
.draw_queued_with_transform_and_scissoring(
|
||||||
&mut self.device,
|
&mut self.device,
|
||||||
encoder,
|
encoder,
|
||||||
|
|
@ -400,6 +403,25 @@ impl Renderer {
|
||||||
|
|
||||||
impl iced_native::Renderer for Renderer {
|
impl iced_native::Renderer for Renderer {
|
||||||
type Output = (Primitive, MouseCursor);
|
type Output = (Primitive, MouseCursor);
|
||||||
|
|
||||||
|
fn layout<'a, Message>(
|
||||||
|
&mut self,
|
||||||
|
element: &iced_native::Element<'a, Message, Self>,
|
||||||
|
) -> iced_native::layout::Node {
|
||||||
|
let node = element.layout(self, &iced_native::layout::Limits::NONE);
|
||||||
|
|
||||||
|
// Trim measurements cache
|
||||||
|
// TODO: We should probably use a `GlyphCalculator` for this. However,
|
||||||
|
// it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop.
|
||||||
|
// This makes stuff quite inconvenient. A manual method for trimming the
|
||||||
|
// cache would make our lives easier.
|
||||||
|
self.text_measurements
|
||||||
|
.borrow_mut()
|
||||||
|
.process_queued(|_, _| {}, |_| {})
|
||||||
|
.expect("Trim text measurements");
|
||||||
|
|
||||||
|
node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Windowed for Renderer {
|
impl Windowed for Renderer {
|
||||||
|
|
@ -438,7 +460,7 @@ impl Debugger for Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn explain_layout(
|
fn explain_layout(
|
||||||
layout: Layout,
|
layout: Layout<'_>,
|
||||||
color: Color,
|
color: Color,
|
||||||
primitives: &mut Vec<Primitive>,
|
primitives: &mut Vec<Primitive>,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,30 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{
|
use iced_native::{
|
||||||
button, Align, Background, Button, Layout, Length, MouseCursor,
|
button, layout, Background, Button, Layout, Length, MouseCursor, Point,
|
||||||
Node, Point, Rectangle, Style,
|
Rectangle,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl button::Renderer for Renderer {
|
impl button::Renderer for Renderer {
|
||||||
fn node<Message>(&self, button: &Button<Message, Self>) -> Node {
|
fn layout<Message>(
|
||||||
let style = Style::default()
|
&self,
|
||||||
|
button: &Button<Message, Self>,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
let padding = f32::from(button.padding);
|
||||||
|
let limits = limits
|
||||||
|
.min_width(100)
|
||||||
.width(button.width)
|
.width(button.width)
|
||||||
.padding(button.padding)
|
.height(Length::Shrink)
|
||||||
.min_width(Length::Units(100))
|
.pad(padding);
|
||||||
.align_self(button.align_self)
|
|
||||||
.align_items(Align::Stretch);
|
|
||||||
|
|
||||||
Node::with_children(style, vec![button.content.node(self)])
|
let mut content = button.content.layout(self, &limits);
|
||||||
|
|
||||||
|
content.bounds.x = padding;
|
||||||
|
content.bounds.y = padding;
|
||||||
|
|
||||||
|
let size = limits.resolve(content.size()).pad(padding);
|
||||||
|
|
||||||
|
layout::Node::with_children(size, vec![content])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw<Message>(
|
fn draw<Message>(
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{
|
use iced_native::{
|
||||||
checkbox, text, text::HorizontalAlignment, text::VerticalAlignment, Align,
|
checkbox, layout, text, text::HorizontalAlignment, text::VerticalAlignment,
|
||||||
Background, Checkbox, Column, Layout, Length, MouseCursor, Node,
|
Align, Background, Checkbox, Column, Layout, Length, MouseCursor, Point,
|
||||||
Point, Rectangle, Row, Text, Widget,
|
Rectangle, Row, Text, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SIZE: f32 = 28.0;
|
const SIZE: f32 = 28.0;
|
||||||
|
|
||||||
impl checkbox::Renderer for Renderer {
|
impl checkbox::Renderer for Renderer {
|
||||||
fn node<Message>(&self, checkbox: &Checkbox<Message>) -> Node {
|
fn layout<Message>(
|
||||||
|
&self,
|
||||||
|
checkbox: &Checkbox<Message>,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
Row::<(), Self>::new()
|
Row::<(), Self>::new()
|
||||||
.width(Length::Fill)
|
|
||||||
.spacing(15)
|
.spacing(15)
|
||||||
.align_items(Align::Center)
|
.align_items(Align::Center)
|
||||||
.push(
|
.push(
|
||||||
|
|
@ -19,7 +22,7 @@ impl checkbox::Renderer for Renderer {
|
||||||
.height(Length::Units(SIZE as u16)),
|
.height(Length::Units(SIZE as u16)),
|
||||||
)
|
)
|
||||||
.push(Text::new(&checkbox.label))
|
.push(Text::new(&checkbox.label))
|
||||||
.node(self)
|
.layout(self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw<Message>(
|
fn draw<Message>(
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,28 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{image, Image, Layout, Length, MouseCursor, Node, Style};
|
use iced_native::{image, layout, Image, Layout, Length, MouseCursor, Size};
|
||||||
|
|
||||||
impl image::Renderer for Renderer {
|
impl image::Renderer for Renderer {
|
||||||
fn node(&self, image: &Image) -> Node {
|
fn layout(&self, image: &Image, limits: &layout::Limits) -> layout::Node {
|
||||||
let (width, height) = self.image_pipeline.dimensions(&image.path);
|
let (width, height) = self.image_pipeline.dimensions(&image.path);
|
||||||
|
|
||||||
let aspect_ratio = width as f32 / height as f32;
|
let aspect_ratio = width as f32 / height as f32;
|
||||||
|
|
||||||
let mut style = Style::default().align_self(image.align_self);
|
|
||||||
|
|
||||||
// TODO: Deal with additional cases
|
// TODO: Deal with additional cases
|
||||||
style = match (image.width, image.height) {
|
let (width, height) = match (image.width, image.height) {
|
||||||
(Length::Units(width), _) => style.width(image.width).height(
|
(Length::Units(width), _) => (
|
||||||
|
image.width,
|
||||||
Length::Units((width as f32 / aspect_ratio).round() as u16),
|
Length::Units((width as f32 / aspect_ratio).round() as u16),
|
||||||
),
|
),
|
||||||
(_, _) => style
|
(_, _) => {
|
||||||
.width(Length::Units(width as u16))
|
(Length::Units(width as u16), Length::Units(height as u16))
|
||||||
.height(Length::Units(height as u16)),
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Node::new(style)
|
let mut size = limits.width(width).height(height).resolve(Size::ZERO);
|
||||||
|
|
||||||
|
size.height = size.width / aspect_ratio;
|
||||||
|
|
||||||
|
layout::Node::new(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, image: &Image, layout: Layout<'_>) -> Self::Output {
|
fn draw(&mut self, image: &Image, layout: Layout<'_>) -> Self::Output {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{
|
use iced_native::{
|
||||||
radio, text, Align, Background, Column, Layout, Length, MouseCursor,
|
layout, radio, text, Align, Background, Column, Layout, Length,
|
||||||
Node, Point, Radio, Rectangle, Row, Text, Widget,
|
MouseCursor, Point, Radio, Rectangle, Row, Text, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SIZE: f32 = 28.0;
|
const SIZE: f32 = 28.0;
|
||||||
const DOT_SIZE: f32 = SIZE / 2.0;
|
const DOT_SIZE: f32 = SIZE / 2.0;
|
||||||
|
|
||||||
impl radio::Renderer for Renderer {
|
impl radio::Renderer for Renderer {
|
||||||
fn node<Message>(&self, radio: &Radio<Message>) -> Node {
|
fn layout<Message>(
|
||||||
|
&self,
|
||||||
|
radio: &Radio<Message>,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
Row::<(), Self>::new()
|
Row::<(), Self>::new()
|
||||||
.spacing(15)
|
.spacing(15)
|
||||||
.align_items(Align::Center)
|
.align_items(Align::Center)
|
||||||
|
|
@ -18,7 +22,7 @@ impl radio::Renderer for Renderer {
|
||||||
.height(Length::Units(SIZE as u16)),
|
.height(Length::Units(SIZE as u16)),
|
||||||
)
|
)
|
||||||
.push(Text::new(&radio.label))
|
.push(Text::new(&radio.label))
|
||||||
.node(self)
|
.layout(self, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw<Message>(
|
fn draw<Message>(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{
|
use iced_native::{
|
||||||
scrollable, Background, Layout, MouseCursor, Point, Rectangle,
|
scrollable, Background, Layout, MouseCursor, Point, Rectangle, Scrollable,
|
||||||
Scrollable, Vector, Widget,
|
Vector, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SCROLLBAR_WIDTH: u16 = 10;
|
const SCROLLBAR_WIDTH: u16 = 10;
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{
|
use iced_native::{
|
||||||
slider, Background, Color, Layout, Length, MouseCursor, Node, Point,
|
layout, slider, Background, Color, Layout, Length, MouseCursor, Point,
|
||||||
Rectangle, Slider, Style,
|
Rectangle, Size, Slider,
|
||||||
};
|
};
|
||||||
|
|
||||||
const HANDLE_WIDTH: f32 = 8.0;
|
const HANDLE_WIDTH: f32 = 8.0;
|
||||||
const HANDLE_HEIGHT: f32 = 22.0;
|
const HANDLE_HEIGHT: f32 = 22.0;
|
||||||
|
|
||||||
impl slider::Renderer for Renderer {
|
impl slider::Renderer for Renderer {
|
||||||
fn node<Message>(&self, slider: &Slider<Message>) -> Node {
|
fn layout<Message>(
|
||||||
let style = Style::default()
|
&self,
|
||||||
.width(slider.width)
|
slider: &Slider<Message>,
|
||||||
.height(Length::Units(HANDLE_HEIGHT as u16))
|
limits: &layout::Limits,
|
||||||
.min_width(Length::Units(100));
|
) -> layout::Node {
|
||||||
|
let limits = limits.width(slider.width).height(Length::Units(30));
|
||||||
|
let size = limits.resolve(Size::ZERO);
|
||||||
|
|
||||||
Node::new(style)
|
layout::Node::new(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw<Message>(
|
fn draw<Message>(
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,37 @@
|
||||||
use crate::{Primitive, Renderer};
|
use crate::{Primitive, Renderer};
|
||||||
use iced_native::{text, Color, Layout, MouseCursor, Node, Style, Text};
|
use iced_native::{layout, text, Color, Layout, MouseCursor, Size, Text};
|
||||||
|
|
||||||
use wgpu_glyph::{GlyphCruncher, Section};
|
use wgpu_glyph::{GlyphCruncher, Section};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
// TODO: Obtain from renderer configuration
|
// TODO: Obtain from renderer configuration
|
||||||
const DEFAULT_TEXT_SIZE: f32 = 20.0;
|
const DEFAULT_TEXT_SIZE: f32 = 20.0;
|
||||||
|
|
||||||
impl text::Renderer for Renderer {
|
impl text::Renderer for Renderer {
|
||||||
fn node(&self, text: &Text) -> Node {
|
fn layout(&self, text: &Text, limits: &layout::Limits) -> layout::Node {
|
||||||
let glyph_brush = self.glyph_brush.clone();
|
let limits = limits.width(text.width).height(text.height);
|
||||||
let content = text.content.clone();
|
|
||||||
|
|
||||||
// TODO: Investigate why stretch tries to measure this MANY times
|
|
||||||
// with every ancestor's bounds.
|
|
||||||
// Bug? Using the library wrong? I should probably open an issue on
|
|
||||||
// the stretch repository.
|
|
||||||
// I noticed that the first measure is the one that matters in
|
|
||||||
// practice. Here, we use a RefCell to store the cached measurement.
|
|
||||||
let measure = RefCell::new(None);
|
|
||||||
let size = text.size.map(f32::from).unwrap_or(DEFAULT_TEXT_SIZE);
|
let size = text.size.map(f32::from).unwrap_or(DEFAULT_TEXT_SIZE);
|
||||||
|
let bounds = limits.max();
|
||||||
|
|
||||||
let style = Style::default().width(text.width);
|
let section = Section {
|
||||||
|
text: &text.content,
|
||||||
|
scale: wgpu_glyph::Scale { x: size, y: size },
|
||||||
|
bounds: (bounds.width, bounds.height),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
iced_native::Node::with_measure(style, move |bounds| {
|
let (width, height) = if let Some(bounds) =
|
||||||
let mut measure = measure.borrow_mut();
|
self.text_measurements.borrow_mut().glyph_bounds(§ion)
|
||||||
|
{
|
||||||
|
(bounds.width().ceil(), bounds.height().ceil())
|
||||||
|
} else {
|
||||||
|
(0.0, 0.0)
|
||||||
|
};
|
||||||
|
|
||||||
if measure.is_none() {
|
let size = limits.resolve(Size::new(width, height));
|
||||||
let bounds = (
|
|
||||||
match bounds.width {
|
|
||||||
iced_native::Number::Undefined => f32::INFINITY,
|
|
||||||
iced_native::Number::Defined(w) => w,
|
|
||||||
},
|
|
||||||
match bounds.height {
|
|
||||||
iced_native::Number::Undefined => f32::INFINITY,
|
|
||||||
iced_native::Number::Defined(h) => h,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let text = Section {
|
layout::Node::new(size)
|
||||||
text: &content,
|
|
||||||
scale: wgpu_glyph::Scale { x: size, y: size },
|
|
||||||
bounds,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let (width, height) = if let Some(bounds) =
|
|
||||||
glyph_brush.borrow_mut().glyph_bounds(&text)
|
|
||||||
{
|
|
||||||
(bounds.width().ceil(), bounds.height().ceil())
|
|
||||||
} else {
|
|
||||||
(0.0, 0.0)
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = iced_native::Size { width, height };
|
|
||||||
|
|
||||||
// If the text has no width boundary we avoid caching as the
|
|
||||||
// layout engine may just be measuring text in a row.
|
|
||||||
if bounds.0 == f32::INFINITY {
|
|
||||||
return size;
|
|
||||||
} else {
|
|
||||||
*measure = Some(size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
measure.unwrap()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, text: &Text, layout: Layout<'_>) -> Self::Output {
|
fn draw(&mut self, text: &Text, layout: Layout<'_>) -> Self::Output {
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ impl text_input::Renderer for Renderer {
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let mut text_value_width = self
|
let mut text_value_width = self
|
||||||
.glyph_brush
|
.text_measurements
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.glyph_bounds(Section {
|
.glyph_bounds(Section {
|
||||||
text: text_before_cursor,
|
text: text_before_cursor,
|
||||||
|
|
@ -94,7 +94,7 @@ impl text_input::Renderer for Renderer {
|
||||||
|
|
||||||
if spaces_at_the_end > 0 {
|
if spaces_at_the_end > 0 {
|
||||||
let space_width = {
|
let space_width = {
|
||||||
let glyph_brush = self.glyph_brush.borrow();
|
let glyph_brush = self.text_measurements.borrow();
|
||||||
|
|
||||||
// TODO: Select appropriate font
|
// TODO: Select appropriate font
|
||||||
let font = &glyph_brush.fonts()[0];
|
let font = &glyph_brush.fonts()[0];
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
column, conversion,
|
conversion,
|
||||||
input::{keyboard, mouse},
|
input::{keyboard, mouse},
|
||||||
renderer::{Target, Windowed},
|
renderer::{Target, Windowed},
|
||||||
Cache, Column, Debug, Element, Event, Length, MouseCursor, UserInterface,
|
Cache, Container, Debug, Element, Event, Length, MouseCursor,
|
||||||
|
UserInterface,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Application {
|
pub trait Application {
|
||||||
type Renderer: Windowed + column::Renderer;
|
type Renderer: Windowed;
|
||||||
|
|
||||||
type Message: std::fmt::Debug;
|
type Message: std::fmt::Debug;
|
||||||
|
|
||||||
|
|
@ -60,7 +61,7 @@ pub trait Application {
|
||||||
let user_interface = UserInterface::build(
|
let user_interface = UserInterface::build(
|
||||||
document(&mut self, size, &mut debug),
|
document(&mut self, size, &mut debug),
|
||||||
Cache::default(),
|
Cache::default(),
|
||||||
&renderer,
|
&mut renderer,
|
||||||
);
|
);
|
||||||
debug.layout_finished();
|
debug.layout_finished();
|
||||||
|
|
||||||
|
|
@ -86,7 +87,7 @@ pub trait Application {
|
||||||
let mut user_interface = UserInterface::build(
|
let mut user_interface = UserInterface::build(
|
||||||
document(&mut self, size, &mut debug),
|
document(&mut self, size, &mut debug),
|
||||||
cache.take().unwrap(),
|
cache.take().unwrap(),
|
||||||
&renderer,
|
&mut renderer,
|
||||||
);
|
);
|
||||||
debug.layout_finished();
|
debug.layout_finished();
|
||||||
|
|
||||||
|
|
@ -129,7 +130,7 @@ pub trait Application {
|
||||||
let user_interface = UserInterface::build(
|
let user_interface = UserInterface::build(
|
||||||
document(&mut self, size, &mut debug),
|
document(&mut self, size, &mut debug),
|
||||||
temp_cache,
|
temp_cache,
|
||||||
&renderer,
|
&mut renderer,
|
||||||
);
|
);
|
||||||
debug.layout_finished();
|
debug.layout_finished();
|
||||||
|
|
||||||
|
|
@ -282,9 +283,8 @@ where
|
||||||
let view = application.view();
|
let view = application.view();
|
||||||
debug.view_finished();
|
debug.view_finished();
|
||||||
|
|
||||||
Column::new()
|
Container::new(view)
|
||||||
.width(Length::Units(size.width.round() as u16))
|
.width(Length::Units(size.width.round() as u16))
|
||||||
.height(Length::Units(size.height.round() as u16))
|
.height(Length::Units(size.height.round() as u16))
|
||||||
.push(view)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue