diff options
| author | 2019-11-14 06:46:50 +0100 | |
|---|---|---|
| committer | 2019-11-14 06:46:50 +0100 | |
| commit | bc8d347736ec997ec0e0c401289e2bc09e212b8a (patch) | |
| tree | b98798c09a3aa914b7d0869fba0cfd3efff7754f | |
| parent | 839e039dbf2fb89dcb8c141503740777d2af2eb3 (diff) | |
| parent | 73f3c900071f950ea914652ca3f0002c1e173f61 (diff) | |
| download | iced-bc8d347736ec997ec0e0c401289e2bc09e212b8a.tar.gz iced-bc8d347736ec997ec0e0c401289e2bc09e212b8a.tar.bz2 iced-bc8d347736ec997ec0e0c401289e2bc09e212b8a.zip  | |
Merge pull request #52 from hecrj/custom-layout-engine
Custom layout engine
53 files changed, 1271 insertions, 759 deletions
diff --git a/core/src/align.rs b/core/src/align.rs index 5dbd658d..d6915086 100644 --- a/core/src/align.rs +++ b/core/src/align.rs @@ -15,7 +15,4 @@ pub enum Align {      /// Align at the end of the cross axis.      End, - -    /// Stretch over the cross axis. -    Stretch,  } diff --git a/core/src/justify.rs b/core/src/justify.rs deleted file mode 100644 index 53aa7319..00000000 --- a/core/src/justify.rs +++ /dev/null @@ -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, -} diff --git a/core/src/length.rs b/core/src/length.rs index 0e670038..73c227d8 100644 --- a/core/src/length.rs +++ b/core/src/length.rs @@ -1,7 +1,17 @@  /// 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 {      Fill,      Shrink,      Units(u16),  } + +impl Length { +    pub fn fill_factor(&self) -> u16 { +        match self { +            Length::Fill => 1, +            Length::Shrink => 0, +            Length::Units(_) => 0, +        } +    } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 877a8f85..ab43ab94 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,7 +3,6 @@ pub mod widget;  mod align;  mod background;  mod color; -mod justify;  mod length;  mod point;  mod rectangle; @@ -12,7 +11,6 @@ mod vector;  pub use align::Align;  pub use background::Background;  pub use color::Color; -pub use justify::Justify;  pub use length::Length;  pub use point::Point;  pub use rectangle::Rectangle; diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index c3191677..ee1e3807 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -1,7 +1,7 @@  use crate::Point;  /// A rectangle. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]  pub struct Rectangle<T = f32> {      /// X coordinate of the top-left corner.      pub x: T, diff --git a/core/src/widget.rs b/core/src/widget.rs index 41c4c6a8..9e629e4f 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -9,6 +9,7 @@  //! ```  mod checkbox;  mod column; +mod container;  mod image;  mod radio;  mod row; @@ -32,6 +33,7 @@ pub use text_input::TextInput;  pub use checkbox::Checkbox;  pub use column::Column; +pub use container::Container;  pub use image::Image;  pub use radio::Radio;  pub use row::Row; diff --git a/core/src/widget/button.rs b/core/src/widget/button.rs index a57f2dd8..9cf20071 100644 --- a/core/src/widget/button.rs +++ b/core/src/widget/button.rs @@ -5,7 +5,7 @@  //! [`Button`]: struct.Button.html  //! [`State`]: struct.State.html -use crate::{Align, Background, Length}; +use crate::{Background, Length};  /// A generic widget that produces a message when clicked.  pub struct Button<'a, Message, Element> { @@ -24,8 +24,6 @@ pub struct Button<'a, Message, Element> {      pub background: Option<Background>,      pub border_radius: u16, - -    pub align_self: Option<Align>,  }  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,              background: None,              border_radius: 0, -            align_self: None,          }      } @@ -84,17 +81,6 @@ impl<'a, Message, Element> Button<'a, Message, Element> {          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.      ///      /// [`Button`]: struct.Button.html diff --git a/core/src/widget/column.rs b/core/src/widget/column.rs index 2df327a0..4a97ad98 100644 --- a/core/src/widget/column.rs +++ b/core/src/widget/column.rs @@ -1,4 +1,6 @@ -use crate::{Align, Justify, Length}; +use crate::{Align, Length}; + +use std::u32;  /// A container that distributes its contents vertically.  /// @@ -10,11 +12,9 @@ pub struct Column<Element> {      pub padding: u16,      pub width: Length,      pub height: Length, -    pub max_width: Length, -    pub max_height: Length, -    pub align_self: Option<Align>, +    pub max_width: u32, +    pub max_height: u32,      pub align_items: Align, -    pub justify_content: Justify,      pub children: Vec<Element>,  } @@ -28,11 +28,9 @@ impl<Element> Column<Element> {              padding: 0,              width: Length::Fill,              height: Length::Shrink, -            max_width: Length::Shrink, -            max_height: Length::Shrink, -            align_self: None, +            max_width: u32::MAX, +            max_height: u32::MAX,              align_items: Align::Start, -            justify_content: Justify::Start,              children: Vec::new(),          }      } @@ -74,7 +72,7 @@ impl<Element> Column<Element> {      /// Sets the maximum width of the [`Column`].      ///      /// [`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      } @@ -82,22 +80,11 @@ impl<Element> Column<Element> {      /// Sets the maximum height of the [`Column`] in pixels.      ///      /// [`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      } -    /// 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`] .      ///      /// [`Column`]: struct.Column.html @@ -106,15 +93,6 @@ impl<Element> Column<Element> {          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`].      ///      /// [`Column`]: struct.Column.html diff --git a/core/src/widget/container.rs b/core/src/widget/container.rs new file mode 100644 index 00000000..9bc92fe0 --- /dev/null +++ b/core/src/widget/container.rs @@ -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 +    } +} diff --git a/core/src/widget/image.rs b/core/src/widget/image.rs index 6e410dce..996ab5e1 100644 --- a/core/src/widget/image.rs +++ b/core/src/widget/image.rs @@ -1,6 +1,6 @@  //! 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.  /// @@ -24,8 +24,6 @@ pub struct Image {      /// The height of the image      pub height: Length, - -    pub align_self: Option<Align>,  }  impl Image { @@ -38,7 +36,6 @@ impl Image {              clip: None,              width: Length::Shrink,              height: Length::Shrink, -            align_self: None,          }      } @@ -65,15 +62,4 @@ impl Image {          self.height = height;          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 -    }  } diff --git a/core/src/widget/row.rs b/core/src/widget/row.rs index 6bdb4ed2..3d882b47 100644 --- a/core/src/widget/row.rs +++ b/core/src/widget/row.rs @@ -1,4 +1,6 @@ -use crate::{Align, Justify, Length}; +use crate::{Align, Length}; + +use std::u32;  /// A container that distributes its contents horizontally.  /// @@ -10,11 +12,9 @@ pub struct Row<Element> {      pub padding: u16,      pub width: Length,      pub height: Length, -    pub max_width: Length, -    pub max_height: Length, -    pub align_self: Option<Align>, +    pub max_width: u32, +    pub max_height: u32,      pub align_items: Align, -    pub justify_content: Justify,      pub children: Vec<Element>,  } @@ -28,11 +28,9 @@ impl<Element> Row<Element> {              padding: 0,              width: Length::Fill,              height: Length::Shrink, -            max_width: Length::Shrink, -            max_height: Length::Shrink, -            align_self: None, +            max_width: u32::MAX, +            max_height: u32::MAX,              align_items: Align::Start, -            justify_content: Justify::Start,              children: Vec::new(),          }      } @@ -74,7 +72,7 @@ impl<Element> Row<Element> {      /// Sets the maximum width of the [`Row`].      ///      /// [`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      } @@ -82,22 +80,11 @@ impl<Element> Row<Element> {      /// Sets the maximum height of the [`Row`].      ///      /// [`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      } -    /// 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`] .      ///      /// [`Row`]: struct.Row.html @@ -106,15 +93,6 @@ impl<Element> Row<Element> {          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`].      ///      /// [`Element`]: ../struct.Element.html diff --git a/core/src/widget/scrollable.rs b/core/src/widget/scrollable.rs index 31a5abed..7f2f0e99 100644 --- a/core/src/widget/scrollable.rs +++ b/core/src/widget/scrollable.rs @@ -1,11 +1,12 @@  use crate::{Align, Column, Length, Point, Rectangle}; +use std::u32; +  #[derive(Debug)]  pub struct Scrollable<'a, Element> {      pub state: &'a mut State,      pub height: Length, -    pub max_height: Length, -    pub align_self: Option<Align>, +    pub max_height: u32,      pub content: Column<Element>,  } @@ -14,8 +15,7 @@ impl<'a, Element> Scrollable<'a, Element> {          Scrollable {              state,              height: Length::Shrink, -            max_height: Length::Shrink, -            align_self: None, +            max_height: u32::MAX,              content: Column::new(),          }      } @@ -57,7 +57,7 @@ impl<'a, Element> Scrollable<'a, Element> {      /// Sets the maximum width of the [`Scrollable`].      ///      /// [`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      } @@ -65,22 +65,11 @@ impl<'a, Element> Scrollable<'a, Element> {      /// Sets the maximum height of the [`Scrollable`] in pixels.      ///      /// [`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      } -    /// 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`] .      ///      /// [`Scrollable`]: struct.Scrollable.html @@ -140,9 +129,9 @@ impl State {      pub fn offset(&self, bounds: Rectangle, content_bounds: Rectangle) -> u32 {          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 { diff --git a/examples/scroll.rs b/examples/scroll.rs index 5a725f0c..50701879 100644 --- a/examples/scroll.rs +++ b/examples/scroll.rs @@ -1,6 +1,6 @@  use iced::{ -    button, scrollable, Align, Application, Button, Column, Element, Image, -    Justify, Length, Scrollable, Text, +    button, scrollable, Align, Application, Button, Container, Element, Image, +    Length, Scrollable, Text,  };  pub fn main() { @@ -65,11 +65,10 @@ impl Application for Example {                      .border_radius(5),              ); -        Column::new() +        Container::new(content) +            .width(Length::Fill)              .height(Length::Fill) -            .justify_content(Justify::Center) -            .padding(20) -            .push(content) +            .center_y()              .into()      }  } diff --git a/examples/todos.rs b/examples/todos.rs index 5257fc12..7f37300b 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -1,6 +1,6 @@  use iced::{ -    scrollable, text::HorizontalAlignment, text_input, Align, Application, -    Checkbox, Color, Column, Element, Length, Scrollable, Text, TextInput, +    scrollable, text::HorizontalAlignment, text_input, Application, Checkbox, +    Color, Column, Container, Element, Length, Scrollable, Text, TextInput,  };  pub fn main() { @@ -77,8 +77,7 @@ impl Application for Todos {          );          let content = Column::new() -            .max_width(Length::Units(800)) -            .align_self(Align::Center) +            .max_width(800)              .spacing(20)              .push(title)              .push(input) @@ -86,7 +85,7 @@ impl Application for Todos {          Scrollable::new(&mut self.scroll)              .padding(40) -            .push(content) +            .push(Container::new(content).width(Length::Fill).center_x())              .into()      }  } diff --git a/examples/tour.rs b/examples/tour.rs index ac654a5e..3fd031b8 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -1,7 +1,7 @@  use iced::{ -    button, scrollable, slider, text::HorizontalAlignment, text_input, Align, -    Application, Background, Button, Checkbox, Color, Column, Element, Image, -    Justify, Length, Radio, Row, Scrollable, Slider, Text, TextInput, +    button, scrollable, slider, text::HorizontalAlignment, text_input, +    Application, Background, Button, Checkbox, Color, Column, Container, +    Element, Image, Length, Radio, Row, Scrollable, Slider, Text, TextInput,  };  pub fn main() { @@ -25,7 +25,7 @@ impl Tour {              scroll: scrollable::State::new(),              back_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() -            .max_width(Length::Units(540)) +        let content: Element<_> = Column::new() +            .max_width(540)              .spacing(20)              .padding(20)              .push(steps.view(self.debug).map(Message::StepMessage))              .push(controls)              .into(); -        let element = if self.debug { -            element.explain(Color::BLACK) +        let content = if self.debug { +            content.explain(Color::BLACK)          } 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) -            .justify_content(Justify::Center) -            .push( -                Scrollable::new(scroll) -                    .align_items(Align::Center) -                    .push(element), -            ) +            .center_y()              .into()      }  } @@ -340,10 +338,7 @@ impl<'a> Step {      }      fn container(title: &str) -> Column<'a, StepMessage> { -        Column::new() -            .spacing(20) -            .align_items(Align::Stretch) -            .push(Text::new(title).size(50)) +        Column::new().spacing(20).push(Text::new(title).size(50))      }      fn welcome() -> Column<'a, StepMessage> { @@ -646,19 +641,22 @@ impl<'a> Step {      }  } -fn ferris(width: u16) -> Image { -    // This should go away once we unify resource loading on native -    // platforms -    if cfg!(target_arch = "wasm32") { -        Image::new("resources/ferris.png") -    } else { -        Image::new(format!( -            "{}/examples/resources/ferris.png", -            env!("CARGO_MANIFEST_DIR") -        )) -    } -    .width(Length::Units(width)) -    .align_self(Align::Center) +fn ferris<'a>(width: u16) -> Container<'a, StepMessage> { +    Container::new( +        // This should go away once we unify resource loading on native +        // platforms +        if cfg!(target_arch = "wasm32") { +            Image::new("resources/ferris.png") +        } else { +            Image::new(format!( +                "{}/examples/resources/ferris.png", +                env!("CARGO_MANIFEST_DIR") +            )) +        } +        .width(Length::Units(width)), +    ) +    .width(Length::Fill) +    .center_x()  }  fn button<'a, Message>( diff --git a/native/Cargo.toml b/native/Cargo.toml index bb6139d6..38db1610 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -9,6 +9,5 @@ repository = "https://github.com/hecrj/iced"  [dependencies]  iced_core = { version = "0.1.0-alpha", path = "../core" } -stretch = "0.2"  twox-hash = "1.5"  raw-window-handle = "0.3" diff --git a/native/src/element.rs b/native/src/element.rs index be64a981..23f069f1 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -1,6 +1,6 @@ -use stretch::{geometry, result}; - -use crate::{renderer, Color, Event, Hasher, Layout, Node, Point, Widget}; +use crate::{ +    layout, renderer, Color, Event, Hasher, Layout, Length, Point, Widget, +};  /// A generic [`Widget`].  /// @@ -41,8 +41,20 @@ where          }      } -    pub fn node(&self, renderer: &Renderer) -> Node { -        self.widget.node(renderer) +    pub fn width(&self) -> Length { +        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( @@ -116,7 +128,7 @@ where      /// #      /// # mod iced_wgpu {      /// #     use iced_native::{ -    /// #         text, row, Text, Node, Point, Rectangle, Style, Layout, Row +    /// #         text, row, layout, Text, Size, Point, Rectangle, Layout, Row      /// #     };      /// #     pub struct Renderer;      /// # @@ -132,8 +144,12 @@ where      /// #     }      /// #      /// #     impl text::Renderer for Renderer { -    /// #         fn node(&self, _text: &Text) -> Node { -    /// #             Node::new(Style::default()) +    /// #         fn layout( +    /// #             &self, +    /// #             _text: &Text, +    /// #             _limits: &layout::Limits, +    /// #         ) -> layout::Node { +    /// #             layout::Node::new(Size::ZERO)      /// #         }      /// #      /// #         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) {          self.widget.hash_layout(state);      } @@ -289,8 +299,12 @@ where      A: Clone,      Renderer: crate::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        self.widget.node(renderer) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        self.widget.layout(renderer, limits)      }      fn on_event( @@ -361,8 +375,12 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>  where      Renderer: crate::Renderer + renderer::Debugger,  { -    fn node(&self, renderer: &Renderer) -> Node { -        self.element.widget.node(renderer) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        self.element.widget.layout(renderer, limits)      }      fn on_event( diff --git a/native/src/layout.rs b/native/src/layout.rs index 32630f35..0a744346 100644 --- a/native/src/layout.rs +++ b/native/src/layout.rs @@ -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}; -/// 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)]  pub struct Layout<'a> { -    layout: &'a result::Layout,      position: Point, +    node: &'a Node,  }  impl<'a> Layout<'a> { -    pub(crate) fn new(layout: &'a result::Layout) -> Self { -        Self::with_parent_position(layout, Point::new(0.0, 0.0)) +    pub(crate) fn new(node: &'a Node) -> Self { +        Self::with_offset(Vector::new(0.0, 0.0), node)      } -    fn with_parent_position( -        layout: &'a result::Layout, -        parent_position: Point, -    ) -> Self { -        let position = -            parent_position + Vector::new(layout.location.x, layout.location.y); +    pub(crate) fn with_offset(offset: Vector, node: &'a Node) -> Self { +        let bounds = node.bounds(); -        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`]. -    /// -    /// [`Layout`]: struct.Layout.html -    /// [`Rectangle`]: struct.Rectangle.html -    /// [`Node`]: struct.Node.html      pub fn bounds(&self) -> Rectangle { +        let bounds = self.node.bounds(); +          Rectangle {              x: self.position.x,              y: self.position.y, -            width: self.layout.size.width, -            height: self.layout.size.height, +            width: bounds.width, +            height: bounds.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>> { -        self.layout.children.iter().map(move |layout| { -            Layout::with_parent_position(layout, self.position) +        self.node.children().iter().map(move |node| { +            Layout::with_offset( +                Vector::new(self.position.x, self.position.y), +                node, +            )          })      }  } diff --git a/native/src/layout/DRUID_LICENSE b/native/src/layout/DRUID_LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/native/src/layout/DRUID_LICENSE @@ -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. diff --git a/native/src/layout/flex.rs b/native/src/layout/flex.rs new file mode 100644 index 00000000..7a2b0d70 --- /dev/null +++ b/native/src/layout/flex.rs @@ -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, +    ) +} diff --git a/native/src/layout/limits.rs b/native/src/layout/limits.rs new file mode 100644 index 00000000..af269acd --- /dev/null +++ b/native/src/layout/limits.rs @@ -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), +        ) +    } +} diff --git a/native/src/layout/node.rs b/native/src/layout/node.rs new file mode 100644 index 00000000..64ebf2d0 --- /dev/null +++ b/native/src/layout/node.rs @@ -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; +            } +        } +    } +} diff --git a/native/src/lib.rs b/native/src/lib.rs index 56399e57..a091059e 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -77,7 +77,7 @@  //! #  //! # mod iced_wgpu {  //! #     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 {} @@ -87,11 +87,12 @@  //! #     }  //! #  //! #     impl button::Renderer for Renderer { -//! #         fn node<Message>( +//! #         fn layout<Message>(  //! #             &self, -//! #             _button: &Button<'_, Message, Self> -//! #         ) -> Node { -//! #             Node::new(Style::default()) +//! #             _button: &Button<'_, Message, Self>, +//! #             _limits: &layout::Limits, +//! #         ) -> layout::Node { +//! #             layout::Node::new(Size::ZERO)  //! #         }  //! #  //! #         fn draw<Message>( @@ -103,8 +104,12 @@  //! #     }  //! #  //! #     impl text::Renderer for Renderer { -//! #         fn node(&self, _text: &Text) -> Node { -//! #             Node::new(Style::default()) +//! #         fn layout( +//! #             &self, +//! #             _text: &Text, +//! #             _limits: &layout::Limits, +//! #         ) -> layout::Node { +//! #             layout::Node::new(Size::ZERO)  //! #         }  //! #  //! #         fn draw( @@ -199,32 +204,27 @@  #![deny(unsafe_code)]  #![deny(rust_2018_idioms)]  pub mod input; +pub mod layout;  pub mod renderer;  pub mod widget;  mod element;  mod event;  mod hasher; -mod layout;  mod mouse_cursor; -mod node; -mod style; +mod size;  mod user_interface;  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 event::Event;  pub use hasher::Hasher;  pub use layout::Layout;  pub use mouse_cursor::MouseCursor; -pub use node::Node;  pub use renderer::Renderer; -pub use style::Style; +pub use size::Size;  pub use user_interface::{Cache, UserInterface};  pub use widget::*; diff --git a/native/src/node.rs b/native/src/node.rs deleted file mode 100644 index 1db10d7f..00000000 --- a/native/src/node.rs +++ /dev/null @@ -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(), -        )) -    } -} diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 5963d577..833de571 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -26,6 +26,15 @@ mod windowed;  pub use debugger::Debugger;  pub use windowed::{Target, Windowed}; -pub trait Renderer { +use crate::{layout, Element}; + +pub trait Renderer: Sized {      type Output; + +    fn layout<'a, Message>( +        &mut self, +        element: &Element<'a, Message, Self>, +    ) -> layout::Node { +        element.layout(self, &layout::Limits::NONE) +    }  } diff --git a/native/src/size.rs b/native/src/size.rs new file mode 100644 index 00000000..bd909292 --- /dev/null +++ b/native/src/size.rs @@ -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, +        } +    } +} diff --git a/native/src/style.rs b/native/src/style.rs deleted file mode 100644 index 70a7ff74..00000000 --- a/native/src/style.rs +++ /dev/null @@ -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, -    } -} diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 0760dd7e..f031b090 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -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 stretch::{geometry, result};  /// A set of interactive graphical elements with a specific [`Layout`].  /// @@ -15,7 +14,7 @@ use stretch::{geometry, result};  pub struct UserInterface<'a, Message, Renderer> {      hash: u64,      root: Element<'a, Message, Renderer>, -    layout: result::Layout, +    layout: layout::Node,      cursor_position: Point,  } @@ -98,7 +97,7 @@ where      pub fn build<E: Into<Element<'a, Message, Renderer>>>(          root: E,          cache: Cache, -        renderer: &Renderer, +        renderer: &mut Renderer,      ) -> Self {          let root = root.into(); @@ -110,7 +109,11 @@ where          let layout = if hash == cache.hash {              cache.layout          } 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 { @@ -326,7 +329,7 @@ where  #[derive(Debug, Clone)]  pub struct Cache {      hash: u64, -    layout: result::Layout, +    layout: layout::Node,      cursor_position: Point,  } @@ -339,16 +342,9 @@ impl Cache {      /// [`Cache`]: struct.Cache.html      /// [`UserInterface`]: struct.UserInterface.html      pub fn new() -> Cache { -        use crate::{Node, Style}; - -        let empty_node = Node::new(Style::default()); -          Cache {              hash: 0, -            layout: empty_node -                .0 -                .compute_layout(geometry::Size::undefined()) -                .unwrap(), +            layout: layout::Node::new(Size::new(0.0, 0.0)),              cursor_position: Point::new(-1.0, -1.0),          }      } diff --git a/native/src/widget.rs b/native/src/widget.rs index 01f5c92e..9010b06f 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -31,6 +31,8 @@ pub mod slider;  pub mod text;  pub mod text_input; +mod container; +  #[doc(no_inline)]  pub use button::Button;  #[doc(no_inline)] @@ -38,6 +40,8 @@ pub use checkbox::Checkbox;  #[doc(no_inline)]  pub use column::Column;  #[doc(no_inline)] +pub use container::Container; +#[doc(no_inline)]  pub use image::Image;  #[doc(no_inline)]  pub use radio::Radio; @@ -52,7 +56,7 @@ pub use text::Text;  #[doc(no_inline)]  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.  /// @@ -73,7 +77,19 @@ where      /// [`Node`]: ../struct.Node.html      /// [`Widget`]: trait.Widget.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`.      /// diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 31dd6fcc..15beaeba 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -7,7 +7,7 @@  //! [`Class`]: enum.Class.html  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;  pub use iced_core::button::State; @@ -21,8 +21,12 @@ where      Renderer: self::Renderer,      Message: Clone + std::fmt::Debug,  { -    fn node(&self, renderer: &Renderer) -> Node { -        renderer.node(&self) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        renderer.layout(&self, limits)      }      fn on_event( @@ -74,7 +78,6 @@ where      fn hash_layout(&self, state: &mut Hasher) {          self.width.hash(state); -        self.align_self.hash(state);          self.content.hash_layout(state);      }  } @@ -91,7 +94,11 @@ pub trait Renderer: crate::Renderer + Sized {      ///      /// [`Node`]: ../../struct.Node.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`].      /// diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index b8053238..a7040e02 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -2,7 +2,7 @@  use std::hash::Hash;  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; @@ -10,8 +10,12 @@ impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>  where      Renderer: self::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        renderer.node(&self) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        renderer.layout(&self, limits)      }      fn on_event( @@ -63,7 +67,11 @@ pub trait Renderer: crate::Renderer {      ///      /// [`Node`]: ../../struct.Node.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`].      /// diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 086d05ef..7e7156a0 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -1,6 +1,6 @@  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.  pub type Column<'a, Message, Renderer> = @@ -11,42 +11,30 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>  where      Renderer: self::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        let mut children: Vec<Node> = self -            .children -            .iter() -            .map(|child| { -                let mut node = child.widget.node(renderer); - -                let mut style = node.0.style(); -                style.margin.bottom = -                    stretch::style::Dimension::Points(f32::from(self.spacing)); - -                node.0.set_style(style); -                node -            }) -            .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); -        } +    fn width(&self) -> Length { +        self.width +    } -        let mut style = Style::default() -            .width(self.width) -            .height(self.height) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        let limits = limits              .max_width(self.max_width)              .max_height(self.max_height) -            .padding(self.padding) -            .align_self(self.align_self) -            .align_items(self.align_items) -            .justify_content(self.justify_content); - -        style.0.flex_direction = stretch::style::FlexDirection::Column; - -        Node::with_children(style, children) +            .width(self.width) +            .height(self.height); + +        layout::flex::resolve( +            layout::flex::Axis::Vertical, +            renderer, +            &limits, +            self.padding as f32, +            self.spacing as f32, +            self.align_items, +            &self.children, +        )      }      fn on_event( @@ -85,9 +73,7 @@ where          self.height.hash(state);          self.max_width.hash(state);          self.max_height.hash(state); -        self.align_self.hash(state);          self.align_items.hash(state); -        self.justify_content.hash(state);          self.spacing.hash(state);          for child in &self.children { diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs new file mode 100644 index 00000000..c616db2a --- /dev/null +++ b/native/src/widget/container.rs @@ -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) +    } +} diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 6255a7b5..b2541b87 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -1,6 +1,6 @@  //! 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; @@ -10,8 +10,12 @@ impl<Message, Renderer> Widget<Message, Renderer> for Image  where      Renderer: self::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        renderer.node(&self) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        renderer.layout(&self, limits)      }      fn draw( @@ -26,7 +30,6 @@ where      fn hash_layout(&self, state: &mut Hasher) {          self.width.hash(state);          self.height.hash(state); -        self.align_self.hash(state);      }  } @@ -44,7 +47,7 @@ pub trait Renderer: crate::Renderer {      ///      /// [`Node`]: ../../struct.Node.html      /// [`Image`]: struct.Image.html -    fn node(&self, image: &Image) -> Node; +    fn layout(&self, image: &Image, limits: &layout::Limits) -> layout::Node;      /// Draws an [`Image`].      /// diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 626e6ffc..b68919e5 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -1,6 +1,6 @@  //! Create choices using radio buttons.  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; @@ -11,8 +11,16 @@ where      Renderer: self::Renderer,      Message: Clone + std::fmt::Debug,  { -    fn node(&self, renderer: &Renderer) -> Node { -        renderer.node(&self) +    fn width(&self) -> Length { +        Length::Fill +    } + +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        renderer.layout(&self, limits)      }      fn on_event( @@ -62,7 +70,11 @@ pub trait Renderer: crate::Renderer {      ///      /// [`Node`]: ../../struct.Node.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.      /// diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 7dbfb92a..132479fd 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -1,6 +1,6 @@  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.  pub type Row<'a, Message, Renderer> = @@ -11,42 +11,30 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>  where      Renderer: self::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        let mut children: Vec<Node> = self -            .children -            .iter() -            .map(|child| { -                let mut node = child.widget.node(renderer); - -                let mut style = node.0.style(); -                style.margin.end = -                    stretch::style::Dimension::Points(f32::from(self.spacing)); - -                node.0.set_style(style); -                node -            }) -            .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); -        } +    fn width(&self) -> Length { +        self.width +    } -        let mut style = Style::default() -            .width(self.width) -            .height(self.height) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        let limits = limits              .max_width(self.max_width)              .max_height(self.max_height) -            .padding(self.padding) -            .align_self(self.align_self) -            .align_items(self.align_items) -            .justify_content(self.justify_content); - -        style.0.flex_direction = stretch::style::FlexDirection::Row; - -        Node::with_children(style, children) +            .width(self.width) +            .height(self.height); + +        layout::flex::resolve( +            layout::flex::Axis::Horizontal, +            renderer, +            &limits, +            self.padding as f32, +            self.spacing as f32, +            self.align_items, +            &self.children, +        )      }      fn on_event( @@ -85,9 +73,7 @@ where          self.height.hash(state);          self.max_width.hash(state);          self.max_height.hash(state); -        self.align_self.hash(state);          self.align_items.hash(state); -        self.justify_content.hash(state);          self.spacing.hash(state);          self.spacing.hash(state); diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index de4c749c..091dac47 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -1,11 +1,13 @@  use crate::{      column,      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; +use std::f32;  use std::hash::Hash;  /// A scrollable [`Column`]. @@ -19,26 +21,25 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>  where      Renderer: self::Renderer + column::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        let mut content = self.content.node(renderer); - -        { -            let mut style = content.0.style(); -            style.flex_shrink = 0.0; - -            content.0.set_style(style); -        } - -        let mut style = Style::default() -            .width(self.content.width) -            .max_width(self.content.max_width) -            .height(self.height) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        let limits = limits              .max_height(self.max_height) -            .align_self(self.align_self); +            .width(Length::Fill) +            .height(self.height); + +        let child_limits = layout::Limits::new( +            Size::new(limits.min().width, 0.0), +            Size::new(limits.max().width, f32::INFINITY), +        ); -        style.0.flex_direction = stretch::style::FlexDirection::Column; +        let content = self.content.layout(renderer, &child_limits); +        let size = limits.resolve(content.size()); -        Node::with_children(style, vec![content]) +        layout::Node::with_children(size, vec![content])      }      fn on_event( @@ -167,7 +168,6 @@ where          self.height.hash(state);          self.max_height.hash(state); -        self.align_self.hash(state);          self.content.hash_layout(state)      } diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index be2b9b22..3a998c40 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -7,7 +7,7 @@  use std::hash::Hash;  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::*; @@ -15,8 +15,16 @@ impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message>  where      Renderer: self::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        renderer.node(&self) +    fn width(&self) -> Length { +        self.width +    } + +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        renderer.layout(&self, limits)      }      fn on_event( @@ -93,7 +101,11 @@ pub trait Renderer: crate::Renderer {      ///      /// [`Node`]: ../../struct.Node.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`].      /// diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index e389e1d9..10d892a3 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -1,5 +1,5 @@  //! 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; @@ -9,8 +9,16 @@ impl<Message, Renderer> Widget<Message, Renderer> for Text  where      Renderer: self::Renderer,  { -    fn node(&self, renderer: &Renderer) -> Node { -        renderer.node(&self) +    fn width(&self) -> Length { +        self.width +    } + +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        renderer.layout(&self, limits)      }      fn draw( @@ -49,7 +57,7 @@ pub trait Renderer: crate::Renderer {      /// [`Style`]: ../../struct.Style.html      /// [`Text`]: struct.Text.html      /// [`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.      /// diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index d9837b61..7e81e257 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -1,6 +1,6 @@  use crate::{      input::{keyboard, mouse, ButtonState}, -    Element, Event, Hasher, Layout, Length, Node, Point, Rectangle, Style, +    layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,      Widget,  }; @@ -11,19 +11,24 @@ where      Renderer: self::Renderer,      Message: Clone + std::fmt::Debug,  { -    fn node(&self, renderer: &Renderer) -> Node { -        let text_bounds = -            Node::new(Style::default().width(Length::Fill).height( -                Length::Units(self.size.unwrap_or(renderer.default_size())), -            )); - -        Node::with_children( -            Style::default() -                .width(self.width) -                .max_width(self.width) -                .padding(self.padding), -            vec![text_bounds], -        ) +    fn layout( +        &self, +        renderer: &Renderer, +        limits: &layout::Limits, +    ) -> layout::Node { +        let padding = self.padding as f32; +        let text_size = self.size.unwrap_or(renderer.default_size()); + +        let limits = limits +            .pad(padding) +            .width(self.width) +            .height(Length::Units(text_size)); + +        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( diff --git a/src/winit.rs b/src/winit.rs index 51ae1c1f..6da0cae0 100644 --- a/src/winit.rs +++ b/src/winit.rs @@ -2,11 +2,11 @@ pub use iced_wgpu::{Primitive, Renderer};  pub use iced_winit::{      button, scrollable, slider, text, text_input, winit, Align, Background, -    Checkbox, Color, Image, Justify, Length, Radio, Scrollable, Slider, Text, -    TextInput, +    Checkbox, Color, Image, Length, Radio, Scrollable, Slider, Text, TextInput,  };  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 Column<'a, Message> = iced_winit::Column<'a, Message, Renderer>;  pub type Button<'a, Message> = iced_winit::Button<'a, Message, Renderer>; diff --git a/web/src/lib.rs b/web/src/lib.rs index 559a5af0..6252f2be 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -7,7 +7,7 @@ pub mod widget;  pub use bus::Bus;  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 trait Application { diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 032f4ae0..3ebfa7ea 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/hecrj/iced"  [dependencies]  iced_native = { version = "0.1.0-alpha", path = "../native" }  wgpu = "0.4" +glyph_brush = "0.6"  wgpu_glyph = { version = "0.5", git = "https://github.com/hecrj/wgpu_glyph", branch = "feature/scissoring" }  raw-window-handle = "0.3"  image = "0.22" diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 235eefc6..e9df3550 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -9,9 +9,8 @@ use wgpu::{      Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions,      TextureFormat,  }; -use wgpu_glyph::{GlyphBrush, GlyphBrushBuilder, Section}; -use std::{cell::RefCell, rc::Rc}; +use std::cell::RefCell;  mod target;  mod widget; @@ -23,8 +22,8 @@ pub struct Renderer {      queue: Queue,      quad_pipeline: quad::Pipeline,      image_pipeline: crate::image::Pipeline, - -    glyph_brush: Rc<RefCell<GlyphBrush<'static, ()>>>, +    text_pipeline: wgpu_glyph::GlyphBrush<'static, ()>, +    text_measurements: RefCell<glyph_brush::GlyphBrush<'static, ()>>,  }  pub struct Layer<'a> { @@ -72,10 +71,17 @@ impl Renderer {              .load(&[font::Family::Monospace])              .expect("Find monospace font"); -        let glyph_brush = -            GlyphBrushBuilder::using_fonts_bytes(vec![default_font, mono_font]) -                .initial_cache_size((2048, 2048)) -                .build(&mut device, TextureFormat::Bgra8UnormSrgb); +        let text_pipeline = +            wgpu_glyph::GlyphBrushBuilder::using_fonts_bytes(vec![ +                default_font.clone(), +                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 image_pipeline = crate::image::Pipeline::new(&mut device); @@ -85,8 +91,8 @@ impl Renderer {              queue,              quad_pipeline,              image_pipeline, - -            glyph_brush: Rc::new(RefCell::new(glyph_brush)), +            text_pipeline, +            text_measurements: RefCell::new(text_measurements),          }      } @@ -190,7 +196,7 @@ impl Renderer {                      }                  }; -                layer.text.push(Section { +                layer.text.push(wgpu_glyph::Section {                      text: &content,                      screen_position: (                          x - layer.offset.x as f32, @@ -292,27 +298,26 @@ impl Renderer {          let first = layers.first().unwrap();          let mut overlay = Layer::new(first.bounds, Vector::new(0, 0)); -        let font_id = -            wgpu_glyph::FontId(self.glyph_brush.borrow().fonts().len() - 1); +        let font_id = wgpu_glyph::FontId(self.text_pipeline.fonts().len() - 1);          let scale = wgpu_glyph::Scale { x: 20.0, y: 20.0 };          for (i, line) in lines.iter().enumerate() { -            overlay.text.push(Section { +            overlay.text.push(wgpu_glyph::Section {                  text: line.as_ref(),                  screen_position: (11.0, 11.0 + 25.0 * i as f32),                  color: [0.9, 0.9, 0.9, 1.0],                  scale,                  font_id, -                ..Section::default() +                ..wgpu_glyph::Section::default()              }); -            overlay.text.push(Section { +            overlay.text.push(wgpu_glyph::Section {                  text: line.as_ref(),                  screen_position: (10.0, 10.0 + 25.0 * i as f32),                  color: [0.0, 0.0, 0.0, 1.0],                  scale,                  font_id, -                ..Section::default() +                ..wgpu_glyph::Section::default()              });          } @@ -360,11 +365,9 @@ impl Renderer {          }          if layer.text.len() > 0 { -            let mut glyph_brush = self.glyph_brush.borrow_mut(); -              for text in layer.text.iter() {                  // Target physical coordinates directly to avoid blurry text -                let text = Section { +                let text = wgpu_glyph::Section {                      screen_position: (                          (text.screen_position.0 * dpi).round(),                          (text.screen_position.1 * dpi).round(), @@ -377,10 +380,10 @@ impl Renderer {                      ..*text                  }; -                glyph_brush.queue(text); +                self.text_pipeline.queue(text);              } -            glyph_brush +            self.text_pipeline                  .draw_queued_with_transform_and_scissoring(                      &mut self.device,                      encoder, @@ -400,6 +403,25 @@ impl Renderer {  impl iced_native::Renderer for Renderer {      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 { @@ -438,7 +460,7 @@ impl Debugger for Renderer {  }  fn explain_layout( -    layout: Layout, +    layout: Layout<'_>,      color: Color,      primitives: &mut Vec<Primitive>,  ) { diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index 0ac1c0a6..3d5e42ba 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -1,19 +1,30 @@  use crate::{Primitive, Renderer};  use iced_native::{ -    button, Align, Background, Button, Layout, Length, MouseCursor, -    Node, Point, Rectangle, Style, +    button, layout, Background, Button, Layout, Length, MouseCursor, Point, +    Rectangle,  };  impl button::Renderer for Renderer { -    fn node<Message>(&self, button: &Button<Message, Self>) -> Node { -        let style = Style::default() +    fn layout<Message>( +        &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) -            .padding(button.padding) -            .min_width(Length::Units(100)) -            .align_self(button.align_self) -            .align_items(Align::Stretch); +            .height(Length::Shrink) +            .pad(padding); + +        let mut content = button.content.layout(self, &limits); + +        content.bounds.x = padding; +        content.bounds.y = padding; + +        let size = limits.resolve(content.size()).pad(padding); -        Node::with_children(style, vec![button.content.node(self)]) +        layout::Node::with_children(size, vec![content])      }      fn draw<Message>( diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs index 1594c769..c2d7911c 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/wgpu/src/renderer/widget/checkbox.rs @@ -1,16 +1,19 @@  use crate::{Primitive, Renderer};  use iced_native::{ -    checkbox, text, text::HorizontalAlignment, text::VerticalAlignment, Align, -    Background, Checkbox, Column, Layout, Length, MouseCursor, Node, -    Point, Rectangle, Row, Text, Widget, +    checkbox, layout, text, text::HorizontalAlignment, text::VerticalAlignment, +    Align, Background, Checkbox, Column, Layout, Length, MouseCursor, Point, +    Rectangle, Row, Text, Widget,  };  const SIZE: f32 = 28.0;  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() -            .width(Length::Fill)              .spacing(15)              .align_items(Align::Center)              .push( @@ -19,7 +22,7 @@ impl checkbox::Renderer for Renderer {                      .height(Length::Units(SIZE as u16)),              )              .push(Text::new(&checkbox.label)) -            .node(self) +            .layout(self, limits)      }      fn draw<Message>( diff --git a/wgpu/src/renderer/widget/image.rs b/wgpu/src/renderer/widget/image.rs index 0e312706..0afb11e3 100644 --- a/wgpu/src/renderer/widget/image.rs +++ b/wgpu/src/renderer/widget/image.rs @@ -1,25 +1,28 @@  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 { -    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 aspect_ratio = width as f32 / height as f32; -        let mut style = Style::default().align_self(image.align_self); -          // TODO: Deal with additional cases -        style = match (image.width, image.height) { -            (Length::Units(width), _) => style.width(image.width).height( +        let (width, height) = match (image.width, image.height) { +            (Length::Units(width), _) => ( +                image.width,                  Length::Units((width as f32 / aspect_ratio).round() as u16),              ), -            (_, _) => style -                .width(Length::Units(width as u16)) -                .height(Length::Units(height as u16)), +            (_, _) => { +                (Length::Units(width as u16), 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 { diff --git a/wgpu/src/renderer/widget/radio.rs b/wgpu/src/renderer/widget/radio.rs index 61f5ce47..1f17ba28 100644 --- a/wgpu/src/renderer/widget/radio.rs +++ b/wgpu/src/renderer/widget/radio.rs @@ -1,14 +1,18 @@  use crate::{Primitive, Renderer};  use iced_native::{ -    radio, text, Align, Background, Column, Layout, Length, MouseCursor, -    Node, Point, Radio, Rectangle, Row, Text, Widget, +    layout, radio, text, Align, Background, Column, Layout, Length, +    MouseCursor, Point, Radio, Rectangle, Row, Text, Widget,  };  const SIZE: f32 = 28.0;  const DOT_SIZE: f32 = SIZE / 2.0;  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()              .spacing(15)              .align_items(Align::Center) @@ -18,7 +22,7 @@ impl radio::Renderer for Renderer {                      .height(Length::Units(SIZE as u16)),              )              .push(Text::new(&radio.label)) -            .node(self) +            .layout(self, limits)      }      fn draw<Message>( diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index 5eadf275..dd6ebcc1 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -1,7 +1,7 @@  use crate::{Primitive, Renderer};  use iced_native::{ -    scrollable, Background, Layout, MouseCursor, Point, Rectangle, -    Scrollable, Vector, Widget, +    scrollable, Background, Layout, MouseCursor, Point, Rectangle, Scrollable, +    Vector, Widget,  };  const SCROLLBAR_WIDTH: u16 = 10; diff --git a/wgpu/src/renderer/widget/slider.rs b/wgpu/src/renderer/widget/slider.rs index 789e7bd4..98065bc9 100644 --- a/wgpu/src/renderer/widget/slider.rs +++ b/wgpu/src/renderer/widget/slider.rs @@ -1,20 +1,22 @@  use crate::{Primitive, Renderer};  use iced_native::{ -    slider, Background, Color, Layout, Length, MouseCursor, Node, Point, -    Rectangle, Slider, Style, +    layout, slider, Background, Color, Layout, Length, MouseCursor, Point, +    Rectangle, Size, Slider,  };  const HANDLE_WIDTH: f32 = 8.0;  const HANDLE_HEIGHT: f32 = 22.0;  impl slider::Renderer for Renderer { -    fn node<Message>(&self, slider: &Slider<Message>) -> Node { -        let style = Style::default() -            .width(slider.width) -            .height(Length::Units(HANDLE_HEIGHT as u16)) -            .min_width(Length::Units(100)); +    fn layout<Message>( +        &self, +        slider: &Slider<Message>, +        limits: &layout::Limits, +    ) -> 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>( diff --git a/wgpu/src/renderer/widget/text.rs b/wgpu/src/renderer/widget/text.rs index 29e07ff7..b9ccd787 100644 --- a/wgpu/src/renderer/widget/text.rs +++ b/wgpu/src/renderer/widget/text.rs @@ -1,73 +1,37 @@  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 std::cell::RefCell;  use std::f32;  // TODO: Obtain from renderer configuration  const DEFAULT_TEXT_SIZE: f32 = 20.0;  impl text::Renderer for Renderer { -    fn node(&self, text: &Text) -> Node { -        let glyph_brush = self.glyph_brush.clone(); -        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); +    fn layout(&self, text: &Text, limits: &layout::Limits) -> layout::Node { +        let limits = limits.width(text.width).height(text.height);          let size = text.size.map(f32::from).unwrap_or(DEFAULT_TEXT_SIZE); - -        let style = Style::default().width(text.width); - -        iced_native::Node::with_measure(style, move |bounds| { -            let mut measure = measure.borrow_mut(); - -            if measure.is_none() { -                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 { -                    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() -        }) +        let bounds = limits.max(); + +        let section = Section { +            text: &text.content, +            scale: wgpu_glyph::Scale { x: size, y: size }, +            bounds: (bounds.width, bounds.height), +            ..Default::default() +        }; + +        let (width, height) = if let Some(bounds) = +            self.text_measurements.borrow_mut().glyph_bounds(§ion) +        { +            (bounds.width().ceil(), bounds.height().ceil()) +        } else { +            (0.0, 0.0) +        }; + +        let size = limits.resolve(Size::new(width, height)); + +        layout::Node::new(size)      }      fn draw(&mut self, text: &Text, layout: Layout<'_>) -> Self::Output { diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs index b5f6c5f6..75eb20f7 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/wgpu/src/renderer/widget/text_input.rs @@ -78,7 +78,7 @@ impl text_input::Renderer for Renderer {                  .to_string();              let mut text_value_width = self -                .glyph_brush +                .text_measurements                  .borrow_mut()                  .glyph_bounds(Section {                      text: text_before_cursor, @@ -94,7 +94,7 @@ impl text_input::Renderer for Renderer {              if spaces_at_the_end > 0 {                  let space_width = { -                    let glyph_brush = self.glyph_brush.borrow(); +                    let glyph_brush = self.text_measurements.borrow();                      // TODO: Select appropriate font                      let font = &glyph_brush.fonts()[0]; diff --git a/winit/src/application.rs b/winit/src/application.rs index 4deffecc..331bafa0 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1,12 +1,13 @@  use crate::{ -    column, conversion, +    conversion,      input::{keyboard, mouse},      renderer::{Target, Windowed}, -    Cache, Column, Debug, Element, Event, Length, MouseCursor, UserInterface, +    Cache, Container, Debug, Element, Event, Length, MouseCursor, +    UserInterface,  };  pub trait Application { -    type Renderer: Windowed + column::Renderer; +    type Renderer: Windowed;      type Message: std::fmt::Debug; @@ -60,7 +61,7 @@ pub trait Application {          let user_interface = UserInterface::build(              document(&mut self, size, &mut debug),              Cache::default(), -            &renderer, +            &mut renderer,          );          debug.layout_finished(); @@ -86,7 +87,7 @@ pub trait Application {                  let mut user_interface = UserInterface::build(                      document(&mut self, size, &mut debug),                      cache.take().unwrap(), -                    &renderer, +                    &mut renderer,                  );                  debug.layout_finished(); @@ -129,7 +130,7 @@ pub trait Application {                      let user_interface = UserInterface::build(                          document(&mut self, size, &mut debug),                          temp_cache, -                        &renderer, +                        &mut renderer,                      );                      debug.layout_finished(); @@ -282,9 +283,8 @@ where      let view = application.view();      debug.view_finished(); -    Column::new() +    Container::new(view)          .width(Length::Units(size.width.round() as u16))          .height(Length::Units(size.height.round() as u16)) -        .push(view)          .into()  }  | 
