summaryrefslogtreecommitdiffstats
path: root/native/src
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-09-20 19:15:31 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-09-20 19:15:31 +0200
commitb9e0f7494881ad7cdfbcbc16878ecc6ef717753f (patch)
treec8a7419b5cb4c0161306c479b93038f2f86498c2 /native/src
parentb83a4b42dd912b5f59d40e7d4f7f7ccdabc43019 (diff)
downloadiced-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.rs228
-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.rs170
-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.rs111
-rw-r--r--native/src/widget/checkbox.rs95
-rw-r--r--native/src/widget/column.rs118
-rw-r--r--native/src/widget/image.rs67
-rw-r--r--native/src/widget/radio.rs92
-rw-r--r--native/src/widget/row.rs117
-rw-r--r--native/src/widget/slider.rs126
-rw-r--r--native/src/widget/text.rs77
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)
+ }
+}