summaryrefslogtreecommitdiffstats
path: root/web/src
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2019-11-24 11:34:30 +0100
committerLibravatar GitHub <noreply@github.com>2019-11-24 11:34:30 +0100
commit149fd2aa1fa86858c7c1dcec8fd844caa78cec94 (patch)
treea199cf8d2caaf6aa60e48e93d6dd0688969d43b0 /web/src
parent9712b319bb7a32848001b96bd84977430f14b623 (diff)
parent47196c9007d12d3b3e0036ffabe3bf6d14ff4523 (diff)
downloadiced-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.rs15
-rw-r--r--web/src/element.rs48
-rw-r--r--web/src/lib.rs95
-rw-r--r--web/src/widget.rs27
-rw-r--r--web/src/widget/button.rs119
-rw-r--r--web/src/widget/checkbox.rs59
-rw-r--r--web/src/widget/column.rs107
-rw-r--r--web/src/widget/image.rs50
-rw-r--r--web/src/widget/radio.rs68
-rw-r--r--web/src/widget/row.rs108
-rw-r--r--web/src/widget/slider.rs95
-rw-r--r--web/src/widget/text.rs108
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` crate graph](https://github.com/hecrj/iced/blob/cae26cb7bc627f4a5b3bcf1cd023a0c552e8c65e/docs/graphs/web.png?raw=true)
+//!
+//! `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);
+/// ```
+///
+/// ![Checkbox drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/checkbox.png?raw=true)
+#[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);
+/// ```
+///
+/// ![Radio buttons drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/radio.png?raw=true)
+#[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);
+/// ```
+///
+/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)
+#[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>(