diff options
author | 2019-11-24 11:34:30 +0100 | |
---|---|---|
committer | 2019-11-24 11:34:30 +0100 | |
commit | 149fd2aa1fa86858c7c1dcec8fd844caa78cec94 (patch) | |
tree | a199cf8d2caaf6aa60e48e93d6dd0688969d43b0 /web/src | |
parent | 9712b319bb7a32848001b96bd84977430f14b623 (diff) | |
parent | 47196c9007d12d3b3e0036ffabe3bf6d14ff4523 (diff) | |
download | iced-149fd2aa1fa86858c7c1dcec8fd844caa78cec94.tar.gz iced-149fd2aa1fa86858c7c1dcec8fd844caa78cec94.tar.bz2 iced-149fd2aa1fa86858c7c1dcec8fd844caa78cec94.zip |
Merge pull request #65 from hecrj/improvement/docs
Documentation
Diffstat (limited to 'web/src')
-rw-r--r-- | web/src/bus.rs | 15 | ||||
-rw-r--r-- | web/src/element.rs | 48 | ||||
-rw-r--r-- | web/src/lib.rs | 95 | ||||
-rw-r--r-- | web/src/widget.rs | 27 | ||||
-rw-r--r-- | web/src/widget/button.rs | 119 | ||||
-rw-r--r-- | web/src/widget/checkbox.rs | 59 | ||||
-rw-r--r-- | web/src/widget/column.rs | 107 | ||||
-rw-r--r-- | web/src/widget/image.rs | 50 | ||||
-rw-r--r-- | web/src/widget/radio.rs | 68 | ||||
-rw-r--r-- | web/src/widget/row.rs | 108 | ||||
-rw-r--r-- | web/src/widget/slider.rs | 95 | ||||
-rw-r--r-- | web/src/widget/text.rs | 108 |
12 files changed, 866 insertions, 33 deletions
diff --git a/web/src/bus.rs b/web/src/bus.rs index b4fd67c7..09908679 100644 --- a/web/src/bus.rs +++ b/web/src/bus.rs @@ -2,6 +2,12 @@ use crate::Instance; use std::rc::Rc; +/// A publisher of messages. +/// +/// It can be used to route messages back to the [`Application`]. +/// +/// [`Application`]: trait.Application.html +#[allow(missing_debug_implementations)] #[derive(Clone)] pub struct Bus<Message> { publish: Rc<Box<dyn Fn(Message, &mut dyn dodrio::RootRender)>>, @@ -11,7 +17,7 @@ impl<Message> Bus<Message> where Message: 'static, { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { publish: Rc::new(Box::new(|message, root| { let app = root.unwrap_mut::<Instance<Message>>(); @@ -21,10 +27,17 @@ where } } + /// Publishes a new message for the [`Application`]. + /// + /// [`Application`]: trait.Application.html pub fn publish(&self, message: Message, root: &mut dyn dodrio::RootRender) { (self.publish)(message, root); } + /// Creates a new [`Bus`] that applies the given function to the messages + /// before publishing. + /// + /// [`Bus`]: struct.Bus.html pub fn map<B>(&self, mapper: Rc<Box<dyn Fn(B) -> Message>>) -> Bus<B> where B: 'static, diff --git a/web/src/element.rs b/web/src/element.rs index a2b78c69..fcf0a4b6 100644 --- a/web/src/element.rs +++ b/web/src/element.rs @@ -3,29 +3,39 @@ use crate::{Bus, Color, Widget}; use dodrio::bumpalo; use std::rc::Rc; +/// A generic [`Widget`]. +/// +/// It is useful to build composable user interfaces that do not leak +/// implementation details in their __view logic__. +/// +/// If you have a [built-in widget], you should be able to use `Into<Element>` +/// to turn it into an [`Element`]. +/// +/// [built-in widget]: widget/index.html +/// [`Widget`]: widget/trait.Widget.html +/// [`Element`]: struct.Element.html +#[allow(missing_debug_implementations)] pub struct Element<'a, Message> { pub(crate) widget: Box<dyn Widget<Message> + 'a>, } impl<'a, Message> Element<'a, Message> { + /// Create a new [`Element`] containing the given [`Widget`]. + /// + /// [`Element`]: struct.Element.html + /// [`Widget`]: widget/trait.Widget.html pub fn new(widget: impl Widget<Message> + 'a) -> Self { Self { widget: Box::new(widget), } } - pub fn node<'b>( - &self, - bump: &'b bumpalo::Bump, - bus: &Bus<Message>, - ) -> dodrio::Node<'b> { - self.widget.node(bump, bus) - } - - pub fn explain(self, _color: Color) -> Element<'a, Message> { - self - } - + /// Applies a transformation to the produced message of the [`Element`]. + /// + /// This method is useful when you want to decouple different parts of your + /// UI and make them __composable__. + /// + /// [`Element`]: struct.Element.html pub fn map<F, B>(self, f: F) -> Element<'a, B> where Message: 'static, @@ -36,6 +46,20 @@ impl<'a, Message> Element<'a, Message> { widget: Box::new(Map::new(self.widget, f)), } } + + /// Marks the [`Element`] as _to-be-explained_. + pub fn explain(self, _color: Color) -> Element<'a, Message> { + self + } + + /// Produces a VDOM node for the [`Element`]. + pub fn node<'b>( + &self, + bump: &'b bumpalo::Bump, + bus: &Bus<Message>, + ) -> dodrio::Node<'b> { + self.widget.node(bump, bus) + } } struct Map<'a, A, B> { diff --git a/web/src/lib.rs b/web/src/lib.rs index 6252f2be..77a963ba 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -1,3 +1,61 @@ +//! A web runtime for Iced, targetting the DOM. +//! +//!  +//! +//! `iced_web` takes [`iced_core`] and builds a WebAssembly runtime on top. It +//! achieves this by introducing a `Widget` trait that can be used to produce +//! VDOM nodes. +//! +//! The crate is currently a __very experimental__, simple abstraction layer +//! over [`dodrio`]. +//! +//! [`iced_core`]: https://github.com/hecrj/iced/tree/master/core +//! [`dodrio`]: https://github.com/fitzgen/dodrio +//! +//! # Usage +//! The current build process is a bit involved, as [`wasm-pack`] does not +//! currently [support building binary crates](https://github.com/rustwasm/wasm-pack/issues/734). +//! +//! Therefore, we instead build using the `wasm32-unknown-unknown` target and +//! use the [`wasm-bindgen`] CLI to generate appropriate bindings. +//! +//! For instance, let's say we want to build the [`tour` example]: +//! +//! ```bash +//! cd examples +//! cargo build --example tour --target wasm32-unknown-unknown +//! wasm-bindgen ../target/wasm32-unknown-unknown/debug/examples/tour.wasm --out-dir tour --web +//! ``` +//! +//! Then, we need to create an `.html` file to load our application: +//! +//! ```html +//! <!DOCTYPE html> +//! <html> +//! <head> +//! <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> +//! <title>Tour - Iced</title> +//! </head> +//! <body> +//! <script type="module"> +//! import init from "./tour/tour.js"; +//! +//! init('./tour/tour_bg.wasm'); +//! </script> +//! </body> +//! </html> +//! ``` +//! +//! Finally, we serve it using an HTTP server and access it with our browser. +//! +//! [`wasm-pack`]: https://github.com/rustwasm/wasm-pack +//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen +//! [`tour` example]: https://github.com/hecrj/iced/blob/master/examples/tour.rs +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![deny(unused_results)] +#![deny(unsafe_code)] +#![deny(rust_2018_idioms)] use dodrio::bumpalo; use std::cell::RefCell; @@ -6,17 +64,50 @@ mod element; pub mod widget; pub use bus::Bus; +pub use dodrio; pub use element::Element; -pub use iced_core::{Align, Background, Color, Length}; +pub use iced_core::{ + Align, Background, Color, Font, HorizontalAlignment, Length, + VerticalAlignment, +}; pub use widget::*; +/// An interactive web application. +/// +/// This trait is the main entrypoint of Iced. Once implemented, you can run +/// your GUI application by simply calling [`run`](#method.run). 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. pub trait Application { + /// The type of __messages__ your [`Application`] will produce. + /// + /// [`Application`]: trait.Application.html type Message; + /// 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); - 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`]. + /// + /// [`Application`]: trait.Application.html fn run(self) where Self: 'static + Sized, diff --git a/web/src/widget.rs b/web/src/widget.rs index 88b2efc9..30ac8eeb 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -1,15 +1,31 @@ +//! Use the built-in widgets or create your own. +//! +//! # Custom widgets +//! If you want to implement a custom widget, you simply need to implement the +//! [`Widget`] trait. You can use the API of the built-in widgets as a guide or +//! source of inspiration. +//! +//! # Re-exports +//! For convenience, the contents of this module are available at the root +//! module. Therefore, you can directly type: +//! +//! ``` +//! use iced_web::{button, Button, Widget}; +//! ``` +//! +//! [`Widget`]: trait.Widget.html use crate::Bus; use dodrio::bumpalo; pub mod button; pub mod slider; -pub mod text; mod checkbox; mod column; mod image; mod radio; mod row; +mod text; #[doc(no_inline)] pub use button::Button; @@ -26,7 +42,16 @@ pub use image::Image; pub use radio::Radio; pub use row::Row; +/// A component that displays information and allows interaction. +/// +/// If you want to build your own widgets, you will need to implement this +/// trait. +/// +/// [`Widget`]: trait.Widget.html pub trait Widget<Message> { + /// Produces a VDOM node for the [`Widget`]. + /// + /// [`Widget`]: trait.Widget.html fn node<'b>( &self, bump: &'b bumpalo::Bump, diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index 257034a7..1c13f34d 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -1,11 +1,122 @@ -use crate::{Bus, Element, Widget}; +//! Allow your users to perform actions by pressing a button. +//! +//! A [`Button`] has some local [`State`]. +//! +//! [`Button`]: struct.Button.html +//! [`State`]: struct.State.html +use crate::{Background, Bus, Element, Length, Widget}; use dodrio::bumpalo; -pub use iced_core::button::State; +/// A generic widget that produces a message when pressed. +/// +/// ``` +/// # use iced_web::{button, Button, Text}; +/// # +/// enum Message { +/// ButtonPressed, +/// } +/// +/// let mut state = button::State::new(); +/// let button = Button::new(&mut state, Text::new("Press me!")) +/// .on_press(Message::ButtonPressed); +/// ``` +#[allow(missing_debug_implementations)] +pub struct Button<'a, Message> { + content: Element<'a, Message>, + on_press: Option<Message>, + width: Length, + min_width: u32, + padding: u16, + background: Option<Background>, + border_radius: u16, +} + +impl<'a, Message> Button<'a, Message> { + /// Creates a new [`Button`] with some local [`State`] and the given + /// content. + /// + /// [`Button`]: struct.Button.html + /// [`State`]: struct.State.html + pub fn new<E>(_state: &'a mut State, content: E) -> Self + where + E: Into<Element<'a, Message>>, + { + Button { + content: content.into(), + on_press: None, + width: Length::Shrink, + min_width: 0, + padding: 0, + background: None, + border_radius: 0, + } + } + + /// Sets the width of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the minimum width of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn min_width(mut self, min_width: u32) -> Self { + self.min_width = min_width; + self + } + + /// Sets the padding of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn padding(mut self, padding: u16) -> Self { + self.padding = padding; + self + } -pub type Button<'a, Message> = - iced_core::Button<'a, Message, Element<'a, Message>>; + /// Sets the [`Background`] of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + /// [`Background`]: ../../struct.Background.html + pub fn background(mut self, background: Background) -> Self { + self.background = Some(background); + self + } + + /// Sets the border radius of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn border_radius(mut self, border_radius: u16) -> Self { + self.border_radius = border_radius; + self + } + + /// Sets the message that will be produced when the [`Button`] is pressed. + /// + /// [`Button`]: struct.Button.html + pub fn on_press(mut self, msg: Message) -> Self { + self.on_press = Some(msg); + self + } +} + +/// The local state of a [`Button`]. +/// +/// [`Button`]: struct.Button.html +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct State; + +impl State { + /// Creates a new [`State`]. + /// + /// [`State`]: struct.State.html + pub fn new() -> State { + State::default() + } +} impl<'a, Message> Widget<Message> for Button<'a, Message> where diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 72f0a2aa..94b42554 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -1,8 +1,63 @@ -use crate::{Bus, Element, Widget}; +use crate::{Bus, Color, Element, Widget}; use dodrio::bumpalo; -pub use iced_core::Checkbox; +/// A box that can be checked. +/// +/// # Example +/// +/// ``` +/// # use iced_web::Checkbox; +/// +/// pub enum Message { +/// CheckboxToggled(bool), +/// } +/// +/// let is_checked = true; +/// +/// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled); +/// ``` +/// +///  +#[allow(missing_debug_implementations)] +pub struct Checkbox<Message> { + is_checked: bool, + on_toggle: Box<dyn Fn(bool) -> Message>, + label: String, + label_color: Option<Color>, +} + +impl<Message> Checkbox<Message> { + /// Creates a new [`Checkbox`]. + /// + /// It expects: + /// * a boolean describing whether the [`Checkbox`] is checked or not + /// * the label of the [`Checkbox`] + /// * a function that will be called when the [`Checkbox`] is toggled. It + /// will receive the new state of the [`Checkbox`] and must produce a + /// `Message`. + /// + /// [`Checkbox`]: struct.Checkbox.html + pub fn new<F>(is_checked: bool, label: &str, f: F) -> Self + where + F: 'static + Fn(bool) -> Message, + { + Checkbox { + is_checked, + on_toggle: Box::new(f), + label: String::from(label), + label_color: None, + } + } + + /// Sets the color of the label of the [`Checkbox`]. + /// + /// [`Checkbox`]: struct.Checkbox.html + pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self { + self.label_color = Some(color.into()); + self + } +} impl<Message> Widget<Message> for Checkbox<Message> where diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs index becd6bc6..ee8c14fa 100644 --- a/web/src/widget/column.rs +++ b/web/src/widget/column.rs @@ -1,8 +1,111 @@ -use crate::{Bus, Element, Widget}; +use crate::{Align, Bus, Element, Length, Widget}; use dodrio::bumpalo; +use std::u32; -pub type Column<'a, Message> = iced_core::Column<Element<'a, Message>>; +/// A container that distributes its contents vertically. +/// +/// A [`Column`] will try to fill the horizontal space of its container. +/// +/// [`Column`]: struct.Column.html +#[allow(missing_debug_implementations)] +pub struct Column<'a, Message> { + spacing: u16, + padding: u16, + width: Length, + height: Length, + max_width: u32, + max_height: u32, + align_items: Align, + children: Vec<Element<'a, Message>>, +} + +impl<'a, Message> Column<'a, Message> { + /// Creates an empty [`Column`]. + /// + /// [`Column`]: struct.Column.html + pub fn new() -> Self { + Column { + spacing: 0, + padding: 0, + width: Length::Fill, + height: Length::Shrink, + max_width: u32::MAX, + max_height: u32::MAX, + align_items: Align::Start, + children: Vec::new(), + } + } + + /// Sets the vertical spacing _between_ elements. + /// + /// Custom margins per element do not exist in Iced. You should use this + /// method instead! While less flexible, it helps you keep spacing between + /// elements consistent. + pub fn spacing(mut self, units: u16) -> Self { + self.spacing = units; + self + } + + /// Sets the padding of the [`Column`]. + /// + /// [`Column`]: struct.Column.html + pub fn padding(mut self, units: u16) -> Self { + self.padding = units; + self + } + + /// Sets the width of the [`Column`]. + /// + /// [`Column`]: struct.Column.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`Column`]. + /// + /// [`Column`]: struct.Column.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } + + /// Sets the maximum width of the [`Column`]. + /// + /// [`Column`]: struct.Column.html + pub fn max_width(mut self, max_width: u32) -> Self { + self.max_width = max_width; + self + } + + /// Sets the maximum height of the [`Column`] in pixels. + /// + /// [`Column`]: struct.Column.html + pub fn max_height(mut self, max_height: u32) -> Self { + self.max_height = max_height; + self + } + + /// Sets the horizontal alignment of the contents of the [`Column`] . + /// + /// [`Column`]: struct.Column.html + pub fn align_items(mut self, align: Align) -> Self { + self.align_items = align; + self + } + + /// Adds an element to the [`Column`]. + /// + /// [`Column`]: struct.Column.html + pub fn push<E>(mut self, child: E) -> Self + where + E: Into<Element<'a, Message>>, + { + self.children.push(child.into()); + self + } +} impl<'a, Message> Widget<Message> for Column<'a, Message> { fn node<'b>( diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs index bd3e5daf..ab510bdb 100644 --- a/web/src/widget/image.rs +++ b/web/src/widget/image.rs @@ -2,7 +2,55 @@ use crate::{Bus, Element, Length, Widget}; use dodrio::bumpalo; -pub use iced_core::Image; +/// A frame that displays an image while keeping aspect ratio. +/// +/// # Example +/// +/// ``` +/// # use iced_web::Image; +/// +/// let image = Image::new("resources/ferris.png"); +/// ``` +#[derive(Debug)] +pub struct Image { + /// The image path + pub path: String, + + /// The width of the image + pub width: Length, + + /// The height of the image + pub height: Length, +} + +impl Image { + /// Creates a new [`Image`] with the given path. + /// + /// [`Image`]: struct.Image.html + pub fn new<T: Into<String>>(path: T) -> Self { + Image { + path: path.into(), + width: Length::Shrink, + height: Length::Shrink, + } + } + + /// Sets the width of the [`Image`] boundaries. + /// + /// [`Image`]: struct.Image.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`Image`] boundaries. + /// + /// [`Image`]: struct.Image.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } +} impl<Message> Widget<Message> for Image { fn node<'b>( diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index d249ad26..32532ebe 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -1,8 +1,72 @@ -use crate::{Bus, Element, Widget}; +use crate::{Bus, Color, Element, Widget}; use dodrio::bumpalo; -pub use iced_core::Radio; +/// A circular button representing a choice. +/// +/// # Example +/// ``` +/// # use iced_web::Radio; +/// +/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// pub enum Choice { +/// A, +/// B, +/// } +/// +/// #[derive(Debug, Clone, Copy)] +/// pub enum Message { +/// RadioSelected(Choice), +/// } +/// +/// let selected_choice = Some(Choice::A); +/// +/// Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected); +/// +/// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected); +/// ``` +/// +///  +#[allow(missing_debug_implementations)] +pub struct Radio<Message> { + is_selected: bool, + on_click: Message, + label: String, + label_color: Option<Color>, +} + +impl<Message> Radio<Message> { + /// Creates a new [`Radio`] button. + /// + /// It expects: + /// * the value related to the [`Radio`] button + /// * the label of the [`Radio`] button + /// * the current selected value + /// * a function that will be called when the [`Radio`] is selected. It + /// receives the value of the radio and must produce a `Message`. + /// + /// [`Radio`]: struct.Radio.html + pub fn new<F, V>(value: V, label: &str, selected: Option<V>, f: F) -> Self + where + V: Eq + Copy, + F: 'static + Fn(V) -> Message, + { + Radio { + is_selected: Some(value) == selected, + on_click: f(value), + label: String::from(label), + label_color: None, + } + } + + /// Sets the `Color` of the label of the [`Radio`]. + /// + /// [`Radio`]: struct.Radio.html + pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self { + self.label_color = Some(color.into()); + self + } +} impl<Message> Widget<Message> for Radio<Message> where diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs index cf6ae594..b980d9b4 100644 --- a/web/src/widget/row.rs +++ b/web/src/widget/row.rs @@ -1,8 +1,112 @@ -use crate::{Bus, Element, Widget}; +use crate::{Align, Bus, Element, Length, Widget}; use dodrio::bumpalo; +use std::u32; -pub type Row<'a, Message> = iced_core::Row<Element<'a, Message>>; +/// A container that distributes its contents horizontally. +/// +/// A [`Row`] will try to fill the horizontal space of its container. +/// +/// [`Row`]: struct.Row.html +#[allow(missing_debug_implementations)] +pub struct Row<'a, Message> { + spacing: u16, + padding: u16, + width: Length, + height: Length, + max_width: u32, + max_height: u32, + align_items: Align, + children: Vec<Element<'a, Message>>, +} + +impl<'a, Message> Row<'a, Message> { + /// Creates an empty [`Row`]. + /// + /// [`Row`]: struct.Row.html + pub fn new() -> Self { + Row { + spacing: 0, + padding: 0, + width: Length::Fill, + height: Length::Shrink, + max_width: u32::MAX, + max_height: u32::MAX, + align_items: Align::Start, + children: Vec::new(), + } + } + + /// Sets the horizontal spacing _between_ elements. + /// + /// Custom margins per element do not exist in Iced. You should use this + /// method instead! While less flexible, it helps you keep spacing between + /// elements consistent. + pub fn spacing(mut self, units: u16) -> Self { + self.spacing = units; + self + } + + /// Sets the padding of the [`Row`]. + /// + /// [`Row`]: struct.Row.html + pub fn padding(mut self, units: u16) -> Self { + self.padding = units; + self + } + + /// Sets the width of the [`Row`]. + /// + /// [`Row`]: struct.Row.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`Row`]. + /// + /// [`Row`]: struct.Row.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } + + /// Sets the maximum width of the [`Row`]. + /// + /// [`Row`]: struct.Row.html + pub fn max_width(mut self, max_width: u32) -> Self { + self.max_width = max_width; + self + } + + /// Sets the maximum height of the [`Row`]. + /// + /// [`Row`]: struct.Row.html + pub fn max_height(mut self, max_height: u32) -> Self { + self.max_height = max_height; + self + } + + /// Sets the vertical alignment of the contents of the [`Row`] . + /// + /// [`Row`]: struct.Row.html + pub fn align_items(mut self, align: Align) -> Self { + self.align_items = align; + self + } + + /// Adds an [`Element`] to the [`Row`]. + /// + /// [`Element`]: ../struct.Element.html + /// [`Row`]: struct.Row.html + pub fn push<E>(mut self, child: E) -> Self + where + E: Into<Element<'a, Message>>, + { + self.children.push(child.into()); + self + } +} impl<'a, Message> Widget<Message> for Row<'a, Message> { fn node<'b>( diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs index 54b2fdf6..16e20b82 100644 --- a/web/src/widget/slider.rs +++ b/web/src/widget/slider.rs @@ -1,8 +1,84 @@ -use crate::{Bus, Element, Widget}; +//! Display an interactive selector of a single value from a range of values. +//! +//! A [`Slider`] has some local [`State`]. +//! +//! [`Slider`]: struct.Slider.html +//! [`State`]: struct.State.html +use crate::{Bus, Element, Length, Widget}; use dodrio::bumpalo; +use std::{ops::RangeInclusive, rc::Rc}; -pub use iced_core::slider::*; +/// 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_web::{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); +/// ``` +/// +///  +#[allow(missing_debug_implementations)] +pub struct Slider<'a, Message> { + _state: &'a mut State, + range: RangeInclusive<f32>, + value: f32, + on_change: Rc<Box<dyn Fn(f32) -> Message>>, + width: Length, +} + +impl<'a, Message> Slider<'a, Message> { + /// Creates a new [`Slider`]. + /// + /// It expects: + /// * the local [`State`] of the [`Slider`] + /// * an inclusive range of possible values + /// * the current value of the [`Slider`] + /// * a function that will be called when the [`Slider`] is dragged. + /// It receives the new value of the [`Slider`] and must produce a + /// `Message`. + /// + /// [`Slider`]: struct.Slider.html + /// [`State`]: struct.State.html + pub fn new<F>( + state: &'a mut State, + range: RangeInclusive<f32>, + value: f32, + on_change: F, + ) -> Self + where + F: 'static + Fn(f32) -> Message, + { + Slider { + _state: state, + value: value.max(*range.start()).min(*range.end()), + range, + on_change: Rc::new(Box::new(on_change)), + width: Length::Fill, + } + } + + /// Sets the width of the [`Slider`]. + /// + /// [`Slider`]: struct.Slider.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } +} impl<'a, Message> Widget<Message> for Slider<'a, Message> where @@ -60,3 +136,18 @@ where Element::new(slider) } } + +/// The local state of a [`Slider`]. +/// +/// [`Slider`]: struct.Slider.html +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct State; + +impl State { + /// Creates a new [`State`]. + /// + /// [`State`]: struct.State.html + pub fn new() -> Self { + Self + } +} diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs index 41ccd6fc..1183a3cd 100644 --- a/web/src/widget/text.rs +++ b/web/src/widget/text.rs @@ -1,7 +1,111 @@ -use crate::{Bus, Element, Widget}; +use crate::{ + Bus, Color, Element, Font, HorizontalAlignment, Length, VerticalAlignment, + Widget, +}; use dodrio::bumpalo; -pub use iced_core::text::*; +/// A paragraph of text. +/// +/// # Example +/// +/// ``` +/// # use iced_web::Text; +/// +/// Text::new("I <3 iced!") +/// .size(40); +/// ``` +#[derive(Debug, Clone)] +pub struct Text { + content: String, + size: Option<u16>, + color: Option<Color>, + font: Font, + width: Length, + height: Length, + horizontal_alignment: HorizontalAlignment, + vertical_alignment: VerticalAlignment, +} + +impl Text { + /// Create a new fragment of [`Text`] with the given contents. + /// + /// [`Text`]: struct.Text.html + pub fn new<T: Into<String>>(label: T) -> Self { + Text { + content: label.into(), + size: None, + color: None, + font: Font::Default, + width: Length::Fill, + height: Length::Shrink, + horizontal_alignment: HorizontalAlignment::Left, + vertical_alignment: VerticalAlignment::Top, + } + } + + /// Sets the size of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + pub fn size(mut self, size: u16) -> Self { + self.size = Some(size); + self + } + + /// Sets the [`Color`] of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + /// [`Color`]: ../../struct.Color.html + pub fn color<C: Into<Color>>(mut self, color: C) -> Self { + self.color = Some(color.into()); + self + } + + /// Sets the [`Font`] of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + /// [`Font`]: ../../struct.Font.html + pub fn font(mut self, font: Font) -> Self { + self.font = font; + self + } + + /// Sets the width of the [`Text`] boundaries. + /// + /// [`Text`]: struct.Text.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`Text`] boundaries. + /// + /// [`Text`]: struct.Text.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } + + /// Sets the [`HorizontalAlignment`] of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + /// [`HorizontalAlignment`]: enum.HorizontalAlignment.html + pub fn horizontal_alignment( + mut self, + alignment: HorizontalAlignment, + ) -> Self { + self.horizontal_alignment = alignment; + self + } + + /// Sets the [`VerticalAlignment`] of the [`Text`]. + /// + /// [`Text`]: struct.Text.html + /// [`VerticalAlignment`]: enum.VerticalAlignment.html + pub fn vertical_alignment(mut self, alignment: VerticalAlignment) -> Self { + self.vertical_alignment = alignment; + self + } +} impl<'a, Message> Widget<Message> for Text { fn node<'b>( |