diff options
| author | 2019-09-20 19:15:31 +0200 | |
|---|---|---|
| committer | 2019-09-20 19:15:31 +0200 | |
| commit | b9e0f7494881ad7cdfbcbc16878ecc6ef717753f (patch) | |
| tree | c8a7419b5cb4c0161306c479b93038f2f86498c2 /native/src | |
| parent | b83a4b42dd912b5f59d40e7d4f7f7ccdabc43019 (diff) | |
| download | iced-b9e0f7494881ad7cdfbcbc16878ecc6ef717753f.tar.gz iced-b9e0f7494881ad7cdfbcbc16878ecc6ef717753f.tar.bz2 iced-b9e0f7494881ad7cdfbcbc16878ecc6ef717753f.zip  | |
Create `iced_core` and `iced_native`
Diffstat (limited to '')
| -rw-r--r-- | native/src/element.rs (renamed from src/element.rs) | 20 | ||||
| -rw-r--r-- | native/src/event.rs (renamed from src/event.rs) | 0 | ||||
| -rw-r--r-- | native/src/hasher.rs (renamed from src/hasher.rs) | 0 | ||||
| -rw-r--r-- | native/src/input.rs (renamed from src/input.rs) | 0 | ||||
| -rw-r--r-- | native/src/input/button_state.rs (renamed from src/input/button_state.rs) | 0 | ||||
| -rw-r--r-- | native/src/input/keyboard.rs (renamed from src/input/keyboard.rs) | 0 | ||||
| -rw-r--r-- | native/src/input/keyboard/event.rs (renamed from src/input/keyboard/event.rs) | 0 | ||||
| -rw-r--r-- | native/src/input/keyboard/key_code.rs (renamed from src/input/keyboard/key_code.rs) | 0 | ||||
| -rw-r--r-- | native/src/input/mouse.rs (renamed from src/input/mouse.rs) | 0 | ||||
| -rw-r--r-- | native/src/input/mouse/button.rs (renamed from src/input/mouse/button.rs) | 0 | ||||
| -rw-r--r-- | native/src/input/mouse/event.rs (renamed from src/input/mouse/event.rs) | 0 | ||||
| -rw-r--r-- | native/src/layout.rs (renamed from src/layout.rs) | 2 | ||||
| -rw-r--r-- | native/src/lib.rs | 228 | ||||
| -rw-r--r-- | native/src/mouse_cursor.rs (renamed from src/mouse_cursor.rs) | 0 | ||||
| -rw-r--r-- | native/src/node.rs (renamed from src/node.rs) | 0 | ||||
| -rw-r--r-- | native/src/renderer.rs (renamed from src/renderer.rs) | 0 | ||||
| -rw-r--r-- | native/src/style.rs | 170 | ||||
| -rw-r--r-- | native/src/user_interface.rs (renamed from src/user_interface.rs) | 12 | ||||
| -rw-r--r-- | native/src/widget.rs (renamed from src/widget.rs) | 12 | ||||
| -rw-r--r-- | native/src/widget/button.rs | 111 | ||||
| -rw-r--r-- | native/src/widget/checkbox.rs | 95 | ||||
| -rw-r--r-- | native/src/widget/column.rs | 118 | ||||
| -rw-r--r-- | native/src/widget/image.rs | 67 | ||||
| -rw-r--r-- | native/src/widget/radio.rs | 92 | ||||
| -rw-r--r-- | native/src/widget/row.rs | 117 | ||||
| -rw-r--r-- | native/src/widget/slider.rs | 126 | ||||
| -rw-r--r-- | native/src/widget/text.rs | 77 | 
27 files changed, 1228 insertions, 19 deletions
diff --git a/src/element.rs b/native/src/element.rs index f6276fbf..dd5ce621 100644 --- a/src/element.rs +++ b/native/src/element.rs @@ -87,7 +87,7 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {      ///      /// ```      /// # mod counter { -    /// #     use iced::{button, Button}; +    /// #     use iced_native::{button, Button};      /// #      /// #     #[derive(Debug, Clone, Copy)]      /// #     pub enum Message {} @@ -101,19 +101,21 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {      /// # }      /// #      /// # mod iced_wgpu { -    /// #     use iced::{ -    /// #         button, MouseCursor, Node, Point, Rectangle, Style, +    /// #     use iced_native::{ +    /// #         button, Button, MouseCursor, Node, Point, Rectangle, Style, Layout      /// #     };      /// #     pub struct Renderer;      /// #      /// #     impl button::Renderer for Renderer { -    /// #         fn draw( +    /// #         fn node<Message>(&self, _button: &Button<'_, Message>) -> Node { +    /// #             Node::new(Style::default()) +    /// #         } +    /// # +    /// #         fn draw<Message>(      /// #             &mut self, +    /// #             _button: &Button<'_, Message>, +    /// #             _layout: Layout<'_>,      /// #             _cursor_position: Point, -    /// #             _bounds: Rectangle, -    /// #             _state: &button::State, -    /// #             _label: &str, -    /// #             _class: button::Class,      /// #         ) -> MouseCursor {      /// #             MouseCursor::OutOfBounds      /// #         } @@ -130,7 +132,7 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {      /// # pub enum Message {      /// #    Counter(usize, counter::Message)      /// # } -    /// use iced::{Element, Row}; +    /// use iced_native::{Element, Row};      /// use iced_wgpu::Renderer;      ///      /// impl ManyCounters { diff --git a/src/event.rs b/native/src/event.rs index 71f06006..71f06006 100644 --- a/src/event.rs +++ b/native/src/event.rs diff --git a/src/hasher.rs b/native/src/hasher.rs index 9f6aacce..9f6aacce 100644 --- a/src/hasher.rs +++ b/native/src/hasher.rs diff --git a/src/input.rs b/native/src/input.rs index 097fa730..097fa730 100644 --- a/src/input.rs +++ b/native/src/input.rs diff --git a/src/input/button_state.rs b/native/src/input/button_state.rs index e9dc05d7..e9dc05d7 100644 --- a/src/input/button_state.rs +++ b/native/src/input/button_state.rs diff --git a/src/input/keyboard.rs b/native/src/input/keyboard.rs index 57c24484..57c24484 100644 --- a/src/input/keyboard.rs +++ b/native/src/input/keyboard.rs diff --git a/src/input/keyboard/event.rs b/native/src/input/keyboard/event.rs index 8118f112..8118f112 100644 --- a/src/input/keyboard/event.rs +++ b/native/src/input/keyboard/event.rs diff --git a/src/input/keyboard/key_code.rs b/native/src/input/keyboard/key_code.rs index 207ddeac..207ddeac 100644 --- a/src/input/keyboard/key_code.rs +++ b/native/src/input/keyboard/key_code.rs diff --git a/src/input/mouse.rs b/native/src/input/mouse.rs index d37f5b96..d37f5b96 100644 --- a/src/input/mouse.rs +++ b/native/src/input/mouse.rs diff --git a/src/input/mouse/button.rs b/native/src/input/mouse/button.rs index 6320d701..6320d701 100644 --- a/src/input/mouse/button.rs +++ b/native/src/input/mouse/button.rs diff --git a/src/input/mouse/event.rs b/native/src/input/mouse/event.rs index 7b68208f..7b68208f 100644 --- a/src/input/mouse/event.rs +++ b/native/src/input/mouse/event.rs diff --git a/src/layout.rs b/native/src/layout.rs index de284a43..32630f35 100644 --- a/src/layout.rs +++ b/native/src/layout.rs @@ -12,7 +12,7 @@ use crate::{Point, Rectangle, Vector};  /// [`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)] +#[derive(Debug, Clone, Copy)]  pub struct Layout<'a> {      layout: &'a result::Layout,      position: Point, diff --git a/native/src/lib.rs b/native/src/lib.rs new file mode 100644 index 00000000..39da4943 --- /dev/null +++ b/native/src/lib.rs @@ -0,0 +1,228 @@ +//! Iced is a renderer-agnostic GUI library focused on simplicity and +//! type-safety. Inspired by [Elm]. +//! +//! # Features +//!   * Simple, easy-to-use, renderer-agnostic API +//!   * Responsive, flexbox-based layouting +//!   * Type-safe, reactive programming model +//!   * Built-in widgets +//!   * Custom widget support +//! +//! Check out the [repository] and the [examples] for more details! +//! +//! [examples]: https://github.com/hecrj/iced/tree/0.1.0/examples +//! [repository]: https://github.com/hecrj/iced +//! +//! # Usage +//! Inspired by [The Elm Architecture], Iced expects you to split user interfaces +//! into four different concepts: +//! +//!   * __State__ — the state of your application +//!   * __Messages__ — user interactions or meaningful events that you care +//!   about +//!   * __View logic__ — a way to display your __state__ as widgets that +//!   may produce __messages__ on user interaction +//!   * __Update logic__ — a way to react to __messages__ and update your +//!   __state__ +//! +//! We can build something to see how this works! Let's say we want a simple counter +//! that can be incremented and decremented using two buttons. +//! +//! We start by modelling the __state__ of our application: +//! +//! ``` +//! use iced_native::button; +//! +//! struct Counter { +//!     // The counter value +//!     value: i32, +//! +//!     // The local state of the two buttons +//!     increment_button: button::State, +//!     decrement_button: button::State, +//! } +//! ``` +//! +//! Next, we need to define the possible user interactions of our counter: +//! the button presses. These interactions are our __messages__: +//! +//! ``` +//! #[derive(Debug, Clone, Copy)] +//! pub enum Message { +//!     IncrementPressed, +//!     DecrementPressed, +//! } +//! ``` +//! +//! Now, let's show the actual counter by putting it all together in our +//! __view logic__: +//! +//! ``` +//! # use iced_native::button; +//! # +//! # struct Counter { +//! #     // The counter value +//! #     value: i32, +//! # +//! #     // The local state of the two buttons +//! #     increment_button: button::State, +//! #     decrement_button: button::State, +//! # } +//! # +//! # #[derive(Debug, Clone, Copy)] +//! # pub enum Message { +//! #     IncrementPressed, +//! #     DecrementPressed, +//! # } +//! # +//! # mod iced_wgpu { +//! #     use iced_native::{ +//! #         button, text, Button, Text, +//! #         MouseCursor, Node, Point, Rectangle, Style, Color, Layout +//! #     }; +//! # +//! #     pub struct Renderer {} +//! # +//! #     impl button::Renderer for Renderer { +//! #         fn node<Message>( +//! #             &self, +//! #             _button: &Button<'_, Message> +//! #         ) -> Node { +//! #             Node::new(Style::default()) +//! #         } +//! # +//! #         fn draw<Message>( +//! #             &mut self, +//! #             _button: &Button<'_, Message>, +//! #             _layout: Layout<'_>, +//! #             _cursor_position: Point, +//! #         ) -> MouseCursor { +//! #             MouseCursor::OutOfBounds +//! #         } +//! #     } +//! # +//! #     impl text::Renderer for Renderer { +//! #         fn node(&self, _text: &Text) -> Node { +//! #             Node::new(Style::default()) +//! #         } +//! # +//! #         fn draw( +//! #             &mut self, +//! #             _text: &Text, +//! #             _layout: Layout<'_>, +//! #         ) { +//! #         } +//! #     } +//! # } +//! use iced_native::{Button, Column, Text}; +//! use iced_wgpu::Renderer; // Iced does not include a renderer! We need to bring our own! +//! +//! impl Counter { +//!     pub fn view(&mut self) -> Column<Message, Renderer> { +//!         // We use a column: a simple vertical layout +//!         Column::new() +//!             .push( +//!                 // The increment button. We tell it to produce an +//!                 // `IncrementPressed` message when pressed +//!                 Button::new(&mut self.increment_button, "+") +//!                     .on_press(Message::IncrementPressed), +//!             ) +//!             .push( +//!                 // We show the value of the counter here +//!                 Text::new(&self.value.to_string()).size(50), +//!             ) +//!             .push( +//!                 // The decrement button. We tell it to produce a +//!                 // `DecrementPressed` message when pressed +//!                 Button::new(&mut self.decrement_button, "-") +//!                     .on_press(Message::DecrementPressed), +//!             ) +//!     } +//! } +//! ``` +//! +//! Finally, we need to be able to react to any produced __messages__ and change +//! our __state__ accordingly in our __update logic__: +//! +//! ``` +//! # use iced_native::button; +//! # +//! # struct Counter { +//! #     // The counter value +//! #     value: i32, +//! # +//! #     // The local state of the two buttons +//! #     increment_button: button::State, +//! #     decrement_button: button::State, +//! # } +//! # +//! # #[derive(Debug, Clone, Copy)] +//! # pub enum Message { +//! #     IncrementPressed, +//! #     DecrementPressed, +//! # } +//! impl Counter { +//!     // ... +//! +//!     pub fn update(&mut self, message: Message) { +//!         match message { +//!             Message::IncrementPressed => { +//!                 self.value += 1; +//!             } +//!             Message::DecrementPressed => { +//!                 self.value -= 1; +//!             } +//!         } +//!     } +//! } +//! ``` +//! +//! And that's everything! We just wrote a whole user interface. Iced is now able +//! to: +//! +//!   1. Take the result of our __view logic__ and layout its widgets. +//!   1. Process events from our system and produce __messages__ for our +//!      __update logic__. +//!   1. Draw the resulting user interface using our chosen __renderer__. +//! +//! Check out the [`UserInterface`] type to learn how to wire everything up! +//! +//! [Elm]: https://elm-lang.org/ +//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/ +//! [documentation]: https://docs.rs/iced +//! [examples]: https://github.com/hecrj/iced/tree/master/examples +//! [`UserInterface`]: struct.UserInterface.html +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![deny(unused_results)] +#![deny(unsafe_code)] +#![deny(rust_2018_idioms)] +pub mod input; +pub mod renderer; +pub mod widget; + +mod element; +mod event; +mod hasher; +mod layout; +mod mouse_cursor; +mod node; +mod style; +mod user_interface; + +pub(crate) use iced_core::Vector; + +pub use iced_core::{Align, Color, Justify, Length, Point, Rectangle}; + +#[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 style::Style; +pub use user_interface::{Cache, UserInterface}; +pub use widget::*; diff --git a/src/mouse_cursor.rs b/native/src/mouse_cursor.rs index 4ef6361a..4ef6361a 100644 --- a/src/mouse_cursor.rs +++ b/native/src/mouse_cursor.rs diff --git a/src/node.rs b/native/src/node.rs index 1db10d7f..1db10d7f 100644 --- a/src/node.rs +++ b/native/src/node.rs diff --git a/src/renderer.rs b/native/src/renderer.rs index 2244f00b..2244f00b 100644 --- a/src/renderer.rs +++ b/native/src/renderer.rs diff --git a/native/src/style.rs b/native/src/style.rs new file mode 100644 index 00000000..3a75c925 --- /dev/null +++ b/native/src/style.rs @@ -0,0 +1,170 @@ +use crate::{Align, Justify, Length}; + +use std::hash::{Hash, Hasher}; +use stretch::{geometry, style}; + +/// The appearance of a [`Node`]. +/// +/// [`Node`]: struct.Node.html +#[derive(Debug, Clone, Copy)] +pub struct Style(pub(crate) style::Style); + +impl Style { +    /// Defines the width of a [`Node`]. +    /// +    /// [`Node`]: struct.Node.html +    pub fn width(mut self, width: Length) -> Self { +        self.0.size.width = length_to_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 = length_to_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 = length_to_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 = length_to_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 = length_to_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 = length_to_dimension(max_height); +        self +    } + +    pub(crate) fn align_items(mut self, align: Align) -> Self { +        self.0.align_items = align.into(); +        self +    } + +    pub(crate) fn justify_content(mut self, justify: Justify) -> Self { +        self.0.justify_content = justify.into(); +        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) => align.into(), +            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 length_to_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), +    } +} + +impl Default for Style { +    fn default() -> Style { +        Style(style::Style { +            align_items: style::AlignItems::FlexStart, +            justify_content: style::JustifyContent::FlexStart, +            ..style::Style::default() +        }) +    } +} + +impl Hash for Style { +    fn hash<H: Hasher>(&self, state: &mut H) { +        hash_size(&self.0.size, state); +        hash_size(&self.0.min_size, state); +        hash_size(&self.0.max_size, state); + +        hash_rect(&self.0.margin, state); + +        (self.0.flex_direction as u8).hash(state); +        (self.0.align_items as u8).hash(state); +        (self.0.justify_content as u8).hash(state); +        (self.0.align_self as u8).hash(state); +        (self.0.flex_grow as u32).hash(state); +    } +} + +fn hash_size<H: Hasher>( +    size: &geometry::Size<style::Dimension>, +    state: &mut H, +) { +    hash_dimension(size.width, state); +    hash_dimension(size.height, state); +} + +fn hash_rect<H: Hasher>( +    rect: &geometry::Rect<style::Dimension>, +    state: &mut H, +) { +    hash_dimension(rect.start, state); +    hash_dimension(rect.end, state); +    hash_dimension(rect.top, state); +    hash_dimension(rect.bottom, state); +} + +fn hash_dimension<H: Hasher>(dimension: style::Dimension, state: &mut H) { +    match dimension { +        style::Dimension::Undefined => state.write_u8(0), +        style::Dimension::Auto => state.write_u8(1), +        style::Dimension::Points(points) => { +            state.write_u8(2); +            (points as u32).hash(state); +        } +        style::Dimension::Percent(percent) => { +            state.write_u8(3); +            (percent as u32).hash(state); +        } +    } +} diff --git a/src/user_interface.rs b/native/src/user_interface.rs index 6a69f81a..4bfacb2e 100644 --- a/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -35,7 +35,7 @@ impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer> {      /// is naive way to set up our application loop:      ///      /// ```no_run -    /// use iced::{UserInterface, Cache}; +    /// use iced_native::{UserInterface, Cache};      /// use iced_wgpu::Renderer;      ///      /// # mod iced_wgpu { @@ -46,7 +46,7 @@ impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer> {      /// #     }      /// # }      /// # -    /// # use iced::Column; +    /// # use iced_native::Column;      /// #      /// # pub struct Counter;      /// # @@ -118,7 +118,7 @@ impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer> {      /// [the previous example](#example):      ///      /// ```no_run -    /// use iced::{UserInterface, Cache}; +    /// use iced_native::{UserInterface, Cache};      /// use iced_wgpu::Renderer;      ///      /// # mod iced_wgpu { @@ -129,7 +129,7 @@ impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer> {      /// #     }      /// # }      /// # -    /// # use iced::Column; +    /// # use iced_native::Column;      /// #      /// # pub struct Counter;      /// # @@ -203,7 +203,7 @@ impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer> {      /// [completing the last example](#example-1):      ///      /// ```no_run -    /// use iced::{UserInterface, Cache}; +    /// use iced_native::{UserInterface, Cache};      /// use iced_wgpu::Renderer;      ///      /// # mod iced_wgpu { @@ -214,7 +214,7 @@ impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer> {      /// #     }      /// # }      /// # -    /// # use iced::Column; +    /// # use iced_native::Column;      /// #      /// # pub struct Counter;      /// # diff --git a/src/widget.rs b/native/src/widget.rs index 45451f47..9b770454 100644 --- a/src/widget.rs +++ b/native/src/widget.rs @@ -15,7 +15,7 @@  //! module. Therefore, you can directly type:  //!  //! ``` -//! use iced::{button, Button, Widget}; +//! use iced_native::{button, Button, Widget};  //! ```  //!  //! [`Widget`]: trait.Widget.html @@ -26,19 +26,25 @@ mod row;  pub mod button;  pub mod checkbox;  pub mod image; -//pub mod progress_bar;  pub mod radio;  pub mod slider;  pub mod text; +#[doc(no_inline)]  pub use button::Button; +#[doc(no_inline)]  pub use checkbox::Checkbox; +#[doc(no_inline)]  pub use column::Column; +#[doc(no_inline)]  pub use image::Image; -//pub use progress_bar::ProgressBar; +#[doc(no_inline)]  pub use radio::Radio; +#[doc(no_inline)]  pub use row::Row; +#[doc(no_inline)]  pub use slider::Slider; +#[doc(no_inline)]  pub use text::Text;  use crate::{Event, Hasher, Layout, MouseCursor, Node, Point}; diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs new file mode 100644 index 00000000..c9436dc4 --- /dev/null +++ b/native/src/widget/button.rs @@ -0,0 +1,111 @@ +//! Allow your users to perform actions by pressing a button. +//! +//! A [`Button`] has some local [`State`] and a [`Class`]. +//! +//! [`Button`]: struct.Button.html +//! [`State`]: struct.State.html +//! [`Class`]: enum.Class.html + +use crate::input::{mouse, ButtonState}; +use crate::{ +    Element, Event, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Widget, +}; +use std::hash::Hash; + +pub use iced_core::button::*; + +impl<'a, Message, Renderer> Widget<Message, Renderer> for Button<'a, Message> +where +    Renderer: self::Renderer, +    Message: Copy + std::fmt::Debug, +{ +    fn node(&self, renderer: &mut Renderer) -> Node { +        renderer.node(&self) +    } + +    fn on_event( +        &mut self, +        event: Event, +        layout: Layout<'_>, +        cursor_position: Point, +        messages: &mut Vec<Message>, +    ) { +        match event { +            Event::Mouse(mouse::Event::Input { +                button: mouse::Button::Left, +                state, +            }) => { +                if let Some(on_press) = self.on_press { +                    let bounds = layout.bounds(); + +                    match state { +                        ButtonState::Pressed => { +                            self.state.is_pressed = +                                bounds.contains(cursor_position); +                        } +                        ButtonState::Released => { +                            let is_clicked = self.state.is_pressed +                                && bounds.contains(cursor_position); + +                            self.state.is_pressed = false; + +                            if is_clicked { +                                messages.push(on_press); +                            } +                        } +                    } +                } +            } +            _ => {} +        } +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor { +        renderer.draw(&self, layout, cursor_position) +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        self.width.hash(state); +    } +} + +/// The renderer of a [`Button`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Button`] in your user interface. +/// +/// [`Button`]: struct.Button.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer { +    /// Creates a [`Node`] for the provided [`Button`]. +    /// +    /// [`Node`]: ../../struct.Node.html +    /// [`Button`]: struct.Button.html +    fn node<Message>(&self, button: &Button<'_, Message>) -> Node; + +    /// Draws a [`Button`]. +    /// +    /// [`Button`]: struct.Button.html +    fn draw<Message>( +        &mut self, +        button: &Button<'_, Message>, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor; +} + +impl<'a, Message, Renderer> From<Button<'a, Message>> +    for Element<'a, Message, Renderer> +where +    Renderer: self::Renderer, +    Message: 'static + Copy + std::fmt::Debug, +{ +    fn from(button: Button<'a, Message>) -> Element<'a, Message, Renderer> { +        Element::new(button) +    } +} diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs new file mode 100644 index 00000000..3e307f64 --- /dev/null +++ b/native/src/widget/checkbox.rs @@ -0,0 +1,95 @@ +//! Show toggle controls using checkboxes. +use std::hash::Hash; + +use crate::input::{mouse, ButtonState}; +use crate::{Element, Event, Hasher, Layout, MouseCursor, Node, Point, Widget}; + +pub use iced_core::Checkbox; + +impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message> +where +    Renderer: self::Renderer, +{ +    fn node(&self, renderer: &mut Renderer) -> Node { +        renderer.node(&self) +    } + +    fn on_event( +        &mut self, +        event: Event, +        layout: Layout<'_>, +        cursor_position: Point, +        messages: &mut Vec<Message>, +    ) { +        match event { +            Event::Mouse(mouse::Event::Input { +                button: mouse::Button::Left, +                state: ButtonState::Pressed, +            }) => { +                let mouse_over = layout +                    .children() +                    .any(|child| child.bounds().contains(cursor_position)); + +                if mouse_over { +                    messages.push((self.on_toggle)(!self.is_checked)); +                } +            } +            _ => {} +        } +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor { +        renderer.draw(&self, layout, cursor_position) +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        self.label.hash(state); +    } +} + +/// The renderer of a [`Checkbox`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Checkbox`] in your user interface. +/// +/// [`Checkbox`]: struct.Checkbox.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer { +    /// Creates a [`Node`] for the provided [`Checkbox`]. +    /// +    /// [`Node`]: ../../struct.Node.html +    /// [`Checkbox`]: struct.Checkbox.html +    fn node<Message>(&mut self, checkbox: &Checkbox<Message>) -> Node; + +    /// Draws a [`Checkbox`]. +    /// +    /// It receives: +    ///   * the current cursor position +    ///   * the bounds of the [`Checkbox`] +    ///   * the bounds of the label of the [`Checkbox`] +    ///   * whether the [`Checkbox`] is checked or not +    /// +    /// [`Checkbox`]: struct.Checkbox.html +    fn draw<Message>( +        &mut self, +        checkbox: &Checkbox<Message>, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor; +} + +impl<'a, Message, Renderer> From<Checkbox<Message>> +    for Element<'a, Message, Renderer> +where +    Renderer: self::Renderer, +    Message: 'static, +{ +    fn from(checkbox: Checkbox<Message>) -> Element<'a, Message, Renderer> { +        Element::new(checkbox) +    } +} diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs new file mode 100644 index 00000000..9da2e161 --- /dev/null +++ b/native/src/widget/column.rs @@ -0,0 +1,118 @@ +use std::hash::Hash; + +use crate::{ +    Element, Event, Hasher, Layout, MouseCursor, Node, Point, Style, Widget, +}; + +/// A container that distributes its contents vertically. +pub type Column<'a, Message, Renderer> = +    iced_core::Column<Element<'a, Message, Renderer>>; + +impl<'a, Message, Renderer> Widget<Message, Renderer> +    for Column<'a, Message, Renderer> +{ +    fn node(&self, renderer: &mut 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); +        } + +        let mut style = Style::default() +            .width(self.width) +            .height(self.height) +            .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) +    } + +    fn on_event( +        &mut self, +        event: Event, +        layout: Layout<'_>, +        cursor_position: Point, +        messages: &mut Vec<Message>, +    ) { +        self.children.iter_mut().zip(layout.children()).for_each( +            |(child, layout)| { +                child +                    .widget +                    .on_event(event, layout, cursor_position, messages) +            }, +        ); +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor { +        let mut cursor = MouseCursor::OutOfBounds; + +        self.children.iter().zip(layout.children()).for_each( +            |(child, layout)| { +                let new_cursor = +                    child.widget.draw(renderer, layout, cursor_position); + +                if new_cursor != MouseCursor::OutOfBounds { +                    cursor = new_cursor; +                } +            }, +        ); + +        cursor +    } + +    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.align_self.hash(state); +        self.align_items.hash(state); +        self.justify_content.hash(state); +        self.spacing.hash(state); + +        for child in &self.children { +            child.widget.hash_layout(state); +        } +    } +} + +impl<'a, Message, Renderer> From<Column<'a, Message, Renderer>> +    for Element<'a, Message, Renderer> +where +    Renderer: 'a, +    Message: 'static, +{ +    fn from( +        column: Column<'a, Message, Renderer>, +    ) -> Element<'a, Message, Renderer> { +        Element::new(column) +    } +} diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs new file mode 100644 index 00000000..17f06ebe --- /dev/null +++ b/native/src/widget/image.rs @@ -0,0 +1,67 @@ +//! Display images in your user interface. + +use crate::{ +    Element, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Widget, +}; + +use std::hash::Hash; + +pub use iced_core::Image; + +impl<I, Message, Renderer> Widget<Message, Renderer> for Image<I> +where +    Renderer: self::Renderer<I>, +    I: Clone, +{ +    fn node(&self, renderer: &mut Renderer) -> Node { +        renderer.node(&self) +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        _cursor_position: Point, +    ) -> MouseCursor { +        renderer.draw(&self, layout); + +        MouseCursor::OutOfBounds +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        self.width.hash(state); +        self.height.hash(state); +    } +} + +/// The renderer of an [`Image`]. +/// +/// Your [renderer] will need to implement this trait before being able to use +/// an [`Image`] in your user interface. +/// +/// [`Image`]: struct.Image.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer<I> { +    /// Creates a [`Node`] for the provided [`Image`]. +    /// +    /// You should probably keep the original aspect ratio, if possible. +    /// +    /// [`Node`]: ../../struct.Node.html +    /// [`Image`]: struct.Image.html +    fn node(&mut self, image: &Image<I>) -> Node; + +    /// Draws an [`Image`]. +    /// +    /// [`Image`]: struct.Image.html +    fn draw(&mut self, image: &Image<I>, layout: Layout<'_>); +} + +impl<'a, I, Message, Renderer> From<Image<I>> for Element<'a, Message, Renderer> +where +    Renderer: self::Renderer<I>, +    I: Clone + 'a, +{ +    fn from(image: Image<I>) -> Element<'a, Message, Renderer> { +        Element::new(image) +    } +} diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs new file mode 100644 index 00000000..33d42e61 --- /dev/null +++ b/native/src/widget/radio.rs @@ -0,0 +1,92 @@ +//! Create choices using radio buttons. +use crate::input::{mouse, ButtonState}; +use crate::{Element, Event, Hasher, Layout, MouseCursor, Node, Point, Widget}; + +use std::hash::Hash; + +pub use iced_core::Radio; + +impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message> +where +    Renderer: self::Renderer, +    Message: Copy + std::fmt::Debug, +{ +    fn node(&self, renderer: &mut Renderer) -> Node { +        renderer.node(&self) +    } + +    fn on_event( +        &mut self, +        event: Event, +        layout: Layout<'_>, +        cursor_position: Point, +        messages: &mut Vec<Message>, +    ) { +        match event { +            Event::Mouse(mouse::Event::Input { +                button: mouse::Button::Left, +                state: ButtonState::Pressed, +            }) => { +                if layout.bounds().contains(cursor_position) { +                    messages.push(self.on_click); +                } +            } +            _ => {} +        } +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor { +        renderer.draw(&self, layout, cursor_position) +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        self.label.hash(state); +    } +} + +/// The renderer of a [`Radio`] button. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Radio`] button in your user interface. +/// +/// [`Radio`]: struct.Radio.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer { +    /// Creates a [`Node`] for the provided [`Radio`]. +    /// +    /// [`Node`]: ../../struct.Node.html +    /// [`Radio`]: struct.Radio.html +    fn node<Message>(&mut self, radio: &Radio<Message>) -> Node; + +    /// Draws a [`Radio`] button. +    /// +    /// It receives: +    ///   * the current cursor position +    ///   * the bounds of the [`Radio`] +    ///   * the bounds of the label of the [`Radio`] +    ///   * whether the [`Radio`] is selected or not +    /// +    /// [`Radio`]: struct.Radio.html +    fn draw<Message>( +        &mut self, +        radio: &Radio<Message>, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor; +} + +impl<'a, Message, Renderer> From<Radio<Message>> +    for Element<'a, Message, Renderer> +where +    Renderer: self::Renderer, +    Message: 'static + Copy + std::fmt::Debug, +{ +    fn from(checkbox: Radio<Message>) -> Element<'a, Message, Renderer> { +        Element::new(checkbox) +    } +} diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs new file mode 100644 index 00000000..3cd451b7 --- /dev/null +++ b/native/src/widget/row.rs @@ -0,0 +1,117 @@ +use std::hash::Hash; + +use crate::{ +    Element, Event, Hasher, Layout, MouseCursor, Node, Point, Style, Widget, +}; + +/// A container that distributes its contents horizontally. +pub type Row<'a, Message, Renderer> = +    iced_core::Row<Element<'a, Message, Renderer>>; + +impl<'a, Message, Renderer> Widget<Message, Renderer> +    for Row<'a, Message, Renderer> +{ +    fn node(&self, renderer: &mut 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); +        } + +        let mut style = Style::default() +            .width(self.width) +            .height(self.height) +            .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) +    } + +    fn on_event( +        &mut self, +        event: Event, +        layout: Layout<'_>, +        cursor_position: Point, +        messages: &mut Vec<Message>, +    ) { +        self.children.iter_mut().zip(layout.children()).for_each( +            |(child, layout)| { +                child +                    .widget +                    .on_event(event, layout, cursor_position, messages) +            }, +        ); +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor { +        let mut cursor = MouseCursor::OutOfBounds; + +        self.children.iter().zip(layout.children()).for_each( +            |(child, layout)| { +                let new_cursor = +                    child.widget.draw(renderer, layout, cursor_position); + +                if new_cursor != MouseCursor::OutOfBounds { +                    cursor = new_cursor; +                } +            }, +        ); + +        cursor +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        1.hash(state); +        self.width.hash(state); +        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); + +        for child in &self.children { +            child.widget.hash_layout(state); +        } +    } +} + +impl<'a, Message, Renderer> From<Row<'a, Message, Renderer>> +    for Element<'a, Message, Renderer> +where +    Renderer: 'a, +    Message: 'static, +{ +    fn from(row: Row<'a, Message, Renderer>) -> Element<'a, Message, Renderer> { +        Element::new(row) +    } +} diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs new file mode 100644 index 00000000..481296bd --- /dev/null +++ b/native/src/widget/slider.rs @@ -0,0 +1,126 @@ +//! Display an interactive selector of a single value from a range of values. +//! +//! A [`Slider`] has some local [`State`]. +//! +//! [`Slider`]: struct.Slider.html +//! [`State`]: struct.State.html +use std::hash::Hash; + +use crate::input::{mouse, ButtonState}; +use crate::{Element, Event, Hasher, Layout, MouseCursor, Node, Point, Widget}; + +pub use iced_core::slider::*; + +impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message> +where +    Renderer: self::Renderer, +{ +    fn node(&self, renderer: &mut Renderer) -> Node { +        renderer.node(&self) +    } + +    fn on_event( +        &mut self, +        event: Event, +        layout: Layout<'_>, +        cursor_position: Point, +        messages: &mut Vec<Message>, +    ) { +        let mut change = || { +            let bounds = layout.bounds(); + +            if cursor_position.x <= bounds.x { +                messages.push((self.on_change)(*self.range.start())); +            } else if cursor_position.x >= bounds.x + bounds.width { +                messages.push((self.on_change)(*self.range.end())); +            } else { +                let percent = (cursor_position.x - bounds.x) / bounds.width; +                let value = (self.range.end() - self.range.start()) * percent +                    + self.range.start(); + +                messages.push((self.on_change)(value)); +            } +        }; + +        match event { +            Event::Mouse(mouse::Event::Input { +                button: mouse::Button::Left, +                state, +            }) => match state { +                ButtonState::Pressed => { +                    if layout.bounds().contains(cursor_position) { +                        change(); +                        self.state.is_dragging = true; +                    } +                } +                ButtonState::Released => { +                    self.state.is_dragging = false; +                } +            }, +            Event::Mouse(mouse::Event::CursorMoved { .. }) => { +                if self.state.is_dragging { +                    change(); +                } +            } +            _ => {} +        } +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor { +        renderer.draw(&self, layout, cursor_position) +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        self.width.hash(state); +    } +} + +/// The renderer of a [`Slider`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Slider`] in your user interface. +/// +/// [`Slider`]: struct.Slider.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer { +    /// Creates a [`Node`] for the provided [`Radio`]. +    /// +    /// [`Node`]: ../../struct.Node.html +    /// [`Radio`]: struct.Radio.html +    fn node<Message>(&self, slider: &Slider<'_, Message>) -> Node; + +    /// Draws a [`Slider`]. +    /// +    /// It receives: +    ///   * the current cursor position +    ///   * the bounds of the [`Slider`] +    ///   * the local state of the [`Slider`] +    ///   * the range of values of the [`Slider`] +    ///   * the current value of the [`Slider`] +    /// +    /// [`Slider`]: struct.Slider.html +    /// [`State`]: struct.State.html +    /// [`Class`]: enum.Class.html +    fn draw<Message>( +        &mut self, +        slider: &Slider<'_, Message>, +        layout: Layout<'_>, +        cursor_position: Point, +    ) -> MouseCursor; +} + +impl<'a, Message, Renderer> From<Slider<'a, Message>> +    for Element<'a, Message, Renderer> +where +    Renderer: self::Renderer, +    Message: 'static, +{ +    fn from(slider: Slider<'a, Message>) -> Element<'a, Message, Renderer> { +        Element::new(slider) +    } +} diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs new file mode 100644 index 00000000..affdfd64 --- /dev/null +++ b/native/src/widget/text.rs @@ -0,0 +1,77 @@ +//! Write some text for your users to read. +use crate::{Element, Hasher, Layout, MouseCursor, Node, Point, Widget}; + +use std::hash::Hash; + +pub use iced_core::text::*; + +impl<Message, Renderer> Widget<Message, Renderer> for Text +where +    Renderer: self::Renderer, +{ +    fn node(&self, renderer: &mut Renderer) -> Node { +        renderer.node(&self) +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        layout: Layout<'_>, +        _cursor_position: Point, +    ) -> MouseCursor { +        renderer.draw(&self, layout); + +        MouseCursor::OutOfBounds +    } + +    fn hash_layout(&self, state: &mut Hasher) { +        self.content.hash(state); +        self.size.hash(state); +    } +} + +/// The renderer of a [`Text`] fragment. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use [`Text`] in your [`UserInterface`]. +/// +/// [`Text`]: struct.Text.html +/// [renderer]: ../../renderer/index.html +/// [`UserInterface`]: ../../struct.UserInterface.html +pub trait Renderer { +    /// Creates a [`Node`] with the given [`Style`] for the provided [`Text`] +    /// contents and size. +    /// +    /// You should probably use [`Node::with_measure`] to allow [`Text`] to +    /// adapt to the dimensions of its container. +    /// +    /// [`Node`]: ../../struct.Node.html +    /// [`Style`]: ../../struct.Style.html +    /// [`Text`]: struct.Text.html +    /// [`Node::with_measure`]: ../../struct.Node.html#method.with_measure +    fn node(&self, text: &Text) -> Node; + +    /// Draws a [`Text`] fragment. +    /// +    /// It receives: +    ///   * the bounds of the [`Text`] +    ///   * the contents of the [`Text`] +    ///   * the size of the [`Text`] +    ///   * the color of the [`Text`] +    ///   * the [`HorizontalAlignment`] of the [`Text`] +    ///   * the [`VerticalAlignment`] of the [`Text`] +    /// +    /// [`Text`]: struct.Text.html +    /// [`HorizontalAlignment`]: enum.HorizontalAlignment.html +    /// [`VerticalAlignment`]: enum.VerticalAlignment.html +    fn draw(&mut self, text: &Text, layout: Layout<'_>); +} + +impl<'a, Message, Renderer> From<Text> for Element<'a, Message, Renderer> +where +    Renderer: self::Renderer, +{ +    fn from(text: Text) -> Element<'a, Message, Renderer> { +        Element::new(text) +    } +}  | 
