Write docs for iced and iced_native

This commit is contained in:
Héctor Ramón Jiménez 2019-11-22 19:36:57 +01:00
parent ba56a561b2
commit a7dba612f0
30 changed files with 877 additions and 214 deletions

View File

@ -1,10 +1,4 @@
/// Alignment on the cross axis of a container. /// Alignment on an unspecified 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
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Align { pub enum Align {
/// Align at the start of the cross axis. /// Align at the start of the cross axis.

View File

@ -2,7 +2,7 @@ use futures::future::{BoxFuture, Future, FutureExt};
/// A collection of async operations. /// 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`]. /// using the `From` trait or [`Command::perform`].
/// ///
/// [`Command`]: struct.Command.html /// [`Command`]: struct.Command.html

View File

@ -4,6 +4,8 @@
//! different runtime implementations. For instance, both [`iced_native`] and //! different runtime implementations. For instance, both [`iced_native`] and
//! [`iced_web`] are built on top of `iced_core`. //! [`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]: https://github.com/hecrj/iced
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native //! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
//! [`iced_web`]: https://github.com/hecrj/iced/tree/master/web //! [`iced_web`]: https://github.com/hecrj/iced/tree/master/web

View File

@ -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`]. /// Applies a transformation to the produced message of the [`Element`].
/// ///
/// This method is useful when you want to decouple different parts of your /// 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) { pub(crate) fn hash_layout(&self, state: &mut Hasher) {
self.widget.hash_layout(state); self.widget.hash_layout(state);
} }

View File

@ -34,11 +34,16 @@ pub enum Event {
}, },
/// The mouse wheel was scrolled. /// The mouse wheel was scrolled.
WheelScrolled { delta: ScrollDelta }, WheelScrolled {
/// The scroll movement.
delta: ScrollDelta,
},
} }
/// A scroll movement.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScrollDelta { pub enum ScrollDelta {
/// A line-based scroll movement
Lines { Lines {
/// The number of horizontal lines scrolled /// The number of horizontal lines scrolled
x: f32, x: f32,
@ -46,6 +51,7 @@ pub enum ScrollDelta {
/// The number of vertical lines scrolled /// The number of vertical lines scrolled
y: f32, y: f32,
}, },
/// A pixel-based scroll movement
Pixels { Pixels {
/// The number of horizontal pixels scrolled /// The number of horizontal pixels scrolled
x: f32, x: f32,

View File

@ -1,3 +1,4 @@
//! Position your widgets properly.
mod limits; mod limits;
mod node; mod node;
@ -8,6 +9,9 @@ pub use node::Node;
use crate::{Point, Rectangle, Vector}; use crate::{Point, Rectangle, Vector};
/// The bounds of a [`Node`] and its children, using absolute coordinates.
///
/// [`Node`]: struct.Node.html
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Layout<'a> { pub struct Layout<'a> {
position: Point, 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 { pub fn bounds(&self) -> Rectangle {
let bounds = self.node.bounds(); 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<Item = Layout<'a>> { pub fn children(&'a self) -> impl Iterator<Item = Layout<'a>> {
self.node.children().iter().map(move |node| { self.node.children().iter().map(move |node| {
Layout::with_offset( Layout::with_offset(

View File

@ -1,3 +1,4 @@
//! Distribute elements using a flex-based layout.
// This code is heavily inspired by the [`druid`] codebase. // This code is heavily inspired by the [`druid`] codebase.
// //
// [`druid`]: https://github.com/xi-editor/druid // [`druid`]: https://github.com/xi-editor/druid
@ -20,9 +21,13 @@ use crate::{
Align, Element, Size, Align, Element, Size,
}; };
/// The main axis of a flex layout.
#[derive(Debug)] #[derive(Debug)]
pub enum Axis { pub enum Axis {
/// The horizontal axis
Horizontal, Horizontal,
/// The vertical axis
Vertical, 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<Message, Renderer>( pub fn resolve<Message, Renderer>(
axis: Axis, axis: Axis,
renderer: &Renderer, renderer: &Renderer,
@ -57,7 +67,7 @@ pub fn resolve<Message, Renderer>(
padding: f32, padding: f32,
spacing: f32, spacing: f32,
align_items: Align, align_items: Align,
children: &[Element<'_, Message, Renderer>], items: &[Element<'_, Message, Renderer>],
) -> Node ) -> Node
where where
Renderer: crate::Renderer, Renderer: crate::Renderer,
@ -65,14 +75,14 @@ where
let limits = limits.pad(padding); let limits = limits.pad(padding);
let mut total_non_fill = 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 fill_sum = 0;
let mut cross = axis.cross(limits.min()); let mut cross = axis.cross(limits.min());
let mut nodes: Vec<Node> = Vec::with_capacity(children.len()); let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
nodes.resize(children.len(), Node::default()); 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 { let fill_factor = match axis {
Axis::Horizontal => child.width(), Axis::Horizontal => child.width(),
Axis::Vertical => child.height(), Axis::Vertical => child.height(),
@ -97,7 +107,7 @@ where
let available = axis.main(limits.max()); let available = axis.main(limits.max());
let remaining = (available - total_non_fill).max(0.0); 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 { let fill_factor = match axis {
Axis::Horizontal => child.width(), Axis::Horizontal => child.width(),
Axis::Vertical => child.height(), Axis::Vertical => child.height(),

View File

@ -1,5 +1,6 @@
use crate::{Length, Size}; use crate::{Length, Size};
/// A set of size constraints for layouting.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Limits { pub struct Limits {
min: Size, min: Size,
@ -8,12 +9,17 @@ pub struct Limits {
} }
impl Limits { impl Limits {
/// No limits
pub const NONE: Limits = Limits { pub const NONE: Limits = Limits {
min: Size::ZERO, min: Size::ZERO,
max: Size::INFINITY, max: Size::INFINITY,
fill: 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 { pub fn new(min: Size, max: Size) -> Limits {
Limits { Limits {
min, 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 { pub fn min(&self) -> Size {
self.min self.min
} }
/// Returns the maximum [`Size`] of the [`Limits`].
///
/// [`Limits`]: struct.Limits.html
/// [`Size`]: ../struct.Size.html
pub fn max(&self) -> Size { pub fn max(&self) -> Size {
self.max self.max
} }
/// Applies a width constraint to the current [`Limits`].
///
/// [`Limits`]: struct.Limits.html
pub fn width(mut self, width: Length) -> Limits { pub fn width(mut self, width: Length) -> Limits {
match width { match width {
Length::Shrink => { Length::Shrink => {
@ -51,6 +68,9 @@ impl Limits {
self self
} }
/// Applies a height constraint to the current [`Limits`].
///
/// [`Limits`]: struct.Limits.html
pub fn height(mut self, height: Length) -> Limits { pub fn height(mut self, height: Length) -> Limits {
match height { match height {
Length::Shrink => { Length::Shrink => {
@ -72,6 +92,9 @@ impl Limits {
self self
} }
/// Applies a minimum width constraint to the current [`Limits`].
///
/// [`Limits`]: struct.Limits.html
pub fn min_width(mut self, min_width: u32) -> Limits { pub fn min_width(mut self, min_width: u32) -> Limits {
self.min.width = self.min.width =
self.min.width.max(min_width as f32).min(self.max.width); self.min.width.max(min_width as f32).min(self.max.width);
@ -79,6 +102,9 @@ impl Limits {
self self
} }
/// Applies a maximum width constraint to the current [`Limits`].
///
/// [`Limits`]: struct.Limits.html
pub fn max_width(mut self, max_width: u32) -> Limits { pub fn max_width(mut self, max_width: u32) -> Limits {
self.max.width = self.max.width =
self.max.width.min(max_width as f32).max(self.min.width); self.max.width.min(max_width as f32).max(self.min.width);
@ -86,6 +112,19 @@ impl Limits {
self 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 { pub fn max_height(mut self, max_height: u32) -> Limits {
self.max.height = self.max.height =
self.max.height.min(max_height as f32).max(self.min.height); self.max.height.min(max_height as f32).max(self.min.height);
@ -93,10 +132,17 @@ impl Limits {
self self
} }
/// Shrinks the current [`Limits`] to account for the given padding.
///
/// [`Limits`]: struct.Limits.html
pub fn pad(&self, padding: f32) -> Limits { pub fn pad(&self, padding: f32) -> Limits {
self.shrink(Size::new(padding * 2.0, padding * 2.0)) 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 { pub fn shrink(&self, size: Size) -> Limits {
let min = Size::new( let min = Size::new(
(self.min().width - size.width).max(0.0), (self.min().width - size.width).max(0.0),
@ -116,6 +162,9 @@ impl Limits {
Limits { min, max, fill } Limits { min, max, fill }
} }
/// Removes the minimum width constraint for the current [`Limits`].
///
/// [`Limits`]: struct.Limits.html
pub fn loose(&self) -> Limits { pub fn loose(&self) -> Limits {
Limits { Limits {
min: Size::ZERO, 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 { pub fn resolve(&self, intrinsic_size: Size) -> Size {
Size::new( Size::new(
intrinsic_size intrinsic_size

View File

@ -1,16 +1,25 @@
use crate::{Align, Rectangle, Size}; use crate::{Align, Rectangle, Size};
/// The bounds of an element and its children.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Node { pub struct Node {
pub bounds: Rectangle, pub(crate) bounds: Rectangle,
children: Vec<Node>, children: Vec<Node>,
} }
impl Node { impl Node {
/// Creates a new [`Node`] with the given [`Size`].
///
/// [`Node`]: struct.Node.html
/// [`Size`]: ../struct.Size.html
pub fn new(size: Size) -> Self { pub fn new(size: Size) -> Self {
Self::with_children(size, Vec::new()) 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<Node>) -> Self { pub fn with_children(size: Size, children: Vec<Node>) -> Self {
Node { Node {
bounds: Rectangle { 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 { pub fn size(&self) -> Size {
Size::new(self.bounds.width, self.bounds.height) Size::new(self.bounds.width, self.bounds.height)
} }
/// Returns the bounds of the [`Node`].
///
/// [`Node`]: struct.Node.html
pub fn bounds(&self) -> Rectangle { pub fn bounds(&self) -> Rectangle {
self.bounds self.bounds
} }
/// Returns the children of the [`Node`].
///
/// [`Node`]: struct.Node.html
pub fn children(&self) -> &[Node] { pub fn children(&self) -> &[Node] {
&self.children &self.children
} }
pub fn align( pub(crate) fn align(
&mut self, &mut self,
horizontal_alignment: Align, horizontal_alignment: Align,
vertical_alignment: Align, vertical_alignment: Align,

View File

@ -1,162 +1,34 @@
//! Iced is a renderer-agnostic GUI library focused on simplicity and //! A renderer-agnostic native GUI runtime.
//! type-safety. Inspired by [Elm].
//! //!
//! # Features //! ![`iced_native` crate graph](https://github.com/hecrj/iced/blob/cae26cb7bc627f4a5b3bcf1cd023a0c552e8c65e/docs/graphs/native.png?raw=true)
//! * 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! //! `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 //! - A custom layout engine, greatly inspired by [`druid`]
//! [repository]: https://github.com/hecrj/iced //! - 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 //! # 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<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, 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! //! Check out the [`UserInterface`] type to learn how to wire everything up!
//! //!
//! [Elm]: https://elm-lang.org/ //! [`iced_core`]: https://github.com/hecrj/iced/tree/master/core
//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/ //! [`iced_winit`]: https://github.com/hecrj/iced/tree/master/winit
//! [documentation]: https://docs.rs/iced //! [`druid`]: https://github.com/xi-editor/druid
//! [examples]: https://github.com/hecrj/iced/tree/master/examples //! [`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 //! [`UserInterface`]: struct.UserInterface.html
//#![deny(missing_docs)] #![deny(missing_docs)]
//#![deny(missing_debug_implementations)] //#![deny(missing_debug_implementations)]
#![deny(unused_results)] #![deny(unused_results)]
#![deny(unsafe_code)] #![deny(unsafe_code)]

View File

@ -32,9 +32,21 @@ pub use windowed::{Target, Windowed};
use crate::{layout, Element}; 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 { 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; 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>( fn layout<'a, Message>(
&mut self, &mut self,
element: &Element<'a, Message, Self>, element: &Element<'a, Message, Self>,

View File

@ -4,6 +4,7 @@ use crate::{
Rectangle, Renderer, Size, VerticalAlignment, Rectangle, Renderer, Size, VerticalAlignment,
}; };
/// A renderer that does nothing.
pub struct Null; pub struct Null;
impl Renderer for Null { impl Renderer for Null {

View File

@ -2,11 +2,21 @@ use crate::MouseCursor;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
/// A renderer that can target windows.
pub trait Windowed: super::Renderer + Sized { pub trait Windowed: super::Renderer + Sized {
/// The type of target.
type Target: Target<Renderer = Self>; type Target: Target<Renderer = Self>;
/// Creates a new [`Windowed`] renderer.
///
/// [`Windowed`]: trait.Windowed.html
fn new() -> Self; 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<T: AsRef<str>>( fn draw<T: AsRef<str>>(
&mut self, &mut self,
output: &Self::Output, output: &Self::Output,
@ -15,9 +25,15 @@ pub trait Windowed: super::Renderer + Sized {
) -> MouseCursor; ) -> MouseCursor;
} }
/// A rendering target.
pub trait Target { pub trait Target {
/// The renderer of this target.
type Renderer; type Renderer;
/// Creates a new rendering [`Target`] from the given window handle, width,
/// height and dpi factor.
///
/// [`Target`]: trait.Target.html
fn new<W: HasRawWindowHandle>( fn new<W: HasRawWindowHandle>(
window: &W, window: &W,
width: u16, width: u16,
@ -26,6 +42,9 @@ pub trait Target {
renderer: &Self::Renderer, renderer: &Self::Renderer,
) -> Self; ) -> Self;
/// Resizes the current [`Target`].
///
/// [`Target`]: trait.Target.html
fn resize( fn resize(
&mut self, &mut self,
width: u16, width: u16,

View File

@ -1,5 +1,6 @@
use std::f32; use std::f32;
/// An amount of space in 2 dimensions.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Size { pub struct Size {
/// The width. /// The width.
@ -9,13 +10,26 @@ pub struct Size {
} }
impl Size { impl Size {
/// A [`Size`] with zero width and height.
///
/// [`Size`]: struct.Size.html
pub const ZERO: Size = Size::new(0., 0.); 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); 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 { pub const fn new(width: f32, height: f32) -> Self {
Size { width, height } Size { width, height }
} }
/// Increments the [`Size`] to account for the given padding.
///
/// [`Size`]: struct.Size.html
pub fn pad(&self, padding: f32) -> Self { pub fn pad(&self, padding: f32) -> Self {
Size { Size {
width: self.width + padding * 2.0, width: self.width + padding * 2.0,

View File

@ -23,6 +23,7 @@
pub mod button; pub mod button;
pub mod checkbox; pub mod checkbox;
pub mod column; pub mod column;
pub mod container;
pub mod image; pub mod image;
pub mod radio; pub mod radio;
pub mod row; pub mod row;
@ -31,8 +32,6 @@ pub mod slider;
pub mod text; pub mod text;
pub mod text_input; pub mod text_input;
mod container;
#[doc(no_inline)] #[doc(no_inline)]
pub use button::Button; pub use button::Button;
#[doc(no_inline)] #[doc(no_inline)]
@ -69,8 +68,14 @@ pub trait Widget<Message, Renderer>
where where
Renderer: crate::Renderer, Renderer: crate::Renderer,
{ {
/// Returns the width of the [`Widget`].
///
/// [`Widget`]: trait.Widget.html
fn width(&self) -> Length; fn width(&self) -> Length;
/// Returns the height of the [`Widget`].
///
/// [`Widget`]: trait.Widget.html
fn height(&self) -> Length; fn height(&self) -> Length;
/// Returns the [`Node`] of the [`Widget`]. /// Returns the [`Node`] of the [`Widget`].

View File

@ -166,15 +166,18 @@ where
/// [`Checkbox`]: struct.Checkbox.html /// [`Checkbox`]: struct.Checkbox.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// Returns the default size of a [`Checkbox`].
///
/// [`Checkbox`]: struct.Checkbox.html
fn default_size(&self) -> u32; fn default_size(&self) -> u32;
/// Draws a [`Checkbox`]. /// Draws a [`Checkbox`].
/// ///
/// It receives: /// It receives:
/// * the current cursor position
/// * the bounds of the [`Checkbox`] /// * the bounds of the [`Checkbox`]
/// * the bounds of the label of the [`Checkbox`] /// * whether the [`Checkbox`] is selected or not
/// * whether the [`Checkbox`] is checked or not /// * whether the mouse is over the [`Checkbox`] or not
/// * the drawn label of the [`Checkbox`]
/// ///
/// [`Checkbox`]: struct.Checkbox.html /// [`Checkbox`]: struct.Checkbox.html
fn draw( fn draw(

View File

@ -1,3 +1,4 @@
//! Distribute content vertically.
use std::hash::Hash; use std::hash::Hash;
use crate::{ 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 { 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<Message>( fn draw<Message>(
&mut self, &mut self,
content: &[Element<'_, Message, Self>], content: &[Element<'_, Message, Self>],

View File

@ -1,3 +1,4 @@
//! Decorate content and apply alignment.
use std::hash::Hash; use std::hash::Hash;
use crate::{ use crate::{

View File

@ -116,6 +116,9 @@ where
/// [`Image`]: struct.Image.html /// [`Image`]: struct.Image.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { 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); fn dimensions(&self, path: &str) -> (u32, u32);
/// Draws an [`Image`]. /// Draws an [`Image`].

View File

@ -174,15 +174,18 @@ where
/// [`Radio`]: struct.Radio.html /// [`Radio`]: struct.Radio.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// Returns the default size of a [`Radio`] button.
///
/// [`Radio`]: struct.Radio.html
fn default_size(&self) -> u32; fn default_size(&self) -> u32;
/// Draws a [`Radio`] button. /// Draws a [`Radio`] button.
/// ///
/// It receives: /// It receives:
/// * the current cursor position
/// * the bounds of the [`Radio`] /// * the bounds of the [`Radio`]
/// * the bounds of the label of the [`Radio`]
/// * whether the [`Radio`] is selected or not /// * 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 /// [`Radio`]: struct.Radio.html
fn draw( fn draw(

View File

@ -1,3 +1,4 @@
//! Distribute content horizontally.
use std::hash::Hash; use std::hash::Hash;
use crate::{ 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 { 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<Message>( fn draw<Message>(
&mut self, &mut self,
children: &[Element<'_, Message, Self>], children: &[Element<'_, Message, Self>],

View File

@ -1,3 +1,4 @@
//! Navigate an endless amount of content with a scrollbar.
use crate::{ use crate::{
column, column,
input::{mouse, ButtonState}, 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 { 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( fn is_mouse_over_scrollbar(
&self, &self,
bounds: Rectangle, bounds: Rectangle,
@ -369,6 +381,18 @@ pub trait Renderer: crate::Renderer + Sized {
cursor_position: Point, cursor_position: Point,
) -> bool; ) -> 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( fn draw(
&mut self, &mut self,
scrollable: &State, scrollable: &State,

View File

@ -13,6 +13,28 @@ use crate::{
use std::{hash::Hash, ops::RangeInclusive}; 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> { pub struct Slider<'a, Message> {
state: &'a mut State, state: &'a mut State,
range: RangeInclusive<f32>, range: RangeInclusive<f32>,
@ -180,6 +202,9 @@ where
/// [`Slider`]: struct.Slider.html /// [`Slider`]: struct.Slider.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// Returns the height of the [`Slider`].
///
/// [`Slider`]: struct.Slider.html
fn height(&self) -> u32; fn height(&self) -> u32;
/// Draws a [`Slider`]. /// Draws a [`Slider`].

View File

@ -32,9 +32,9 @@ impl Text {
/// Create a new fragment of [`Text`] with the given contents. /// Create a new fragment of [`Text`] with the given contents.
/// ///
/// [`Text`]: struct.Text.html /// [`Text`]: struct.Text.html
pub fn new(label: &str) -> Self { pub fn new<T: Into<String>>(label: T) -> Self {
Text { Text {
content: String::from(label), content: label.into(),
size: None, size: None,
color: None, color: None,
font: Font::Default, font: Font::Default,
@ -174,8 +174,15 @@ where
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
/// [`UserInterface`]: ../../struct.UserInterface.html /// [`UserInterface`]: ../../struct.UserInterface.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// Returns the default size of the [`Text`].
///
/// [`Text`]: struct.Text.html
fn default_size(&self) -> u16; 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( fn measure(
&self, &self,
content: &str, content: &str,

View File

@ -10,7 +10,26 @@ use crate::{
Widget, 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> { pub struct TextInput<'a, Message> {
state: &'a mut State, state: &'a mut State,
placeholder: String, placeholder: String,
@ -26,7 +45,14 @@ pub struct TextInput<'a, Message> {
impl<'a, Message> TextInput<'a, Message> { impl<'a, Message> TextInput<'a, Message> {
/// Creates a new [`TextInput`]. /// 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 /// [`TextInput`]: struct.TextInput.html
/// [`State`]: struct.State.html
pub fn new<F>( pub fn new<F>(
state: &'a mut State, state: &'a mut State,
placeholder: &str, 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 { 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; 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( fn draw(
&mut self, &mut self,
bounds: Rectangle, 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 { pub fn is_focused(&self) -> bool {
self.is_focused self.is_focused
} }
@ -303,7 +355,7 @@ impl State {
/// Moves the cursor of a [`TextInput`] to the right. /// Moves the cursor of a [`TextInput`] to the right.
/// ///
/// [`TextInput`]: struct.TextInput.html /// [`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); let current = self.cursor_position(value);
if current < value.len() { if current < value.len() {
@ -314,7 +366,7 @@ impl State {
/// Moves the cursor of a [`TextInput`] to the left. /// Moves the cursor of a [`TextInput`] to the left.
/// ///
/// [`TextInput`]: struct.TextInput.html /// [`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); let current = self.cursor_position(value);
if current > 0 { if current > 0 {
@ -325,7 +377,7 @@ impl State {
/// Moves the cursor of a [`TextInput`] to the end. /// Moves the cursor of a [`TextInput`] to the end.
/// ///
/// [`TextInput`]: struct.TextInput.html /// [`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(); self.cursor_position = value.len();
} }
} }

View File

@ -1,19 +1,140 @@
use crate::{Command, Element}; 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 `<title>` 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 { pub trait Application: Sized {
/// The type of __messages__ your [`Application`] will produce.
///
/// [`Application`]: trait.Application.html
type Message: std::fmt::Debug + Send; 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>); 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; 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 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() fn run()
where where
Self: 'static + Sized, Self: 'static,
{ {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
<Instance<Self> as iced_winit::Application>::run(); <Instance<Self> as iced_winit::Application>::run();
@ -47,7 +168,7 @@ where
self.0.update(message) self.0.update(message)
} }
fn view(&mut self) -> Element<Self::Message> { fn view(&mut self) -> Element<'_, Self::Message> {
self.0.view() self.0.view()
} }
} }

View File

@ -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; mod application;
#[cfg_attr(target_arch = "wasm32", path = "web.rs")] #[cfg_attr(target_arch = "wasm32", path = "web.rs")]
#[cfg_attr(not(target_arch = "wasm32"), path = "native.rs")] #[cfg_attr(not(target_arch = "wasm32"), path = "native.rs")]

View File

@ -4,7 +4,36 @@ pub use iced_winit::{
}; };
pub mod widget { 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 { 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> = pub type Button<'a, Message> =
iced_winit::Button<'a, Message, iced_wgpu::Renderer>; iced_winit::Button<'a, Message, iced_wgpu::Renderer>;
@ -12,6 +41,13 @@ pub mod widget {
} }
pub mod scrollable { 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> = pub type Scrollable<'a, Message> =
iced_winit::Scrollable<'a, Message, iced_wgpu::Renderer>; iced_winit::Scrollable<'a, Message, iced_wgpu::Renderer>;
@ -19,10 +55,23 @@ pub mod widget {
} }
pub mod text_input { 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 use iced_winit::text_input::{State, TextInput};
} }
pub mod slider { 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}; pub use iced_winit::slider::{Slider, State};
} }
@ -34,12 +83,22 @@ pub mod widget {
text_input::TextInput, 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> = pub type Column<'a, Message> =
iced_winit::Column<'a, Message, iced_wgpu::Renderer>; 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> = pub type Row<'a, Message> =
iced_winit::Row<'a, Message, iced_wgpu::Renderer>; 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> = pub type Container<'a, Message> =
iced_winit::Container<'a, Message, iced_wgpu::Renderer>; iced_winit::Container<'a, Message, iced_wgpu::Renderer>;
} }
@ -47,5 +106,8 @@ pub mod widget {
#[doc(no_inline)] #[doc(no_inline)]
pub use widget::*; pub use widget::*;
/// A generic widget.
///
/// This is an alias of an `iced_native` element with a default `Renderer`.
pub type Element<'a, Message> = pub type Element<'a, Message> =
iced_winit::Element<'a, Message, iced_wgpu::Renderer>; iced_winit::Element<'a, Message, iced_wgpu::Renderer>;

View File

@ -1,16 +1,126 @@
use crate::{Application, Command, Element}; 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 { pub trait Sandbox {
/// The type of __messages__ your [`Sandbox`] will produce.
///
/// [`Sandbox`]: trait.Sandbox.html
type Message: std::fmt::Debug + Send; 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; 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; 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 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() fn run()
where where
Self: 'static + Sized, Self: 'static + Sized,
@ -39,7 +149,7 @@ where
Command::none() Command::none()
} }
fn view(&mut self) -> Element<T::Message> { fn view(&mut self) -> Element<'_, T::Message> {
T::view(self) T::view(self)
} }
} }

View File

@ -30,9 +30,9 @@ impl Text {
/// Create a new fragment of [`Text`] with the given contents. /// Create a new fragment of [`Text`] with the given contents.
/// ///
/// [`Text`]: struct.Text.html /// [`Text`]: struct.Text.html
pub fn new(label: &str) -> Self { pub fn new<T: Into<String>>(label: T) -> Self {
Text { Text {
content: String::from(label), content: label.into(),
size: None, size: None,
color: None, color: None,
font: Font::Default, font: Font::Default,