Write docs for iced_web

This commit is contained in:
Héctor Ramón Jiménez 2019-11-22 22:14:04 +01:00
parent 048909b45d
commit fa227255b0
10 changed files with 225 additions and 17 deletions

View File

@ -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,

View File

@ -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> {

View File

@ -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,6 +64,7 @@ mod element;
pub mod widget;
pub use bus::Bus;
pub use dodrio;
pub use element::Element;
pub use iced_core::{
Align, Background, Color, Font, HorizontalAlignment, Length,
@ -13,13 +72,42 @@ pub use iced_core::{
};
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,

View File

@ -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,

View File

@ -1,8 +1,27 @@
//! 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;
/// A generic widget that produces a message when clicked.
/// 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>,

View File

@ -19,6 +19,7 @@ use dodrio::bumpalo;
/// ```
///
/// ![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>,

View File

@ -8,6 +8,7 @@ use std::u32;
/// 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,

View File

@ -27,6 +27,7 @@ use dodrio::bumpalo;
/// ```
///
/// ![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,

View File

@ -8,7 +8,7 @@ use std::u32;
/// A [`Row`] will try to fill the horizontal space of its container.
///
/// [`Row`]: struct.Row.html
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
pub struct Row<'a, Message> {
spacing: u16,
padding: u16,

View File

@ -1,8 +1,37 @@
//! 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};
/// 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>,
@ -108,9 +137,16 @@ where
}
}
/// 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
}