diff options
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>( | 
