From a7dba612f03e58d7bd9527499d893987986b347c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 22 Nov 2019 19:36:57 +0100 Subject: [PATCH] Write docs for `iced` and `iced_native` --- core/src/align.rs | 8 +- core/src/command.rs | 2 +- core/src/lib.rs | 2 + native/src/element.rs | 64 ++++++----- native/src/input/mouse/event.rs | 8 +- native/src/layout.rs | 16 +++ native/src/layout/flex.rs | 24 +++-- native/src/layout/limits.rs | 53 ++++++++++ native/src/layout/node.rs | 23 +++- native/src/lib.rs | 174 ++++-------------------------- native/src/renderer.rs | 12 +++ native/src/renderer/null.rs | 1 + native/src/renderer/windowed.rs | 19 ++++ native/src/size.rs | 14 +++ native/src/widget.rs | 9 +- native/src/widget/checkbox.rs | 9 +- native/src/widget/column.rs | 17 +++ native/src/widget/container.rs | 1 + native/src/widget/image.rs | 3 + native/src/widget/radio.rs | 7 +- native/src/widget/row.rs | 17 +++ native/src/widget/scrollable.rs | 24 +++++ native/src/widget/slider.rs | 25 +++++ native/src/widget/text.rs | 11 +- native/src/widget/text_input.rs | 60 ++++++++++- src/application.rs | 127 +++++++++++++++++++++- src/lib.rs | 181 ++++++++++++++++++++++++++++++++ src/native.rs | 62 +++++++++++ src/sandbox.rs | 114 +++++++++++++++++++- web/src/widget/text.rs | 4 +- 30 files changed, 877 insertions(+), 214 deletions(-) diff --git a/core/src/align.rs b/core/src/align.rs index 8b571db4..05bd5e63 100644 --- a/core/src/align.rs +++ b/core/src/align.rs @@ -1,10 +1,4 @@ -/// Alignment on the cross axis of a container. -/// -/// * On a [`Column`], it describes __horizontal__ alignment. -/// * On a [`Row`], it describes __vertical__ alignment. -/// -/// [`Column`]: widget/struct.Column.html -/// [`Row`]: widget/struct.Row.html +/// Alignment on an unspecified axis of a container. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Align { /// Align at the start of the cross axis. diff --git a/core/src/command.rs b/core/src/command.rs index 698105aa..14b48b5b 100644 --- a/core/src/command.rs +++ b/core/src/command.rs @@ -2,7 +2,7 @@ use futures::future::{BoxFuture, Future, FutureExt}; /// A collection of async operations. /// -/// You should be able to turn a future easily into a [`Command`], eiter by +/// You should be able to turn a future easily into a [`Command`], either by /// using the `From` trait or [`Command::perform`]. /// /// [`Command`]: struct.Command.html diff --git a/core/src/lib.rs b/core/src/lib.rs index 5a608fe8..6c36e683 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,6 +4,8 @@ //! different runtime implementations. For instance, both [`iced_native`] and //! [`iced_web`] are built on top of `iced_core`. //! +//! ![`iced_core` crate graph](https://github.com/hecrj/iced/blob/cae26cb7bc627f4a5b3bcf1cd023a0c552e8c65e/docs/graphs/core.png?raw=true) +//! //! [Iced]: https://github.com/hecrj/iced //! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native //! [`iced_web`]: https://github.com/hecrj/iced/tree/master/web diff --git a/native/src/element.rs b/native/src/element.rs index 3bf24317..5335fdc1 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -33,31 +33,6 @@ where } } - 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( - &self, - renderer: &mut Renderer, - layout: Layout<'_>, - cursor_position: Point, - ) -> Renderer::Output { - self.widget.draw(renderer, layout, cursor_position) - } - /// Applies a transformation to the produced message of the [`Element`]. /// /// This method is useful when you want to decouple different parts of your @@ -225,6 +200,45 @@ where } } + /// Returns the width of the [`Element`]. + /// + /// [`Element`]: struct.Element.html + pub fn width(&self) -> Length { + self.widget.width() + } + + /// Returns the height of the [`Element`]. + /// + /// [`Element`]: struct.Element.html + pub fn height(&self) -> Length { + self.widget.height() + } + + /// Computes the layout of the [`Element`] in the given [`Limits`]. + /// + /// [`Element`]: struct.Element.html + /// [`Limits`]: layout/struct.Limits.html + pub fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.widget.layout(renderer, limits) + } + + /// Draws the [`Element`] and its children using the given [`Layout`]. + /// + /// [`Element`]: struct.Element.html + /// [`Layout`]: layout/struct.Layout.html + pub fn draw( + &self, + renderer: &mut Renderer, + layout: Layout<'_>, + cursor_position: Point, + ) -> Renderer::Output { + self.widget.draw(renderer, layout, cursor_position) + } + pub(crate) fn hash_layout(&self, state: &mut Hasher) { self.widget.hash_layout(state); } diff --git a/native/src/input/mouse/event.rs b/native/src/input/mouse/event.rs index 478f9b4d..aafc4fe3 100644 --- a/native/src/input/mouse/event.rs +++ b/native/src/input/mouse/event.rs @@ -34,11 +34,16 @@ pub enum Event { }, /// The mouse wheel was scrolled. - WheelScrolled { delta: ScrollDelta }, + WheelScrolled { + /// The scroll movement. + delta: ScrollDelta, + }, } +/// A scroll movement. #[derive(Debug, Clone, Copy, PartialEq)] pub enum ScrollDelta { + /// A line-based scroll movement Lines { /// The number of horizontal lines scrolled x: f32, @@ -46,6 +51,7 @@ pub enum ScrollDelta { /// The number of vertical lines scrolled y: f32, }, + /// A pixel-based scroll movement Pixels { /// The number of horizontal pixels scrolled x: f32, diff --git a/native/src/layout.rs b/native/src/layout.rs index 0a744346..e945706b 100644 --- a/native/src/layout.rs +++ b/native/src/layout.rs @@ -1,3 +1,4 @@ +//! Position your widgets properly. mod limits; mod node; @@ -8,6 +9,9 @@ pub use node::Node; use crate::{Point, Rectangle, Vector}; +/// The bounds of a [`Node`] and its children, using absolute coordinates. +/// +/// [`Node`]: struct.Node.html #[derive(Debug, Clone, Copy)] pub struct Layout<'a> { position: Point, @@ -28,6 +32,14 @@ impl<'a> Layout<'a> { } } + /// 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(); @@ -39,6 +51,10 @@ impl<'a> Layout<'a> { } } + /// 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> { self.node.children().iter().map(move |node| { Layout::with_offset( diff --git a/native/src/layout/flex.rs b/native/src/layout/flex.rs index 7a2b0d70..bc90553e 100644 --- a/native/src/layout/flex.rs +++ b/native/src/layout/flex.rs @@ -1,3 +1,4 @@ +//! Distribute elements using a flex-based layout. // This code is heavily inspired by the [`druid`] codebase. // // [`druid`]: https://github.com/xi-editor/druid @@ -20,9 +21,13 @@ use crate::{ Align, Element, Size, }; +/// The main axis of a flex layout. #[derive(Debug)] pub enum Axis { + /// The horizontal axis Horizontal, + + /// The vertical axis Vertical, } @@ -49,7 +54,12 @@ impl Axis { } } -// TODO: Remove `Message` type parameter +/// Computes the flex layout with the given axis and limits, applying spacing, +/// padding and alignment to the items as needed. +/// +/// It returns a new layout [`Node`]. +/// +/// [`Node`]: ../struct.Node.html pub fn resolve( axis: Axis, renderer: &Renderer, @@ -57,7 +67,7 @@ pub fn resolve( padding: f32, spacing: f32, align_items: Align, - children: &[Element<'_, Message, Renderer>], + items: &[Element<'_, Message, Renderer>], ) -> Node where Renderer: crate::Renderer, @@ -65,14 +75,14 @@ where let limits = limits.pad(padding); let mut total_non_fill = - spacing as f32 * (children.len() as i32 - 1).max(0) as f32; + spacing as f32 * (items.len() as i32 - 1).max(0) as f32; let mut fill_sum = 0; let mut cross = axis.cross(limits.min()); - let mut nodes: Vec = Vec::with_capacity(children.len()); - nodes.resize(children.len(), Node::default()); + let mut nodes: Vec = Vec::with_capacity(items.len()); + nodes.resize(items.len(), Node::default()); - for (i, child) in children.iter().enumerate() { + for (i, child) in items.iter().enumerate() { let fill_factor = match axis { Axis::Horizontal => child.width(), Axis::Vertical => child.height(), @@ -97,7 +107,7 @@ where let available = axis.main(limits.max()); let remaining = (available - total_non_fill).max(0.0); - for (i, child) in children.iter().enumerate() { + for (i, child) in items.iter().enumerate() { let fill_factor = match axis { Axis::Horizontal => child.width(), Axis::Vertical => child.height(), diff --git a/native/src/layout/limits.rs b/native/src/layout/limits.rs index af269acd..2705a47d 100644 --- a/native/src/layout/limits.rs +++ b/native/src/layout/limits.rs @@ -1,5 +1,6 @@ use crate::{Length, Size}; +/// A set of size constraints for layouting. #[derive(Debug, Clone, Copy)] pub struct Limits { min: Size, @@ -8,12 +9,17 @@ pub struct Limits { } impl Limits { + /// No limits pub const NONE: Limits = Limits { min: Size::ZERO, max: Size::INFINITY, fill: Size::INFINITY, }; + /// Creates new [`Limits`] with the given minimum and maximum [`Size`]. + /// + /// [`Limits`]: struct.Limits.html + /// [`Size`]: ../struct.Size.html pub fn new(min: Size, max: Size) -> Limits { Limits { min, @@ -22,14 +28,25 @@ impl Limits { } } + /// Returns the minimum [`Size`] of the [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html + /// [`Size`]: ../struct.Size.html pub fn min(&self) -> Size { self.min } + /// Returns the maximum [`Size`] of the [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html + /// [`Size`]: ../struct.Size.html pub fn max(&self) -> Size { self.max } + /// Applies a width constraint to the current [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html pub fn width(mut self, width: Length) -> Limits { match width { Length::Shrink => { @@ -51,6 +68,9 @@ impl Limits { self } + /// Applies a height constraint to the current [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html pub fn height(mut self, height: Length) -> Limits { match height { Length::Shrink => { @@ -72,6 +92,9 @@ impl Limits { self } + /// Applies a minimum width constraint to the current [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html 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); @@ -79,6 +102,9 @@ impl Limits { self } + /// Applies a maximum width constraint to the current [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html 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); @@ -86,6 +112,19 @@ impl Limits { self } + /// Applies a minimum height constraint to the current [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html + pub fn min_height(mut self, min_height: u32) -> Limits { + self.min.height = + self.min.height.max(min_height as f32).min(self.max.height); + + self + } + + /// Applies a maximum height constraint to the current [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html 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); @@ -93,10 +132,17 @@ impl Limits { self } + /// Shrinks the current [`Limits`] to account for the given padding. + /// + /// [`Limits`]: struct.Limits.html pub fn pad(&self, padding: f32) -> Limits { self.shrink(Size::new(padding * 2.0, padding * 2.0)) } + /// Shrinks the current [`Limits`] by the given [`Size`]. + /// + /// [`Limits`]: struct.Limits.html + /// [`Size`]: ../struct.Size.html pub fn shrink(&self, size: Size) -> Limits { let min = Size::new( (self.min().width - size.width).max(0.0), @@ -116,6 +162,9 @@ impl Limits { Limits { min, max, fill } } + /// Removes the minimum width constraint for the current [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html pub fn loose(&self) -> Limits { Limits { min: Size::ZERO, @@ -124,6 +173,10 @@ impl Limits { } } + /// Computes the resulting [`Size`] that fits the [`Limits`] given the + /// intrinsic size of some content. + /// + /// [`Limits`]: struct.Limits.html pub fn resolve(&self, intrinsic_size: Size) -> Size { Size::new( intrinsic_size diff --git a/native/src/layout/node.rs b/native/src/layout/node.rs index 64ebf2d0..ed1cd3da 100644 --- a/native/src/layout/node.rs +++ b/native/src/layout/node.rs @@ -1,16 +1,25 @@ use crate::{Align, Rectangle, Size}; +/// The bounds of an element and its children. #[derive(Debug, Clone, Default)] pub struct Node { - pub bounds: Rectangle, + pub(crate) bounds: Rectangle, children: Vec, } impl Node { + /// Creates a new [`Node`] with the given [`Size`]. + /// + /// [`Node`]: struct.Node.html + /// [`Size`]: ../struct.Size.html pub fn new(size: Size) -> Self { Self::with_children(size, Vec::new()) } + /// Creates a new [`Node`] with the given [`Size`] and children. + /// + /// [`Node`]: struct.Node.html + /// [`Size`]: ../struct.Size.html pub fn with_children(size: Size, children: Vec) -> Self { Node { bounds: Rectangle { @@ -23,19 +32,29 @@ impl Node { } } + /// Returns the [`Size`] of the [`Node`]. + /// + /// [`Node`]: struct.Node.html + /// [`Size`]: ../struct.Size.html pub fn size(&self) -> Size { Size::new(self.bounds.width, self.bounds.height) } + /// Returns the bounds of the [`Node`]. + /// + /// [`Node`]: struct.Node.html pub fn bounds(&self) -> Rectangle { self.bounds } + /// Returns the children of the [`Node`]. + /// + /// [`Node`]: struct.Node.html pub fn children(&self) -> &[Node] { &self.children } - pub fn align( + pub(crate) fn align( &mut self, horizontal_alignment: Align, vertical_alignment: Align, diff --git a/native/src/lib.rs b/native/src/lib.rs index 55331ba5..57d014b6 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -1,162 +1,34 @@ -//! Iced is a renderer-agnostic GUI library focused on simplicity and -//! type-safety. Inspired by [Elm]. +//! A renderer-agnostic native GUI runtime. //! -//! # Features -//! * Simple, easy-to-use, renderer-agnostic API -//! * Responsive, flexbox-based layouting -//! * Type-safe, reactive programming model -//! * Built-in widgets -//! * Custom widget support +//! ![`iced_native` crate graph](https://github.com/hecrj/iced/blob/cae26cb7bc627f4a5b3bcf1cd023a0c552e8c65e/docs/graphs/native.png?raw=true) //! -//! Check out the [repository] and the [examples] for more details! +//! `iced_native` takes [`iced_core`] and builds a native runtime on top of it, +//! featuring: //! -//! [examples]: https://github.com/hecrj/iced/tree/0.1.0/examples -//! [repository]: https://github.com/hecrj/iced +//! - A custom layout engine, greatly inspired by [`druid`] +//! - Event handling for all the built-in widgets +//! - A renderer-agnostic API +//! +//! To achieve this, it introduces a bunch of reusable interfaces: +//! +//! - A [`Widget`] trait, which is used to implement new widgets: from layout +//! requirements to event and drawing logic. +//! - A bunch of `Renderer` traits, meant to keep the crate renderer-agnostic. +//! - A [`Windowed`] trait, leveraging [`raw-window-handle`], which can be +//! implemented by graphical renderers that target _windows_. Window-based +//! shells (like [`iced_winit`]) can use this trait to stay renderer-agnostic. //! //! # 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 { -//! # pub use iced_native::renderer::Null as Renderer; -//! # } -//! 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 { -//! // 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, Text::new("+")) -//! .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, Text::new("-")) -//! .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 +//! [`iced_core`]: https://github.com/hecrj/iced/tree/master/core +//! [`iced_winit`]: https://github.com/hecrj/iced/tree/master/winit +//! [`druid`]: https://github.com/xi-editor/druid +//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle +//! [`Widget`]: widget/trait.Widget.html +//! [`Windowed`]: renderer/trait.Windowed.html //! [`UserInterface`]: struct.UserInterface.html -//#![deny(missing_docs)] +#![deny(missing_docs)] //#![deny(missing_debug_implementations)] #![deny(unused_results)] #![deny(unsafe_code)] diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 3e19be33..7a68ada4 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -32,9 +32,21 @@ pub use windowed::{Target, Windowed}; use crate::{layout, Element}; +/// A component that can take the state of a user interface and produce an +/// output for its users. pub trait Renderer: Sized { + /// The type of output of the [`Renderer`]. + /// + /// If you are implementing a graphical renderer, your output will most + /// likely be a tree of visual primitives. + /// + /// [`Renderer`]: trait.Renderer.html type Output; + /// Lays out the elements of a user interface. + /// + /// You should override this if you need to perform any operations before or + /// after layouting. For instance, trimming the measurements cache. fn layout<'a, Message>( &mut self, element: &Element<'a, Message, Self>, diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 8933b09b..3334e6b6 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -4,6 +4,7 @@ use crate::{ Rectangle, Renderer, Size, VerticalAlignment, }; +/// A renderer that does nothing. pub struct Null; impl Renderer for Null { diff --git a/native/src/renderer/windowed.rs b/native/src/renderer/windowed.rs index 6d0419d2..813a03f2 100644 --- a/native/src/renderer/windowed.rs +++ b/native/src/renderer/windowed.rs @@ -2,11 +2,21 @@ use crate::MouseCursor; use raw_window_handle::HasRawWindowHandle; +/// A renderer that can target windows. pub trait Windowed: super::Renderer + Sized { + /// The type of target. type Target: Target; + /// Creates a new [`Windowed`] renderer. + /// + /// [`Windowed`]: trait.Windowed.html fn new() -> Self; + /// Performs the drawing operations described in the output on the given + /// target. + /// + /// The overlay can be a bunch of debug text logs. It should be rendered on + /// top of the GUI on most scenarios. fn draw>( &mut self, output: &Self::Output, @@ -15,9 +25,15 @@ pub trait Windowed: super::Renderer + Sized { ) -> MouseCursor; } +/// A rendering target. pub trait Target { + /// The renderer of this target. type Renderer; + /// Creates a new rendering [`Target`] from the given window handle, width, + /// height and dpi factor. + /// + /// [`Target`]: trait.Target.html fn new( window: &W, width: u16, @@ -26,6 +42,9 @@ pub trait Target { renderer: &Self::Renderer, ) -> Self; + /// Resizes the current [`Target`]. + /// + /// [`Target`]: trait.Target.html fn resize( &mut self, width: u16, diff --git a/native/src/size.rs b/native/src/size.rs index bd909292..30e2a57e 100644 --- a/native/src/size.rs +++ b/native/src/size.rs @@ -1,5 +1,6 @@ use std::f32; +/// An amount of space in 2 dimensions. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Size { /// The width. @@ -9,13 +10,26 @@ pub struct Size { } impl Size { + /// A [`Size`] with zero width and height. + /// + /// [`Size`]: struct.Size.html pub const ZERO: Size = Size::new(0., 0.); + + /// A [`Size`] with infinite width and height. + /// + /// [`Size`]: struct.Size.html pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY); + /// A [`Size`] of infinite width and height. + /// + /// [`Size`]: struct.Size.html pub const fn new(width: f32, height: f32) -> Self { Size { width, height } } + /// Increments the [`Size`] to account for the given padding. + /// + /// [`Size`]: struct.Size.html pub fn pad(&self, padding: f32) -> Self { Size { width: self.width + padding * 2.0, diff --git a/native/src/widget.rs b/native/src/widget.rs index 61b66c5b..48605ee3 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -23,6 +23,7 @@ pub mod button; pub mod checkbox; pub mod column; +pub mod container; pub mod image; pub mod radio; pub mod row; @@ -31,8 +32,6 @@ pub mod slider; pub mod text; pub mod text_input; -mod container; - #[doc(no_inline)] pub use button::Button; #[doc(no_inline)] @@ -69,8 +68,14 @@ pub trait Widget where Renderer: crate::Renderer, { + /// Returns the width of the [`Widget`]. + /// + /// [`Widget`]: trait.Widget.html fn width(&self) -> Length; + /// Returns the height of the [`Widget`]. + /// + /// [`Widget`]: trait.Widget.html fn height(&self) -> Length; /// Returns the [`Node`] of the [`Widget`]. diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index f7bda146..8101e6be 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -166,15 +166,18 @@ where /// [`Checkbox`]: struct.Checkbox.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// Returns the default size of a [`Checkbox`]. + /// + /// [`Checkbox`]: struct.Checkbox.html fn default_size(&self) -> u32; /// 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 + /// * whether the [`Checkbox`] is selected or not + /// * whether the mouse is over the [`Checkbox`] or not + /// * the drawn label of the [`Checkbox`] /// /// [`Checkbox`]: struct.Checkbox.html fn draw( diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 87c51f48..281437fd 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -1,3 +1,4 @@ +//! Distribute content vertically. use std::hash::Hash; use crate::{ @@ -189,7 +190,23 @@ where } } +/// The renderer of a [`Column`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Column`] in your user interface. +/// +/// [`Column`]: struct.Column.html +/// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// Draws a [`Column`]. + /// + /// It receives: + /// - the children of the [`Column`] + /// - the [`Layout`] of the [`Column`] and its children + /// - the cursor position + /// + /// [`Column`]: struct.Row.html + /// [`Layout`]: ../layout/struct.Layout.html fn draw( &mut self, content: &[Element<'_, Message, Self>], diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 2f15573d..64a5f4da 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -1,3 +1,4 @@ +//! Decorate content and apply alignment. use std::hash::Hash; use crate::{ diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 696de683..c64f07b1 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -116,6 +116,9 @@ where /// [`Image`]: struct.Image.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// Returns the dimensions of an [`Image`] located on the given path. + /// + /// [`Image`]: struct.Image.html fn dimensions(&self, path: &str) -> (u32, u32); /// Draws an [`Image`]. diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 3dc764fe..4e48728f 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -174,15 +174,18 @@ where /// [`Radio`]: struct.Radio.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// Returns the default size of a [`Radio`] button. + /// + /// [`Radio`]: struct.Radio.html fn default_size(&self) -> u32; /// 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 + /// * whether the mouse is over the [`Radio`] or not + /// * the drawn label of the [`Radio`] /// /// [`Radio`]: struct.Radio.html fn draw( diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 33222b8a..34c7ca7c 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -1,3 +1,4 @@ +//! Distribute content horizontally. use std::hash::Hash; use crate::{ @@ -192,7 +193,23 @@ where } } +/// The renderer of a [`Row`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Row`] in your user interface. +/// +/// [`Row`]: struct.Row.html +/// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// Draws a [`Row`]. + /// + /// It receives: + /// - the children of the [`Row`] + /// - the [`Layout`] of the [`Row`] and its children + /// - the cursor position + /// + /// [`Row`]: struct.Row.html + /// [`Layout`]: ../layout/struct.Layout.html fn draw( &mut self, children: &[Element<'_, Message, Self>], diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 438f04cf..7b231b5f 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -1,3 +1,4 @@ +//! Navigate an endless amount of content with a scrollbar. use crate::{ column, input::{mouse, ButtonState}, @@ -361,7 +362,18 @@ impl State { } } +/// The renderer of a [`Scrollable`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Scrollable`] in your user interface. +/// +/// [`Scrollable`]: struct.Scrollable.html +/// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// Returns whether the mouse is over the scrollbar given the bounds of + /// the [`Scrollable`] and its contents. + /// + /// [`Scrollable`]: struct.Scrollable.html fn is_mouse_over_scrollbar( &self, bounds: Rectangle, @@ -369,6 +381,18 @@ pub trait Renderer: crate::Renderer + Sized { cursor_position: Point, ) -> bool; + /// Draws the [`Scrollable`]. + /// + /// It receives: + /// - the [`State`] of the [`Scrollable`] + /// - the bounds of the [`Scrollable`] + /// - whether the mouse is over the [`Scrollable`] or not + /// - whether the mouse is over the scrollbar or not + /// - the scrolling offset + /// - the drawn content + /// + /// [`Scrollable`]: struct.Scrollable.html + /// [`State`]: struct.State.html fn draw( &mut self, scrollable: &State, diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 113cc2a4..fe503c34 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -13,6 +13,28 @@ use crate::{ use std::{hash::Hash, ops::RangeInclusive}; +/// An horizontal bar and a handle that selects a single value from a range of +/// values. +/// +/// A [`Slider`] will try to fill the horizontal space of its container. +/// +/// [`Slider`]: struct.Slider.html +/// +/// # Example +/// ``` +/// # use iced_native::{slider, Slider}; +/// # +/// pub enum Message { +/// SliderChanged(f32), +/// } +/// +/// let state = &mut slider::State::new(); +/// let value = 50.0; +/// +/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged); +/// ``` +/// +/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true) pub struct Slider<'a, Message> { state: &'a mut State, range: RangeInclusive, @@ -180,6 +202,9 @@ where /// [`Slider`]: struct.Slider.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// Returns the height of the [`Slider`]. + /// + /// [`Slider`]: struct.Slider.html fn height(&self) -> u32; /// Draws a [`Slider`]. diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index c4ab88d3..cf0701b9 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -32,9 +32,9 @@ impl Text { /// Create a new fragment of [`Text`] with the given contents. /// /// [`Text`]: struct.Text.html - pub fn new(label: &str) -> Self { + pub fn new>(label: T) -> Self { Text { - content: String::from(label), + content: label.into(), size: None, color: None, font: Font::Default, @@ -174,8 +174,15 @@ where /// [renderer]: ../../renderer/index.html /// [`UserInterface`]: ../../struct.UserInterface.html pub trait Renderer: crate::Renderer { + /// Returns the default size of the [`Text`]. + /// + /// [`Text`]: struct.Text.html fn default_size(&self) -> u16; + /// Measures the [`Text`] in the given bounds and returns the minimum + /// boundaries that can fit the contents. + /// + /// [`Text`]: struct.Text.html fn measure( &self, content: &str, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 65306504..c3876b1d 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -10,7 +10,26 @@ use crate::{ Widget, }; -/// A widget that can be filled with text by using a keyboard. +/// A field that can be filled with text. +/// +/// # Example +/// ``` +/// # use iced_native::{text_input, TextInput}; +/// # +/// enum Message { +/// TextInputChanged(String), +/// } +/// +/// let mut state = text_input::State::new(); +/// let value = "Some text"; +/// +/// let input = TextInput::new( +/// &mut state, +/// "This is the placeholder...", +/// value, +/// Message::TextInputChanged, +/// ); +/// ``` pub struct TextInput<'a, Message> { state: &'a mut State, placeholder: String, @@ -26,7 +45,14 @@ pub struct TextInput<'a, Message> { impl<'a, Message> TextInput<'a, Message> { /// Creates a new [`TextInput`]. /// + /// It expects: + /// - some [`State`] + /// - a placeholder + /// - the current value + /// - a function that produces a message when the [`TextInput`] changes + /// /// [`TextInput`]: struct.TextInput.html + /// [`State`]: struct.State.html pub fn new( state: &'a mut State, placeholder: &str, @@ -232,9 +258,32 @@ where } } +/// The renderer of a [`TextInput`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`TextInput`] in your user interface. +/// +/// [`TextInput`]: struct.TextInput.html +/// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// Returns the default size of the text of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html fn default_size(&self) -> u16; + /// Draws a [`TextInput`]. + /// + /// It receives: + /// - its bounds of the [`TextInput`] + /// - the bounds of the text (i.e. the current value) + /// - the cursor position + /// - the placeholder to show when the value is empty + /// - the current [`Value`] + /// - the current [`State`] + /// + /// [`TextInput`]: struct.TextInput.html + /// [`Value`]: struct.Value.html + /// [`State`]: struct.State.html fn draw( &mut self, bounds: Rectangle, @@ -289,6 +338,9 @@ impl State { } } + /// Returns whether the [`TextInput`] is currently focused or not. + /// + /// [`TextInput`]: struct.TextInput.html pub fn is_focused(&self) -> bool { self.is_focused } @@ -303,7 +355,7 @@ impl State { /// Moves the cursor of a [`TextInput`] to the right. /// /// [`TextInput`]: struct.TextInput.html - pub fn move_cursor_right(&mut self, value: &Value) { + pub(crate) fn move_cursor_right(&mut self, value: &Value) { let current = self.cursor_position(value); if current < value.len() { @@ -314,7 +366,7 @@ impl State { /// Moves the cursor of a [`TextInput`] to the left. /// /// [`TextInput`]: struct.TextInput.html - pub fn move_cursor_left(&mut self, value: &Value) { + pub(crate) fn move_cursor_left(&mut self, value: &Value) { let current = self.cursor_position(value); if current > 0 { @@ -325,7 +377,7 @@ impl State { /// Moves the cursor of a [`TextInput`] to the end. /// /// [`TextInput`]: struct.TextInput.html - pub fn move_cursor_to_end(&mut self, value: &Value) { + pub(crate) fn move_cursor_to_end(&mut self, value: &Value) { self.cursor_position = value.len(); } } diff --git a/src/application.rs b/src/application.rs index ba8da446..5ecb901e 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,19 +1,140 @@ use crate::{Command, Element}; +/// An interactive cross-platform application. +/// +/// This trait is the main entrypoint of Iced. Once implemented, you can run +/// your GUI application by simply calling [`run`](#method.run). +/// +/// - On native platforms, it will run in its own window. +/// - On the web, it will take control of the `` and the `<body>` of the +/// document. +/// +/// An [`Application`](trait.Application.html) can execute asynchronous actions +/// by returning a [`Command`](struct.Command.html) in some of its methods. If +/// you do not intend to perform any background work in your program, the +/// [`Sandbox`](trait.Sandbox.html) trait offers a simplified interface. +/// +/// # Example +/// Let's say we want to run the [`Counter` example we implemented +/// before](index.html#overview). We just need to fill in the gaps: +/// +/// ```no_run +/// use iced::{button, Application, Button, Column, Command, Element, Text}; +/// +/// pub fn main() { +/// Counter::run() +/// } +/// +/// #[derive(Default)] +/// struct Counter { +/// value: i32, +/// increment_button: button::State, +/// decrement_button: button::State, +/// } +/// +/// #[derive(Debug, Clone, Copy)] +/// enum Message { +/// IncrementPressed, +/// DecrementPressed, +/// } +/// +/// impl Application for Counter { +/// type Message = Message; +/// +/// fn new() -> (Self, Command<Message>) { +/// (Self::default(), Command::none()) +/// } +/// +/// fn title(&self) -> String { +/// String::from("A simple counter") +/// } +/// +/// fn update(&mut self, message: Message) -> Command<Message> { +/// match message { +/// Message::IncrementPressed => { +/// self.value += 1; +/// } +/// Message::DecrementPressed => { +/// self.value -= 1; +/// } +/// } +/// +/// Command::none() +/// } +/// +/// fn view(&mut self) -> Element<Message> { +/// Column::new() +/// .push( +/// Button::new(&mut self.increment_button, Text::new("Increment")) +/// .on_press(Message::IncrementPressed), +/// ) +/// .push( +/// Text::new(self.value.to_string()).size(50), +/// ) +/// .push( +/// Button::new(&mut self.decrement_button, Text::new("Decrement")) +/// .on_press(Message::DecrementPressed), +/// ) +/// .into() +/// } +/// } +/// ``` pub trait Application: Sized { + /// The type of __messages__ your [`Application`] will produce. + /// + /// [`Application`]: trait.Application.html type Message: std::fmt::Debug + Send; + /// Initializes the [`Application`]. + /// + /// Here is where you should return the initial state of your app. + /// + /// Additionally, you can return a [`Command`](struct.Command.html) if you + /// need to perform some async action in the background on startup. This is + /// useful if you want to load state from a file, perform an initial HTTP + /// request, etc. + /// + /// [`Application`]: trait.Application.html fn new() -> (Self, Command<Self::Message>); + /// Returns the current title of the [`Application`]. + /// + /// This title can be dynamic! The runtime will automatically update the + /// title of your application when necessary. + /// + /// [`Application`]: trait.Application.html fn title(&self) -> String; + /// Handles a __message__ and updates the state of the [`Application`]. + /// + /// This is where you define your __update logic__. All the __messages__, + /// produced by either user interactions or commands, will be handled by + /// this method. + /// + /// Any [`Command`] returned will be executed immediately in the background. + /// + /// [`Application`]: trait.Application.html + /// [`Command`]: struct.Command.html fn update(&mut self, message: Self::Message) -> Command<Self::Message>; - fn view(&mut self) -> Element<Self::Message>; + /// Returns the widgets to display in the [`Application`]. + /// + /// These widgets can produce __messages__ based on user interaction. + /// + /// [`Application`]: trait.Application.html + fn view(&mut self) -> Element<'_, Self::Message>; + /// Runs the [`Application`]. + /// + /// This method will take control of the current thread and __will NOT + /// return__. + /// + /// It should probably be that last thing you call in your `main` function. + /// + /// [`Application`]: trait.Application.html fn run() where - Self: 'static + Sized, + Self: 'static, { #[cfg(not(target_arch = "wasm32"))] <Instance<Self> as iced_winit::Application>::run(); @@ -47,7 +168,7 @@ where self.0.update(message) } - fn view(&mut self) -> Element<Self::Message> { + fn view(&mut self) -> Element<'_, Self::Message> { self.0.view() } } diff --git a/src/lib.rs b/src/lib.rs index 8462cd3c..48588261 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,184 @@ +//! Iced is a cross-platform GUI library focused on simplicity and type-safety. +//! Inspired by [Elm]. +//! +//! # Features +//! * Simple, easy-to-use, batteries-included API +//! * Type-safe, reactive programming model +//! * [Cross-platform support] (Windows, macOS, Linux, and the Web) +//! * Responsive layout +//! * Built-in widgets (including [text inputs], [scrollables], and more!) +//! * Custom widget support (create your own!) +//! * [Debug overlay with performance metrics] +//! * First-class support for async actions (use futures!) +//! * [Modular ecosystem] split into reusable parts: +//! * A [renderer-agnostic native runtime] enabling integration with existing +//! systems +//! * A [built-in renderer] supporting Vulkan, Metal, DX11, and DX12 +//! * A [windowing shell] +//! * A [web runtime] leveraging the DOM +//! +//! Check out the [repository] and the [examples] for more details! +//! +//! [Cross-platform support]: https://github.com/hecrj/iced/blob/master/docs/images/todos_desktop.jpg?raw=true +//! [text inputs]: https://gfycat.com/alertcalmcrow-rust-gui +//! [scrollables]: https://gfycat.com/perkybaggybaboon-rust-gui +//! [Debug overlay with performance metrics]: https://gfycat.com/artisticdiligenthorseshoebat-rust-gui +//! [Modular ecosystem]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md +//! [renderer-agnostic native runtime]: https://github.com/hecrj/iced/tree/master/native +//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs +//! [built-in renderer]: https://github.com/hecrj/iced/tree/master/wgpu +//! [windowing shell]: https://github.com/hecrj/iced/tree/master/winit +//! [`dodrio`]: https://github.com/fitzgen/dodrio +//! [web runtime]: https://github.com/hecrj/iced/tree/master/web +//! [examples]: https://github.com/hecrj/iced/tree/0.1.0/examples +//! [repository]: https://github.com/hecrj/iced +//! +//! # Overview +//! 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::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::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, +//! # } +//! # +//! use iced::{Button, Column, Text}; +//! +//! impl Counter { +//! pub fn view(&mut self) -> Column<Message> { +//! // 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, Text::new("+")) +//! .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, Text::new("-")) +//! .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::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. +//! +//! # Usage +//! Take a look at the [`Application`] trait, which streamlines all the process +//! described above for you! +//! +//! [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 +//! [`Application`]: trait.Application.html +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![deny(unused_results)] +#![deny(unsafe_code)] +#![deny(rust_2018_idioms)] mod application; #[cfg_attr(target_arch = "wasm32", path = "web.rs")] #[cfg_attr(not(target_arch = "wasm32"), path = "native.rs")] diff --git a/src/native.rs b/src/native.rs index fcb50d43..926b2d11 100644 --- a/src/native.rs +++ b/src/native.rs @@ -4,7 +4,36 @@ pub use iced_winit::{ }; pub mod widget { + //! Display information and interactive controls in your application. + //! + //! # Re-exports + //! For convenience, the contents of this module are available at the root + //! module. Therefore, you can directly type: + //! + //! ``` + //! use iced::{button, Button}; + //! ``` + //! + //! # Stateful widgets + //! Some widgets need to keep track of __local state__. + //! + //! These widgets have their own module with a `State` type. For instance, a + //! [`TextInput`] has some [`text_input::State`]. + //! + //! [`TextInput`]: text_input/struct.TextInput.html + //! [`text_input::State`]: text_input/struct.State.html pub mod button { + //! Allow your users to perform actions by pressing a button. + //! + //! A [`Button`] has some local [`State`]. + //! + //! [`Button`]: type.Button.html + //! [`State`]: struct.State.html + + /// A widget that produces a message when clicked. + /// + /// This is an alias of an `iced_native` button with a default + /// `Renderer`. pub type Button<'a, Message> = iced_winit::Button<'a, Message, iced_wgpu::Renderer>; @@ -12,6 +41,13 @@ pub mod widget { } pub mod scrollable { + //! Navigate an endless amount of content with a scrollbar. + + /// A widget that can vertically display an infinite amount of content + /// with a scrollbar. + /// + /// This is an alias of an `iced_native` scrollable with a default + /// `Renderer`. pub type Scrollable<'a, Message> = iced_winit::Scrollable<'a, Message, iced_wgpu::Renderer>; @@ -19,10 +55,23 @@ pub mod widget { } pub mod text_input { + //! Ask for information using text fields. + //! + //! A [`TextInput`] has some local [`State`]. + //! + //! [`TextInput`]: struct.TextInput.html + //! [`State`]: struct.State.html pub use iced_winit::text_input::{State, TextInput}; } pub mod slider { + //! 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 pub use iced_winit::slider::{Slider, State}; } @@ -34,12 +83,22 @@ pub mod widget { text_input::TextInput, }; + /// A container that distributes its contents vertically. + /// + /// This is an alias of an `iced_native` column with a default `Renderer`. pub type Column<'a, Message> = iced_winit::Column<'a, Message, iced_wgpu::Renderer>; + /// A container that distributes its contents horizontally. + /// + /// This is an alias of an `iced_native` row with a default `Renderer`. pub type Row<'a, Message> = iced_winit::Row<'a, Message, iced_wgpu::Renderer>; + /// An element decorating some content. + /// + /// This is an alias of an `iced_native` container with a default + /// `Renderer`. pub type Container<'a, Message> = iced_winit::Container<'a, Message, iced_wgpu::Renderer>; } @@ -47,5 +106,8 @@ pub mod widget { #[doc(no_inline)] pub use widget::*; +/// A generic widget. +/// +/// This is an alias of an `iced_native` element with a default `Renderer`. pub type Element<'a, Message> = iced_winit::Element<'a, Message, iced_wgpu::Renderer>; diff --git a/src/sandbox.rs b/src/sandbox.rs index 8ff374f7..698578f4 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,16 +1,126 @@ use crate::{Application, Command, Element}; +/// A sandboxed [`Application`]. +/// +/// A [`Sandbox`] is just an [`Application`] that cannot run any asynchronous +/// actions. +/// +/// If you do not need to leverage a [`Command`], you can use a [`Sandbox`] +/// instead of returning a [`Command::none`] everywhere. +/// +/// [`Application`]: trait.Application.html +/// [`Sandbox`]: trait.Sandbox.html +/// [`Command`]: struct.Command.html +/// [`Command::none`]: struct.Command.html#method.none +/// +/// # Example +/// We can use a [`Sandbox`] to run the [`Counter` example we implemented +/// before](index.html#overview), instead of an [`Application`]. We just need +/// to remove the use of [`Command`]: +/// +/// ```no_run +/// use iced::{button, Button, Column, Element, Sandbox, Text}; +/// +/// pub fn main() { +/// Counter::run() +/// } +/// +/// #[derive(Default)] +/// struct Counter { +/// value: i32, +/// increment_button: button::State, +/// decrement_button: button::State, +/// } +/// +/// #[derive(Debug, Clone, Copy)] +/// enum Message { +/// IncrementPressed, +/// DecrementPressed, +/// } +/// +/// impl Sandbox for Counter { +/// type Message = Message; +/// +/// fn new() -> Self { +/// Self::default() +/// } +/// +/// fn title(&self) -> String { +/// String::from("A simple counter") +/// } +/// +/// fn update(&mut self, message: Message) { +/// match message { +/// Message::IncrementPressed => { +/// self.value += 1; +/// } +/// Message::DecrementPressed => { +/// self.value -= 1; +/// } +/// } +/// } +/// +/// fn view(&mut self) -> Element<Message> { +/// Column::new() +/// .push( +/// Button::new(&mut self.increment_button, Text::new("Increment")) +/// .on_press(Message::IncrementPressed), +/// ) +/// .push( +/// Text::new(self.value.to_string()).size(50), +/// ) +/// .push( +/// Button::new(&mut self.decrement_button, Text::new("Decrement")) +/// .on_press(Message::DecrementPressed), +/// ) +/// .into() +/// } +/// } +/// ``` pub trait Sandbox { + /// The type of __messages__ your [`Sandbox`] will produce. + /// + /// [`Sandbox`]: trait.Sandbox.html type Message: std::fmt::Debug + Send; + /// Initializes the [`Sandbox`]. + /// + /// Here is where you should return the initial state of your app. + /// + /// [`Sandbox`]: trait.Sandbox.html fn new() -> Self; + /// Returns the current title of the [`Sandbox`]. + /// + /// This title can be dynamic! The runtime will automatically update the + /// title of your application when necessary. + /// + /// [`Sandbox`]: trait.Sandbox.html fn title(&self) -> String; + /// Handles a __message__ and updates the state of the [`Sandbox`]. + /// + /// This is where you define your __update logic__. All the __messages__, + /// produced by user interactions, will be handled by this method. + /// + /// [`Sandbox`]: trait.Sandbox.html fn update(&mut self, message: Self::Message); - fn view(&mut self) -> Element<Self::Message>; + /// Returns the widgets to display in the [`Sandbox`]. + /// + /// These widgets can produce __messages__ based on user interaction. + /// + /// [`Sandbox`]: trait.Sandbox.html + fn view(&mut self) -> Element<'_, Self::Message>; + /// Runs the [`Sandbox`]. + /// + /// This method will take control of the current thread and __will NOT + /// return__. + /// + /// It should probably be that last thing you call in your `main` function. + /// + /// [`Sandbox`]: trait.Sandbox.html fn run() where Self: 'static + Sized, @@ -39,7 +149,7 @@ where Command::none() } - fn view(&mut self) -> Element<T::Message> { + fn view(&mut self) -> Element<'_, T::Message> { T::view(self) } } diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs index 3740af13..1183a3cd 100644 --- a/web/src/widget/text.rs +++ b/web/src/widget/text.rs @@ -30,9 +30,9 @@ impl Text { /// Create a new fragment of [`Text`] with the given contents. /// /// [`Text`]: struct.Text.html - pub fn new(label: &str) -> Self { + pub fn new<T: Into<String>>(label: T) -> Self { Text { - content: String::from(label), + content: label.into(), size: None, color: None, font: Font::Default,