From 0030bcbd33f5c4db60aac826552042e46b51c691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 5 Feb 2020 00:23:22 +0100 Subject: [PATCH 01/29] Rename module `style` to `css` in `iced_web` --- web/src/{style.rs => css.rs} | 50 ++++++++++++++++++------------------ web/src/element.rs | 6 ++--- web/src/lib.rs | 10 ++++---- web/src/widget.rs | 4 +-- web/src/widget/button.rs | 10 ++++---- web/src/widget/checkbox.rs | 4 +-- web/src/widget/column.rs | 16 ++++++------ web/src/widget/container.rs | 14 +++++----- web/src/widget/image.rs | 4 +-- web/src/widget/radio.rs | 4 +-- web/src/widget/row.rs | 16 ++++++------ web/src/widget/scrollable.rs | 8 +++--- web/src/widget/slider.rs | 4 +-- web/src/widget/space.rs | 8 +++--- web/src/widget/text.rs | 10 ++++---- web/src/widget/text_input.rs | 10 ++++---- 16 files changed, 89 insertions(+), 89 deletions(-) rename web/src/{style.rs => css.rs} (76%) diff --git a/web/src/style.rs b/web/src/css.rs similarity index 76% rename from web/src/style.rs rename to web/src/css.rs index 4f72b22c..df0938da 100644 --- a/web/src/style.rs +++ b/web/src/css.rs @@ -3,9 +3,9 @@ use crate::{bumpalo, Align, Color, Length}; use std::collections::BTreeMap; -/// The style of a VDOM node. +/// A CSS rule of a VDOM node. #[derive(Debug)] -pub enum Style { +pub enum Rule { /// Container with vertical distribution Column, @@ -19,16 +19,16 @@ pub enum Style { Spacing(u16), } -impl Style { +impl Rule { /// Returns the class name of the [`Style`]. /// /// [`Style`]: enum.Style.html pub fn class<'a>(&self) -> String { match self { - Style::Column => String::from("c"), - Style::Row => String::from("r"), - Style::Padding(padding) => format!("p-{}", padding), - Style::Spacing(spacing) => format!("s-{}", spacing), + Rule::Column => String::from("c"), + Rule::Row => String::from("r"), + Rule::Padding(padding) => format!("p-{}", padding), + Rule::Spacing(spacing) => format!("s-{}", spacing), } } @@ -39,24 +39,24 @@ impl Style { let class = self.class(); match self { - Style::Column => { + Rule::Column => { let body = "{ display: flex; flex-direction: column; }"; bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str() } - Style::Row => { + Rule::Row => { let body = "{ display: flex; flex-direction: row; }"; bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str() } - Style::Padding(padding) => bumpalo::format!( + Rule::Padding(padding) => bumpalo::format!( in bump, ".{} {{ box-sizing: border-box; padding: {}px }}", class, padding ) .into_bump_str(), - Style::Spacing(spacing) => bumpalo::format!( + Rule::Spacing(spacing) => bumpalo::format!( in bump, ".c.{} > * {{ margin-bottom: {}px }} \ .r.{} > * {{ margin-right: {}px }} \ @@ -74,34 +74,34 @@ impl Style { } } -/// A sheet of styles. +/// A cascading style sheet. #[derive(Debug)] -pub struct Sheet<'a> { - styles: BTreeMap, +pub struct Css<'a> { + rules: BTreeMap, } -impl<'a> Sheet<'a> { +impl<'a> Css<'a> { /// Creates an empty style [`Sheet`]. /// /// [`Sheet`]: struct.Sheet.html pub fn new() -> Self { - Self { - styles: BTreeMap::new(), + Css { + rules: BTreeMap::new(), } } - /// Inserts the [`Style`] in the [`Sheet`], if it was not previously + /// Inserts the [`rule`] in the [`Sheet`], if it was not previously /// inserted. /// - /// It returns the class name of the provided [`Style`]. + /// It returns the class name of the provided [`Rule`]. /// /// [`Sheet`]: struct.Sheet.html - /// [`Style`]: enum.Style.html - pub fn insert(&mut self, bump: &'a bumpalo::Bump, style: Style) -> String { - let class = style.class(); + /// [`Rule`]: enum.Rule.html + pub fn insert(&mut self, bump: &'a bumpalo::Bump, rule: Rule) -> String { + let class = rule.class(); - if !self.styles.contains_key(&class) { - let _ = self.styles.insert(class.clone(), style.declaration(bump)); + if !self.rules.contains_key(&class) { + let _ = self.rules.insert(class.clone(), rule.declaration(bump)); } class @@ -124,7 +124,7 @@ impl<'a> Sheet<'a> { "button { border: none; cursor: pointer; outline: none }", )); - for declaration in self.styles.values() { + for declaration in self.rules.values() { declarations.push(text(*declaration)); } diff --git a/web/src/element.rs b/web/src/element.rs index 0315d7d6..93e73713 100644 --- a/web/src/element.rs +++ b/web/src/element.rs @@ -1,4 +1,4 @@ -use crate::{style, Bus, Color, Widget}; +use crate::{Bus, Color, Css, Widget}; use dodrio::bumpalo; use std::rc::Rc; @@ -57,7 +57,7 @@ impl<'a, Message> Element<'a, Message> { &self, bump: &'b bumpalo::Bump, bus: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { self.widget.node(bump, bus, style_sheet) } @@ -89,7 +89,7 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { self.widget .node(bump, &bus.map(self.mapper.clone()), style_sheet) diff --git a/web/src/lib.rs b/web/src/lib.rs index 0b9c0c3d..7b54a07a 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -63,11 +63,12 @@ mod bus; mod element; mod hasher; -pub mod style; +pub mod css; pub mod subscription; pub mod widget; pub use bus::Bus; +pub use css::Css; pub use dodrio; pub use element::Element; pub use hasher::Hasher; @@ -76,7 +77,6 @@ pub use iced_core::{ VerticalAlignment, }; pub use iced_futures::{executor, futures, Command}; -pub use style::Style; pub use subscription::Subscription; #[doc(no_inline)] @@ -241,13 +241,13 @@ where let mut ui = self.application.borrow_mut(); let element = ui.view(); - let mut style_sheet = style::Sheet::new(); + let mut css = Css::new(); - let node = element.widget.node(bump, &self.bus, &mut style_sheet); + let node = element.widget.node(bump, &self.bus, &mut css); div(bump) .attr("style", "width: 100%; height: 100%") - .children(vec![style_sheet.node(bump), node]) + .children(vec![css.node(bump), node]) .finish() } } diff --git a/web/src/widget.rs b/web/src/widget.rs index 0ac536bd..2e80346e 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -14,7 +14,7 @@ //! ``` //! //! [`Widget`]: trait.Widget.html -use crate::{style, Bus}; +use crate::{Bus, Css}; use dodrio::bumpalo; pub mod button; @@ -64,6 +64,6 @@ pub trait Widget { &self, bump: &'b bumpalo::Bump, _bus: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b>; } diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index 6fef48ce..f3b3e69a 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -4,7 +4,7 @@ //! //! [`Button`]: struct.Button.html //! [`State`]: struct.State.html -use crate::{style, Background, Bus, Element, Length, Style, Widget}; +use crate::{css, Background, Bus, Css, Element, Length, Widget}; use dodrio::bumpalo; @@ -126,18 +126,18 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; - let width = style::length(self.width); + let width = css::length(self.width); let padding_class = - style_sheet.insert(bump, Style::Padding(self.padding)); + style_sheet.insert(bump, css::Rule::Padding(self.padding)); let background = match self.background { None => String::from("none"), Some(background) => match background { - Background::Color(color) => style::color(color), + Background::Color(color) => css::color(color), }, }; diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 1e864875..d1d30048 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -1,4 +1,4 @@ -use crate::{style, Bus, Color, Element, Widget}; +use crate::{Bus, Color, Css, Element, Widget}; use dodrio::bumpalo; use std::rc::Rc; @@ -68,7 +68,7 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - _style_sheet: &mut style::Sheet<'b>, + _style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs index 9aa988ff..8e36231a 100644 --- a/web/src/widget/column.rs +++ b/web/src/widget/column.rs @@ -1,4 +1,4 @@ -use crate::{style, Align, Bus, Element, Length, Style, Widget}; +use crate::{css, Align, Bus, Css, Element, Length, Widget}; use dodrio::bumpalo; use std::u32; @@ -112,7 +112,7 @@ impl<'a, Message> Widget for Column<'a, Message> { &self, bump: &'b bumpalo::Bump, publish: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; @@ -122,18 +122,18 @@ impl<'a, Message> Widget for Column<'a, Message> { .map(|element| element.widget.node(bump, publish, style_sheet)) .collect(); - let column_class = style_sheet.insert(bump, Style::Column); + let column_class = style_sheet.insert(bump, css::Rule::Column); let spacing_class = - style_sheet.insert(bump, Style::Spacing(self.spacing)); + style_sheet.insert(bump, css::Rule::Spacing(self.spacing)); let padding_class = - style_sheet.insert(bump, Style::Padding(self.padding)); + style_sheet.insert(bump, css::Rule::Padding(self.padding)); - let width = style::length(self.width); - let height = style::length(self.height); + let width = css::length(self.width); + let height = css::length(self.height); - let align_items = style::align(self.align_items); + let align_items = css::align(self.align_items); // TODO: Complete styling div(bump) diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs index bdc88979..55995795 100644 --- a/web/src/widget/container.rs +++ b/web/src/widget/container.rs @@ -1,4 +1,4 @@ -use crate::{bumpalo, style, Align, Bus, Element, Length, Style, Widget}; +use crate::{bumpalo, css, Align, Bus, Css, Element, Length, Widget}; /// An element decorating some content. /// @@ -94,17 +94,17 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; - let column_class = style_sheet.insert(bump, Style::Column); + let column_class = style_sheet.insert(bump, css::Rule::Column); - let width = style::length(self.width); - let height = style::length(self.height); + let width = css::length(self.width); + let height = css::length(self.height); - let align_items = style::align(self.horizontal_alignment); - let justify_content = style::align(self.vertical_alignment); + let align_items = css::align(self.horizontal_alignment); + let justify_content = css::align(self.vertical_alignment); let node = div(bump) .attr( diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs index 413b663e..17e9b0f7 100644 --- a/web/src/widget/image.rs +++ b/web/src/widget/image.rs @@ -1,4 +1,4 @@ -use crate::{style, Bus, Element, Length, Widget}; +use crate::{Bus, Css, Element, Length, Widget}; use dodrio::bumpalo; @@ -57,7 +57,7 @@ impl Widget for Image { &self, bump: &'b bumpalo::Bump, _bus: &Bus, - _style_sheet: &mut style::Sheet<'b>, + _style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index 6dd0ad45..4570639c 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -1,4 +1,4 @@ -use crate::{style, Bus, Color, Element, Widget}; +use crate::{Bus, Color, Css, Element, Widget}; use dodrio::bumpalo; @@ -76,7 +76,7 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - _style_sheet: &mut style::Sheet<'b>, + _style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs index c26cb91b..6e184cab 100644 --- a/web/src/widget/row.rs +++ b/web/src/widget/row.rs @@ -1,4 +1,4 @@ -use crate::{style, Align, Bus, Element, Length, Style, Widget}; +use crate::{css, Align, Bus, Css, Element, Length, Widget}; use dodrio::bumpalo; use std::u32; @@ -113,7 +113,7 @@ impl<'a, Message> Widget for Row<'a, Message> { &self, bump: &'b bumpalo::Bump, publish: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; @@ -123,18 +123,18 @@ impl<'a, Message> Widget for Row<'a, Message> { .map(|element| element.widget.node(bump, publish, style_sheet)) .collect(); - let row_class = style_sheet.insert(bump, Style::Row); + let row_class = style_sheet.insert(bump, css::Rule::Row); let spacing_class = - style_sheet.insert(bump, Style::Spacing(self.spacing)); + style_sheet.insert(bump, css::Rule::Spacing(self.spacing)); let padding_class = - style_sheet.insert(bump, Style::Padding(self.padding)); + style_sheet.insert(bump, css::Rule::Padding(self.padding)); - let width = style::length(self.width); - let height = style::length(self.height); + let width = css::length(self.width); + let height = css::length(self.height); - let justify_content = style::align(self.align_items); + let justify_content = css::align(self.align_items); // TODO: Complete styling div(bump) diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs index f146e007..19810531 100644 --- a/web/src/widget/scrollable.rs +++ b/web/src/widget/scrollable.rs @@ -1,5 +1,5 @@ //! Navigate an endless amount of content with a scrollbar. -use crate::{bumpalo, style, Align, Bus, Column, Element, Length, Widget}; +use crate::{bumpalo, css, Align, Bus, Column, Css, Element, Length, Widget}; /// A widget that can vertically display an infinite amount of content with a /// scrollbar. @@ -105,12 +105,12 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; - let width = style::length(self.width); - let height = style::length(self.height); + let width = css::length(self.width); + let height = css::length(self.height); let node = div(bump) .attr( diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs index 25c57933..843003e6 100644 --- a/web/src/widget/slider.rs +++ b/web/src/widget/slider.rs @@ -4,7 +4,7 @@ //! //! [`Slider`]: struct.Slider.html //! [`State`]: struct.State.html -use crate::{style, Bus, Element, Length, Widget}; +use crate::{Bus, Css, Element, Length, Widget}; use dodrio::bumpalo; use std::{ops::RangeInclusive, rc::Rc}; @@ -88,7 +88,7 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - _style_sheet: &mut style::Sheet<'b>, + _style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; use wasm_bindgen::JsCast; diff --git a/web/src/widget/space.rs b/web/src/widget/space.rs index baf4c80b..4ce52595 100644 --- a/web/src/widget/space.rs +++ b/web/src/widget/space.rs @@ -1,4 +1,4 @@ -use crate::{style, Bus, Element, Length, Widget}; +use crate::{css, Bus, Css, Element, Length, Widget}; use dodrio::bumpalo; /// An amount of empty space. @@ -44,12 +44,12 @@ impl<'a, Message> Widget for Space { &self, bump: &'b bumpalo::Bump, _publish: &Bus, - _style_sheet: &mut style::Sheet<'b>, + _css: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; - let width = style::length(self.width); - let height = style::length(self.height); + let width = css::length(self.width); + let height = css::length(self.height); let style = bumpalo::format!( in bump, diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs index 5b0bee55..c5b07747 100644 --- a/web/src/widget/text.rs +++ b/web/src/widget/text.rs @@ -1,5 +1,5 @@ use crate::{ - style, Bus, Color, Element, Font, HorizontalAlignment, Length, + css, Bus, Color, Css, Element, Font, HorizontalAlignment, Length, VerticalAlignment, Widget, }; use dodrio::bumpalo; @@ -112,15 +112,15 @@ impl<'a, Message> Widget for Text { &self, bump: &'b bumpalo::Bump, _publish: &Bus, - _style_sheet: &mut style::Sheet<'b>, + _style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; let content = bumpalo::format!(in bump, "{}", self.content); - let color = style::color(self.color.unwrap_or(Color::BLACK)); + let color = css::color(self.color.unwrap_or(Color::BLACK)); - let width = style::length(self.width); - let height = style::length(self.height); + let width = css::length(self.width); + let height = css::length(self.height); let text_align = match self.horizontal_alignment { HorizontalAlignment::Left => "left", diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index 078e05b2..1fea787a 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -4,7 +4,7 @@ //! //! [`TextInput`]: struct.TextInput.html //! [`State`]: struct.State.html -use crate::{bumpalo, style, Bus, Element, Length, Style, Widget}; +use crate::{bumpalo, css, Bus, Css, Element, Length, Widget}; use std::rc::Rc; /// A field that can be filled with text. @@ -133,15 +133,15 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - style_sheet: &mut style::Sheet<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; use wasm_bindgen::JsCast; - let width = style::length(self.width); - let max_width = style::length(self.max_width); + let width = css::length(self.width); + let max_width = css::length(self.max_width); let padding_class = - style_sheet.insert(bump, Style::Padding(self.padding)); + style_sheet.insert(bump, css::Rule::Padding(self.padding)); let on_change = self.on_change.clone(); let event_bus = bus.clone(); From 28fd9feb40a024ea29f73fa91c21fc3f2cf01d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 5 Feb 2020 01:04:46 +0100 Subject: [PATCH 02/29] Support styling for `Button` in `iced_web` --- web/Cargo.toml | 1 + web/src/widget/button.rs | 61 +++++++++++++++++++++++++--------------- web/src/widget/column.rs | 2 +- web/src/widget/row.rs | 2 +- web/src/widget/text.rs | 5 +++- 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/web/Cargo.toml b/web/Cargo.toml index 46953863..3b2d24d5 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -15,6 +15,7 @@ categories = ["web-programming"] maintenance = { status = "actively-developed" } [dependencies] +iced_style = { version = "0.1.0-alpha", path = "../style" } dodrio = "0.1.0" wasm-bindgen = "0.2.51" wasm-bindgen-futures = "0.4" diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index f3b3e69a..e47b971d 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -6,6 +6,8 @@ //! [`State`]: struct.State.html use crate::{css, Background, Bus, Css, Element, Length, Widget}; +pub use iced_style::button::{Style, StyleSheet}; + use dodrio::bumpalo; /// A generic widget that produces a message when pressed. @@ -26,10 +28,11 @@ pub struct Button<'a, Message> { content: Element<'a, Message>, on_press: Option, width: Length, + height: Length, min_width: u32, + min_height: u32, padding: u16, - background: Option, - border_radius: u16, + style: Box, } impl<'a, Message> Button<'a, Message> { @@ -46,10 +49,11 @@ impl<'a, Message> Button<'a, Message> { content: content.into(), on_press: None, width: Length::Shrink, + height: Length::Shrink, min_width: 0, - padding: 0, - background: None, - border_radius: 0, + min_height: 0, + padding: 5, + style: Default::default(), } } @@ -61,6 +65,14 @@ impl<'a, Message> Button<'a, Message> { self } + /// Sets the height of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } + /// Sets the minimum width of the [`Button`]. /// /// [`Button`]: struct.Button.html @@ -69,6 +81,14 @@ impl<'a, Message> Button<'a, Message> { self } + /// Sets the minimum height of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn min_height(mut self, min_height: u32) -> Self { + self.min_height = min_height; + self + } + /// Sets the padding of the [`Button`]. /// /// [`Button`]: struct.Button.html @@ -77,20 +97,11 @@ impl<'a, Message> Button<'a, Message> { self } - /// Sets the [`Background`] of the [`Button`]. + /// Sets the style of the [`Button`]. /// /// [`Button`]: struct.Button.html - /// [`Background`]: ../../struct.Background.html - pub fn background>(mut self, background: T) -> Self { - self.background = Some(background.into()); - 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; + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); self } @@ -130,11 +141,16 @@ where ) -> dodrio::Node<'b> { use dodrio::builder::*; + // TODO: State-based styling + let style = self.style.active(); + let width = css::length(self.width); + let color = css::color(style.text_color); + let padding_class = style_sheet.insert(bump, css::Rule::Padding(self.padding)); - let background = match self.background { + let background = match style.background { None => String::from("none"), Some(background) => match background { Background::Color(color) => css::color(color), @@ -150,11 +166,12 @@ where "style", bumpalo::format!( in bump, - "background: {}; border-radius: {}px; width:{}; min-width: {}px", + "background: {}; border-radius: {}px; width:{}; min-width: {}px; color: {}", background, - self.border_radius, + style.border_radius, width, - self.min_width + self.min_width, + color ) .into_bump_str(), ) @@ -168,8 +185,6 @@ where }); } - // TODO: Complete styling - node.finish() } } diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs index 8e36231a..640d3673 100644 --- a/web/src/widget/column.rs +++ b/web/src/widget/column.rs @@ -28,7 +28,7 @@ impl<'a, Message> Column<'a, Message> { Column { spacing: 0, padding: 0, - width: Length::Shrink, + width: Length::Fill, height: Length::Shrink, max_width: u32::MAX, max_height: u32::MAX, diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs index 6e184cab..b360bc45 100644 --- a/web/src/widget/row.rs +++ b/web/src/widget/row.rs @@ -28,7 +28,7 @@ impl<'a, Message> Row<'a, Message> { Row { spacing: 0, padding: 0, - width: Length::Shrink, + width: Length::Fill, height: Length::Shrink, max_width: u32::MAX, max_height: u32::MAX, diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs index c5b07747..46230fef 100644 --- a/web/src/widget/text.rs +++ b/web/src/widget/text.rs @@ -117,7 +117,10 @@ impl<'a, Message> Widget for Text { use dodrio::builder::*; let content = bumpalo::format!(in bump, "{}", self.content); - let color = css::color(self.color.unwrap_or(Color::BLACK)); + let color = self + .color + .map(css::color) + .unwrap_or(String::from("inherit")); let width = css::length(self.width); let height = css::length(self.height); From 8f52604987038225ce90261f17fd8408f1a7ebe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 5 Feb 2020 01:40:27 +0100 Subject: [PATCH 03/29] Use `reqwest` and `tokio` in `pokedex` example --- examples/pokedex/Cargo.toml | 6 +++--- examples/pokedex/src/main.rs | 14 +++++++------- futures/Cargo.toml | 6 +++--- futures/src/executor.rs | 8 ++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/pokedex/Cargo.toml b/examples/pokedex/Cargo.toml index 76a3a82f..b96eda91 100644 --- a/examples/pokedex/Cargo.toml +++ b/examples/pokedex/Cargo.toml @@ -6,9 +6,9 @@ edition = "2018" publish = false [dependencies] -iced = { path = "../..", features = ["image"] } -iced_futures = { path = "../../futures", features = ["async-std"] } -surf = "1.0" +iced = { path = "../..", features = ["image", "debug"] } +iced_futures = { path = "../../futures", features = ["tokio"] } +reqwest = { version = "0.10", features = ["json"] } rand = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index 283437b2..3c00d628 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -27,7 +27,7 @@ enum Message { } impl Application for Pokedex { - type Executor = iced_futures::executor::AsyncStd; + type Executor = iced_futures::executor::Tokio; type Message = Message; fn new() -> (Pokedex, Command) { @@ -175,8 +175,8 @@ impl Pokemon { let sprite = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id); let (entry, sprite): (Entry, _) = futures::future::try_join( - surf::get(&url).recv_json(), - surf::get(&sprite).recv_bytes(), + reqwest::get(&url).await?.json(), + reqwest::get(&sprite).await?.bytes(), ) .await?; @@ -195,7 +195,7 @@ impl Pokemon { .chars() .map(|c| if c.is_control() { ' ' } else { c }) .collect(), - image: image::Handle::from_memory(sprite), + image: image::Handle::from_memory(sprite.as_ref().to_vec()), }) } } @@ -206,9 +206,9 @@ enum Error { LanguageError, } -impl From for Error { - fn from(exception: surf::Exception) -> Error { - dbg!(&exception); +impl From for Error { + fn from(error: reqwest::Error) -> Error { + dbg!(&error); Error::APIError } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 91860e1e..483e60cb 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -19,12 +19,12 @@ log = "0.4" [dependencies.futures] version = "0.3" -[dependencies.tokio] +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio] version = "0.2" optional = true -features = ["rt-core"] +features = ["rt-core", "rt-threaded"] -[dependencies.async-std] +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.async-std] version = "1.0" optional = true diff --git a/futures/src/executor.rs b/futures/src/executor.rs index c2b9cc72..2a5281af 100644 --- a/futures/src/executor.rs +++ b/futures/src/executor.rs @@ -4,10 +4,10 @@ mod null; #[cfg(feature = "thread-pool")] mod thread_pool; -#[cfg(feature = "tokio")] +#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))] mod tokio; -#[cfg(feature = "async-std")] +#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))] mod async_std; #[cfg(target_arch = "wasm32")] @@ -18,10 +18,10 @@ pub use null::Null; #[cfg(feature = "thread-pool")] pub use thread_pool::ThreadPool; -#[cfg(feature = "tokio")] +#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))] pub use self::tokio::Tokio; -#[cfg(feature = "async-std")] +#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))] pub use self::async_std::AsyncStd; #[cfg(target_arch = "wasm32")] From 9a06e481b7f52a9d8e123c909d5614332f607c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 5 Feb 2020 04:13:13 +0100 Subject: [PATCH 04/29] Add `Handle` and `Data` to `image` in `iced_web` --- native/src/widget/image.rs | 1 - web/Cargo.toml | 1 + web/src/widget.rs | 2 +- web/src/widget/image.rs | 83 +++++++++++++++++++++++++++++++++++--- 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 1efe4570..200401f9 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -1,5 +1,4 @@ //! Display images in your user interface. - use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget}; use std::{ diff --git a/web/Cargo.toml b/web/Cargo.toml index 3b2d24d5..7ddf3400 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -19,6 +19,7 @@ iced_style = { version = "0.1.0-alpha", path = "../style" } dodrio = "0.1.0" wasm-bindgen = "0.2.51" wasm-bindgen-futures = "0.4" +url = "2.0" [dependencies.iced_core] version = "0.1.0" diff --git a/web/src/widget.rs b/web/src/widget.rs index 2e80346e..3192a767 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -18,6 +18,7 @@ use crate::{Bus, Css}; use dodrio::bumpalo; pub mod button; +pub mod image; pub mod scrollable; pub mod slider; pub mod text_input; @@ -25,7 +26,6 @@ pub mod text_input; mod checkbox; mod column; mod container; -mod image; mod radio; mod row; mod space; diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs index 17e9b0f7..4296d0e2 100644 --- a/web/src/widget/image.rs +++ b/web/src/widget/image.rs @@ -1,6 +1,12 @@ -use crate::{Bus, Css, Element, Length, Widget}; +//! Display images in your user interface. +use crate::{Bus, Css, Element, Hasher, Length, Widget}; use dodrio::bumpalo; +use std::{ + hash::{Hash, Hasher as _}, + path::PathBuf, + sync::Arc, +}; /// A frame that displays an image while keeping aspect ratio. /// @@ -14,7 +20,7 @@ use dodrio::bumpalo; #[derive(Debug)] pub struct Image { /// The image path - pub path: String, + pub handle: Handle, /// The width of the image pub width: Length, @@ -27,9 +33,9 @@ impl Image { /// Creates a new [`Image`] with the given path. /// /// [`Image`]: struct.Image.html - pub fn new>(path: T) -> Self { + pub fn new>(handle: T) -> Self { Image { - path: path.into(), + handle: handle.into(), width: Length::Shrink, height: Length::Shrink, } @@ -61,7 +67,9 @@ impl Widget for Image { ) -> dodrio::Node<'b> { use dodrio::builder::*; - let src = bumpalo::format!(in bump, "{}", self.path); + let src = bumpalo::format!(in bump, "{}", match self.handle.data.as_ref() { + Data::Path(path) => path.to_str().unwrap_or("") + }); let mut image = img(bump).attr("src", src.into_bump_str()); @@ -89,3 +97,68 @@ impl<'a, Message> From for Element<'a, Message> { Element::new(image) } } + +/// An [`Image`] handle. +/// +/// [`Image`]: struct.Image.html +#[derive(Debug, Clone)] +pub struct Handle { + id: u64, + data: Arc, +} + +impl Handle { + /// Creates an image [`Handle`] pointing to the image of the given path. + /// + /// [`Handle`]: struct.Handle.html + pub fn from_path>(path: T) -> Handle { + Self::from_data(Data::Path(path.into())) + } + + fn from_data(data: Data) -> Handle { + let mut hasher = Hasher::default(); + data.hash(&mut hasher); + + Handle { + id: hasher.finish(), + data: Arc::new(data), + } + } + + /// Returns the unique identifier of the [`Handle`]. + /// + /// [`Handle`]: struct.Handle.html + pub fn id(&self) -> u64 { + self.id + } + + /// Returns a reference to the image [`Data`]. + /// + /// [`Data`]: enum.Data.html + pub fn data(&self) -> &Data { + &self.data + } +} + +impl From<&str> for Handle { + fn from(path: &str) -> Handle { + Handle::from_path(path) + } +} + +/// The data of an [`Image`]. +/// +/// [`Image`]: struct.Image.html +#[derive(Clone, Hash)] +pub enum Data { + /// A remote image + Path(PathBuf), +} + +impl std::fmt::Debug for Data { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Data::Path(path) => write!(f, "Path({:?})", path), + } + } +} From ca213922d043a5532d9ab352c0d54bfca7563871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 5 Feb 2020 04:14:26 +0100 Subject: [PATCH 05/29] Drop `Send` in `Command` and `Executor` on Wasm --- futures/src/command.rs | 105 ++------------------------- futures/src/command/native.rs | 100 +++++++++++++++++++++++++ futures/src/command/web.rs | 101 ++++++++++++++++++++++++++ futures/src/executor.rs | 11 ++- futures/src/executor/null.rs | 4 + futures/src/executor/wasm_bindgen.rs | 5 +- futures/src/runtime.rs | 6 +- src/executor.rs | 11 ++- 8 files changed, 232 insertions(+), 111 deletions(-) create mode 100644 futures/src/command/native.rs create mode 100644 futures/src/command/web.rs diff --git a/futures/src/command.rs b/futures/src/command.rs index e7885fb8..26f58fde 100644 --- a/futures/src/command.rs +++ b/futures/src/command.rs @@ -1,100 +1,11 @@ -use futures::future::{BoxFuture, Future, FutureExt}; +#[cfg(not(target_arch = "wasm32"))] +mod native; -/// A collection of async operations. -/// -/// You should be able to turn a future easily into a [`Command`], either by -/// using the `From` trait or [`Command::perform`]. -/// -/// [`Command`]: struct.Command.html -pub struct Command { - futures: Vec>, -} +#[cfg(not(target_arch = "wasm32"))] +pub use native::Command; -impl Command { - /// Creates an empty [`Command`]. - /// - /// In other words, a [`Command`] that does nothing. - /// - /// [`Command`]: struct.Command.html - pub fn none() -> Self { - Self { - futures: Vec::new(), - } - } +#[cfg(target_arch = "wasm32")] +mod web; - /// Creates a [`Command`] that performs the action of the given future. - /// - /// [`Command`]: struct.Command.html - pub fn perform( - future: impl Future + 'static + Send, - f: impl Fn(T) -> A + 'static + Send, - ) -> Command { - Command { - futures: vec![future.map(f).boxed()], - } - } - - /// Applies a transformation to the result of a [`Command`]. - /// - /// [`Command`]: struct.Command.html - pub fn map( - mut self, - f: impl Fn(T) -> A + 'static + Send + Sync, - ) -> Command - where - T: 'static, - { - let f = std::sync::Arc::new(f); - - Command { - futures: self - .futures - .drain(..) - .map(|future| { - let f = f.clone(); - - future.map(move |result| f(result)).boxed() - }) - .collect(), - } - } - - /// Creates a [`Command`] that performs the actions of all the given - /// commands. - /// - /// Once this command is run, all the commands will be exectued at once. - /// - /// [`Command`]: struct.Command.html - pub fn batch(commands: impl IntoIterator>) -> Self { - Self { - futures: commands - .into_iter() - .flat_map(|command| command.futures) - .collect(), - } - } - - /// Converts a [`Command`] into its underlying list of futures. - /// - /// [`Command`]: struct.Command.html - pub fn futures(self) -> Vec> { - self.futures - } -} - -impl From for Command -where - A: Future + 'static + Send, -{ - fn from(future: A) -> Self { - Self { - futures: vec![future.boxed()], - } - } -} - -impl std::fmt::Debug for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Command").finish() - } -} +#[cfg(target_arch = "wasm32")] +pub use web::Command; diff --git a/futures/src/command/native.rs b/futures/src/command/native.rs new file mode 100644 index 00000000..38cb4e06 --- /dev/null +++ b/futures/src/command/native.rs @@ -0,0 +1,100 @@ +use futures::future::{BoxFuture, Future, FutureExt}; + +/// A collection of async operations. +/// +/// You should be able to turn a future easily into a [`Command`], either by +/// using the `From` trait or [`Command::perform`]. +/// +/// [`Command`]: struct.Command.html +pub struct Command { + futures: Vec>, +} + +impl Command { + /// Creates an empty [`Command`]. + /// + /// In other words, a [`Command`] that does nothing. + /// + /// [`Command`]: struct.Command.html + pub fn none() -> Self { + Self { + futures: Vec::new(), + } + } + + /// Creates a [`Command`] that performs the action of the given future. + /// + /// [`Command`]: struct.Command.html + pub fn perform( + future: impl Future + 'static + Send, + f: impl Fn(T) -> A + 'static + Send, + ) -> Command { + Command { + futures: vec![future.map(f).boxed()], + } + } + + /// Applies a transformation to the result of a [`Command`]. + /// + /// [`Command`]: struct.Command.html + pub fn map( + mut self, + f: impl Fn(T) -> A + 'static + Send + Sync, + ) -> Command + where + T: 'static, + { + let f = std::sync::Arc::new(f); + + Command { + futures: self + .futures + .drain(..) + .map(|future| { + let f = f.clone(); + + future.map(move |result| f(result)).boxed() + }) + .collect(), + } + } + + /// Creates a [`Command`] that performs the actions of all the given + /// commands. + /// + /// Once this command is run, all the commands will be executed at once. + /// + /// [`Command`]: struct.Command.html + pub fn batch(commands: impl IntoIterator>) -> Self { + Self { + futures: commands + .into_iter() + .flat_map(|command| command.futures) + .collect(), + } + } + + /// Converts a [`Command`] into its underlying list of futures. + /// + /// [`Command`]: struct.Command.html + pub fn futures(self) -> Vec> { + self.futures + } +} + +impl From for Command +where + A: Future + 'static + Send, +{ + fn from(future: A) -> Self { + Self { + futures: vec![future.boxed()], + } + } +} + +impl std::fmt::Debug for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Command").finish() + } +} diff --git a/futures/src/command/web.rs b/futures/src/command/web.rs new file mode 100644 index 00000000..11b46b90 --- /dev/null +++ b/futures/src/command/web.rs @@ -0,0 +1,101 @@ +use futures::future::{Future, FutureExt}; +use std::pin::Pin; + +/// A collection of async operations. +/// +/// You should be able to turn a future easily into a [`Command`], either by +/// using the `From` trait or [`Command::perform`]. +/// +/// [`Command`]: struct.Command.html +pub struct Command { + futures: Vec + 'static>>>, +} + +impl Command { + /// Creates an empty [`Command`]. + /// + /// In other words, a [`Command`] that does nothing. + /// + /// [`Command`]: struct.Command.html + pub fn none() -> Self { + Self { + futures: Vec::new(), + } + } + + /// Creates a [`Command`] that performs the action of the given future. + /// + /// [`Command`]: struct.Command.html + pub fn perform( + future: impl Future + 'static, + f: impl Fn(T) -> A + 'static, + ) -> Command { + Command { + futures: vec![future.map(f).boxed_local()], + } + } + + /// Applies a transformation to the result of a [`Command`]. + /// + /// [`Command`]: struct.Command.html + pub fn map( + mut self, + f: impl Fn(T) -> A + 'static + Send + Sync + Unpin, + ) -> Command + where + T: 'static, + { + let f = std::sync::Arc::new(f); + + Command { + futures: self + .futures + .drain(..) + .map(|future| { + let f = f.clone(); + + future.map(move |result| f(result)).boxed_local() + }) + .collect(), + } + } + + /// Creates a [`Command`] that performs the actions of all the given + /// commands. + /// + /// Once this command is run, all the commands will be executed at once. + /// + /// [`Command`]: struct.Command.html + pub fn batch(commands: impl IntoIterator>) -> Self { + Self { + futures: commands + .into_iter() + .flat_map(|command| command.futures) + .collect(), + } + } + + /// Converts a [`Command`] into its underlying list of futures. + /// + /// [`Command`]: struct.Command.html + pub fn futures(self) -> Vec + 'static>>> { + self.futures + } +} + +impl From for Command +where + A: Future + 'static, +{ + fn from(future: A) -> Self { + Self { + futures: vec![future.boxed_local()], + } + } +} + +impl std::fmt::Debug for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Command").finish() + } +} diff --git a/futures/src/executor.rs b/futures/src/executor.rs index 2a5281af..5378c0b3 100644 --- a/futures/src/executor.rs +++ b/futures/src/executor.rs @@ -1,7 +1,7 @@ //! Choose your preferred executor to power a runtime. mod null; -#[cfg(feature = "thread-pool")] +#[cfg(all(not(target_arch = "wasm32"), feature = "thread-pool"))] mod thread_pool; #[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))] @@ -15,7 +15,7 @@ mod wasm_bindgen; pub use null::Null; -#[cfg(feature = "thread-pool")] +#[cfg(all(not(target_arch = "wasm32"), feature = "thread-pool"))] pub use thread_pool::ThreadPool; #[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))] @@ -41,8 +41,15 @@ pub trait Executor: Sized { /// Spawns a future in the [`Executor`]. /// /// [`Executor`]: trait.Executor.html + #[cfg(not(target_arch = "wasm32"))] fn spawn(&self, future: impl Future + Send + 'static); + /// Spawns a local future in the [`Executor`]. + /// + /// [`Executor`]: trait.Executor.html + #[cfg(target_arch = "wasm32")] + fn spawn(&self, future: impl Future + 'static); + /// Runs the given closure inside the [`Executor`]. /// /// Some executors, like `tokio`, require some global state to be in place diff --git a/futures/src/executor/null.rs b/futures/src/executor/null.rs index 6d5cf982..65e2e2df 100644 --- a/futures/src/executor/null.rs +++ b/futures/src/executor/null.rs @@ -11,5 +11,9 @@ impl Executor for Null { Ok(Self) } + #[cfg(not(target_arch = "wasm32"))] fn spawn(&self, _future: impl Future + Send + 'static) {} + + #[cfg(target_arch = "wasm32")] + fn spawn(&self, _future: impl Future + 'static) {} } diff --git a/futures/src/executor/wasm_bindgen.rs b/futures/src/executor/wasm_bindgen.rs index 69b7c7e2..94d694c8 100644 --- a/futures/src/executor/wasm_bindgen.rs +++ b/futures/src/executor/wasm_bindgen.rs @@ -9,10 +9,7 @@ impl Executor for WasmBindgen { Ok(Self) } - fn spawn( - &self, - future: impl futures::Future + Send + 'static, - ) { + fn spawn(&self, future: impl futures::Future + 'static) { wasm_bindgen_futures::spawn_local(future); } } diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs index 3be45a26..ede529dc 100644 --- a/futures/src/runtime.rs +++ b/futures/src/runtime.rs @@ -73,11 +73,13 @@ where for future in futures { let mut sender = self.sender.clone(); - self.executor.spawn(future.then(|message| async move { + let future = future.then(|message| async move { let _ = sender.send(message).await; () - })); + }); + + self.executor.spawn(future); } } diff --git a/src/executor.rs b/src/executor.rs index cbbd8283..539bfd5d 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -9,7 +9,8 @@ mod platform { /// A default cross-platform executor. /// - /// - On native platforms, it will use a `iced_futures::executor::ThreadPool`. + /// - On native platforms, it will use a + /// `iced_futures::executor::ThreadPool`. /// - On the Web, it will use `iced_futures::executor::WasmBindgen`. #[derive(Debug)] pub struct Default(ThreadPool); @@ -34,7 +35,8 @@ mod platform { /// A default cross-platform executor. /// - /// - On native platforms, it will use a `iced_futures::executor::ThreadPool`. + /// - On native platforms, it will use a + /// `iced_futures::executor::ThreadPool`. /// - On the Web, it will use `iced_futures::executor::WasmBindgen`. #[derive(Debug)] pub struct Default(WasmBindgen); @@ -44,10 +46,7 @@ mod platform { Ok(Default(WasmBindgen::new()?)) } - fn spawn( - &self, - future: impl futures::Future + Send + 'static, - ) { + fn spawn(&self, future: impl futures::Future + 'static) { self.0.spawn(future); } } From 9df3fb13c7b0372f64ff7bd9073769833689de5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 5 Feb 2020 04:15:45 +0100 Subject: [PATCH 06/29] Make `pokedex` example work on Wasm --- examples/pokedex/Cargo.toml | 9 +++++++-- examples/pokedex/src/main.rs | 39 +++++++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/examples/pokedex/Cargo.toml b/examples/pokedex/Cargo.toml index b96eda91..f8668be0 100644 --- a/examples/pokedex/Cargo.toml +++ b/examples/pokedex/Cargo.toml @@ -8,7 +8,12 @@ publish = false [dependencies] iced = { path = "../..", features = ["image", "debug"] } iced_futures = { path = "../../futures", features = ["tokio"] } -reqwest = { version = "0.10", features = ["json"] } -rand = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +rand = { version = "0.7", features = ["wasm-bindgen"] } + +[dependencies.reqwest] +version = "0.10" +git = "https://github.com/hecrj/reqwest.git" +branch = "feature/wasm-deserialize-json" +features = ["json"] diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index 3c00d628..13e420a9 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -27,7 +27,12 @@ enum Message { } impl Application for Pokedex { + #[cfg(not(target_arch = "wasm32"))] type Executor = iced_futures::executor::Tokio; + + #[cfg(target_arch = "wasm32")] + type Executor = iced_futures::executor::WasmBindgen; + type Message = Message; fn new() -> (Pokedex, Command) { @@ -166,19 +171,21 @@ impl Pokemon { } let id = { - let mut rng = rand::thread_rng(); + let mut rng = rand::rngs::OsRng::default(); rng.gen_range(0, Pokemon::TOTAL) }; - let url = format!("https://pokeapi.co/api/v2/pokemon-species/{}", id); - let sprite = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id); + let fetch_entry = async { + let url = + format!("https://pokeapi.co/api/v2/pokemon-species/{}", id); - let (entry, sprite): (Entry, _) = futures::future::try_join( - reqwest::get(&url).await?.json(), - reqwest::get(&sprite).await?.bytes(), - ) - .await?; + reqwest::get(&url).await?.json().await + }; + + let (entry, image): (Entry, _) = + futures::future::try_join(fetch_entry, Self::fetch_image(id)) + .await?; let description = entry .flavor_text_entries @@ -195,9 +202,23 @@ impl Pokemon { .chars() .map(|c| if c.is_control() { ' ' } else { c }) .collect(), - image: image::Handle::from_memory(sprite.as_ref().to_vec()), + image, }) } + + async fn fetch_image(id: u16) -> Result { + let url = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id); + + #[cfg(not(target_arch = "wasm32"))] + { + let bytes = reqwest::get(&url).await?.bytes().await?; + + Ok(image::Handle::from_memory(bytes.as_ref().to_vec())) + } + + #[cfg(target_arch = "wasm32")] + Ok(image::Handle::from_path(url)) + } } #[derive(Debug, Clone)] From ad13b466d234f04784bf77be0b4cb82acdfa1ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 00:16:59 +0100 Subject: [PATCH 07/29] Add `From` for `image::Handle` in `iced_web` --- web/src/widget/image.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs index 4296d0e2..029ab352 100644 --- a/web/src/widget/image.rs +++ b/web/src/widget/image.rs @@ -140,6 +140,12 @@ impl Handle { } } +impl From for Handle { + fn from(path: String) -> Handle { + Handle::from_path(path) + } +} + impl From<&str> for Handle { fn from(path: &str) -> Handle { Handle::from_path(path) From c2dad2942916ba30435a8e26ccca17f64c092b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 00:20:55 +0100 Subject: [PATCH 08/29] Expose styling types for `slider` in `iced_web` --- web/src/widget/slider.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs index 843003e6..5aa6439e 100644 --- a/web/src/widget/slider.rs +++ b/web/src/widget/slider.rs @@ -6,6 +6,8 @@ //! [`State`]: struct.State.html use crate::{Bus, Css, Element, Length, Widget}; +pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; + use dodrio::bumpalo; use std::{ops::RangeInclusive, rc::Rc}; @@ -38,6 +40,7 @@ pub struct Slider<'a, Message> { value: f32, on_change: Rc Message>>, width: Length, + style: Box, } impl<'a, Message> Slider<'a, Message> { @@ -68,6 +71,7 @@ impl<'a, Message> Slider<'a, Message> { range, on_change: Rc::new(Box::new(on_change)), width: Length::Fill, + style: Default::default(), } } @@ -78,6 +82,14 @@ impl<'a, Message> Slider<'a, Message> { self.width = width; self } + + /// Sets the style of the [`Slider`]. + /// + /// [`Slider`]: struct.Slider.html + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); + self + } } impl<'a, Message> Widget for Slider<'a, Message> @@ -103,7 +115,7 @@ where let event_bus = bus.clone(); // TODO: Make `step` configurable - // TODO: Complete styling + // TODO: Styling input(bump) .attr("type", "range") .attr("step", "0.01") From 9a7f7bfdf5afa329a60beab6556d739cf5d19b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 00:23:44 +0100 Subject: [PATCH 09/29] Expose styling types for `scrollable` in `iced_web` --- web/src/widget/scrollable.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs index 19810531..07b38aad 100644 --- a/web/src/widget/scrollable.rs +++ b/web/src/widget/scrollable.rs @@ -1,6 +1,8 @@ //! Navigate an endless amount of content with a scrollbar. use crate::{bumpalo, css, Align, Bus, Column, Css, Element, Length, Widget}; +pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; + /// A widget that can vertically display an infinite amount of content with a /// scrollbar. #[allow(missing_debug_implementations)] @@ -9,6 +11,7 @@ pub struct Scrollable<'a, Message> { height: Length, max_height: u32, content: Column<'a, Message>, + style: Box, } impl<'a, Message> Scrollable<'a, Message> { @@ -24,6 +27,7 @@ impl<'a, Message> Scrollable<'a, Message> { height: Length::Shrink, max_height: u32::MAX, content: Column::new(), + style: Default::default(), } } @@ -85,6 +89,14 @@ impl<'a, Message> Scrollable<'a, Message> { self } + /// Sets the style of the [`Scrollable`] . + /// + /// [`Scrollable`]: struct.Scrollable.html + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); + self + } + /// Adds an element to the [`Scrollable`]. /// /// [`Scrollable`]: struct.Scrollable.html @@ -112,6 +124,8 @@ where let width = css::length(self.width); let height = css::length(self.height); + // TODO: Scrollbar styling + let node = div(bump) .attr( "style", @@ -126,8 +140,6 @@ where ) .children(vec![self.content.node(bump, bus, style_sheet)]); - // TODO: Complete styling - node.finish() } } From ce45ecc23546efd85f04a76fcb1a3a691d259129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 00:28:25 +0100 Subject: [PATCH 10/29] Expose missing widget modules in `iced_web` --- web/src/widget.rs | 6 +++--- web/src/widget/checkbox.rs | 1 + web/src/widget/container.rs | 1 + web/src/widget/radio.rs | 1 + web/src/widget/text_input.rs | 3 +++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/web/src/widget.rs b/web/src/widget.rs index 3192a767..e5481243 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -18,15 +18,15 @@ use crate::{Bus, Css}; use dodrio::bumpalo; pub mod button; +pub mod checkbox; +pub mod container; pub mod image; +pub mod radio; pub mod scrollable; pub mod slider; pub mod text_input; -mod checkbox; mod column; -mod container; -mod radio; mod row; mod space; mod text; diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index d1d30048..7c7b805c 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -1,3 +1,4 @@ +//! Show toggle controls using checkboxes. use crate::{Bus, Color, Css, Element, Widget}; use dodrio::bumpalo; diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs index 55995795..43d9abf7 100644 --- a/web/src/widget/container.rs +++ b/web/src/widget/container.rs @@ -1,3 +1,4 @@ +//! Decorate content and apply alignment. use crate::{bumpalo, css, Align, Bus, Css, Element, Length, Widget}; /// An element decorating some content. diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index 4570639c..e8721cee 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -1,3 +1,4 @@ +//! Create choices using radio buttons. use crate::{Bus, Color, Css, Element, Widget}; use dodrio::bumpalo; diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index 1fea787a..2b22b93f 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -5,6 +5,9 @@ //! [`TextInput`]: struct.TextInput.html //! [`State`]: struct.State.html use crate::{bumpalo, css, Bus, Css, Element, Length, Widget}; + +pub use iced_style::text_input::{Style, StyleSheet}; + use std::rc::Rc; /// A field that can be filled with text. From abdae3a7ec1413df86fca93699fb2448226e2da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 00:31:52 +0100 Subject: [PATCH 11/29] Expose styling types for `checkbox` in `iced_web` --- web/src/widget/checkbox.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 7c7b805c..5160e221 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -1,5 +1,7 @@ //! Show toggle controls using checkboxes. -use crate::{Bus, Color, Css, Element, Widget}; +use crate::{Bus, Css, Element, Length, Widget}; + +pub use iced_style::checkbox::{Style, StyleSheet}; use dodrio::bumpalo; use std::rc::Rc; @@ -26,7 +28,8 @@ pub struct Checkbox { is_checked: bool, on_toggle: Rc Message>, label: String, - label_color: Option, + width: Length, + style: Box, } impl Checkbox { @@ -48,15 +51,24 @@ impl Checkbox { is_checked, on_toggle: Rc::new(f), label: String::from(label), - label_color: None, + width: Length::Shrink, + style: Default::default(), } } - /// Sets the color of the label of the [`Checkbox`]. + /// Sets the width of the [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html - pub fn label_color>(mut self, color: C) -> Self { - self.label_color = Some(color.into()); + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the style of the [`Checkbox`]. + /// + /// [`Checkbox`]: struct.Checkbox.html + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); self } } @@ -79,7 +91,8 @@ where let on_toggle = self.on_toggle.clone(); let is_checked = self.is_checked; - // TODO: Complete styling + // TODO: Styling + label(bump) .children(vec![ input(bump) From b4a8471aa187de511301605a56cf531923dfb6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 00:33:26 +0100 Subject: [PATCH 12/29] Expose styling types for `radio` in `iced_web` --- web/src/widget/radio.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index e8721cee..e00e26db 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -1,5 +1,7 @@ //! Create choices using radio buttons. -use crate::{Bus, Color, Css, Element, Widget}; +use crate::{Bus, Css, Element, Widget}; + +pub use iced_style::radio::{Style, StyleSheet}; use dodrio::bumpalo; @@ -33,7 +35,7 @@ pub struct Radio { is_selected: bool, on_click: Message, label: String, - label_color: Option, + style: Box, } impl Radio { @@ -56,15 +58,15 @@ impl Radio { is_selected: Some(value) == selected, on_click: f(value), label: String::from(label), - label_color: None, + style: Default::default(), } } - /// Sets the `Color` of the label of the [`Radio`]. + /// Sets the style of the [`Radio`] button. /// /// [`Radio`]: struct.Radio.html - pub fn label_color>(mut self, color: C) -> Self { - self.label_color = Some(color.into()); + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); self } } From 58a7be0a31793400e5686f8e2af558f2307bebcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 00:36:00 +0100 Subject: [PATCH 13/29] Expose styling types for `container` in `iced_web` --- web/src/widget/container.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs index 43d9abf7..767fed67 100644 --- a/web/src/widget/container.rs +++ b/web/src/widget/container.rs @@ -1,6 +1,8 @@ //! Decorate content and apply alignment. use crate::{bumpalo, css, Align, Bus, Css, Element, Length, Widget}; +pub use iced_style::container::{Style, StyleSheet}; + /// An element decorating some content. /// /// It is normally used for alignment purposes. @@ -12,6 +14,7 @@ pub struct Container<'a, Message> { max_height: u32, horizontal_alignment: Align, vertical_alignment: Align, + style: Box, content: Element<'a, Message>, } @@ -32,6 +35,7 @@ impl<'a, Message> Container<'a, Message> { max_height: u32::MAX, horizontal_alignment: Align::Start, vertical_alignment: Align::Start, + style: Default::default(), content: content.into(), } } @@ -85,6 +89,14 @@ impl<'a, Message> Container<'a, Message> { self } + + /// Sets the style of the [`Container`]. + /// + /// [`Container`]: struct.Container.html + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); + self + } } impl<'a, Message> Widget for Container<'a, Message> From fb8dc41f6dee6976395b03cc1fce3d21edb1038a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 01:21:53 +0100 Subject: [PATCH 14/29] Add additional helpers to `css` module in `iced_web` --- web/src/css.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/web/src/css.rs b/web/src/css.rs index df0938da..6a307770 100644 --- a/web/src/css.rs +++ b/web/src/css.rs @@ -1,5 +1,5 @@ //! Style your widgets. -use crate::{bumpalo, Align, Color, Length}; +use crate::{bumpalo, Align, Background, Color, Length}; use std::collections::BTreeMap; @@ -119,7 +119,7 @@ impl<'a> Css<'a> { declarations.push(text( "body { height: 100%; margin: 0; padding: 0; font-family: sans-serif }", )); - declarations.push(text("p { margin: 0 }")); + declarations.push(text("* { margin: 0; padding: 0 }")); declarations.push(text( "button { border: none; cursor: pointer; outline: none }", )); @@ -143,6 +143,26 @@ pub fn length(length: Length) -> String { } } +/// Returns the style value for the given maximum length in units. +pub fn max_length(units: u32) -> String { + use std::u32; + + if units == u32::MAX { + String::from("initial") + } else { + format!("{}px", units) + } +} + +/// Returns the style value for the given minimum length in units. +pub fn min_length(units: u32) -> String { + if units == 0 { + String::from("initial") + } else { + format!("{}px", units) + } +} + /// Returns the style value for the given [`Color`]. /// /// [`Color`]: ../struct.Color.html @@ -150,6 +170,15 @@ pub fn color(Color { r, g, b, a }: Color) -> String { format!("rgba({}, {}, {}, {})", 255.0 * r, 255.0 * g, 255.0 * b, a) } +/// Returns the style value for the given [`Background`]. +/// +/// [`Background`]: ../struct.Background.html +pub fn background(background: Background) -> String { + match background { + Background::Color(c) => color(c), + } +} + /// Returns the style value for the given [`Align`]. /// /// [`Align`]: ../enum.Align.html From acfc815e1df7209d1e43f36e5f465b0ac44294cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 01:24:04 +0100 Subject: [PATCH 15/29] Let checkbox breathe in `styling` example --- examples/styling/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 50095ec7..47408624 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -104,6 +104,7 @@ impl Sandbox for Styling { "Toggle me!", Message::CheckboxToggled, ) + .width(Length::Fill) .style(self.theme); let content = Column::new() From f5228695a2fcdecd1da9071d477ee34855783b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 01:24:40 +0100 Subject: [PATCH 16/29] Implement `ProgressBar` widget in `iced_web` --- web/src/widget.rs | 2 + web/src/widget/progress_bar.rs | 125 +++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 web/src/widget/progress_bar.rs diff --git a/web/src/widget.rs b/web/src/widget.rs index e5481243..025cf22f 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -21,6 +21,7 @@ pub mod button; pub mod checkbox; pub mod container; pub mod image; +pub mod progress_bar; pub mod radio; pub mod scrollable; pub mod slider; @@ -46,6 +47,7 @@ pub use checkbox::Checkbox; pub use column::Column; pub use container::Container; pub use image::Image; +pub use progress_bar::ProgressBar; pub use radio::Radio; pub use row::Row; pub use space::Space; diff --git a/web/src/widget/progress_bar.rs b/web/src/widget/progress_bar.rs new file mode 100644 index 00000000..b1fcb4d8 --- /dev/null +++ b/web/src/widget/progress_bar.rs @@ -0,0 +1,125 @@ +//! Provide progress feedback to your users. +use crate::{bumpalo, css, Bus, Css, Element, Length, Widget}; + +pub use iced_style::progress_bar::{Style, StyleSheet}; + +use std::ops::RangeInclusive; + +/// A bar that displays progress. +/// +/// # Example +/// ``` +/// # use iced_native::renderer::Null; +/// # +/// # pub type ProgressBar = iced_native::ProgressBar; +/// let value = 50.0; +/// +/// ProgressBar::new(0.0..=100.0, value); +/// ``` +/// +/// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png) +#[allow(missing_debug_implementations)] +pub struct ProgressBar { + range: RangeInclusive, + value: f32, + width: Length, + height: Option, + style: Box, +} + +impl ProgressBar { + /// Creates a new [`ProgressBar`]. + /// + /// It expects: + /// * an inclusive range of possible values + /// * the current value of the [`ProgressBar`] + /// + /// [`ProgressBar`]: struct.ProgressBar.html + pub fn new(range: RangeInclusive, value: f32) -> Self { + ProgressBar { + value: value.max(*range.start()).min(*range.end()), + range, + width: Length::Fill, + height: None, + style: Default::default(), + } + } + + /// Sets the width of the [`ProgressBar`]. + /// + /// [`ProgressBar`]: struct.ProgressBar.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`ProgressBar`]. + /// + /// [`ProgressBar`]: struct.ProgressBar.html + pub fn height(mut self, height: Length) -> Self { + self.height = Some(height); + self + } + + /// Sets the style of the [`ProgressBar`]. + /// + /// [`ProgressBar`]: struct.ProgressBar.html + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); + self + } +} + +impl Widget for ProgressBar { + fn node<'b>( + &self, + bump: &'b bumpalo::Bump, + _bus: &Bus, + _style_sheet: &mut Css<'b>, + ) -> dodrio::Node<'b> { + use dodrio::builder::*; + + let (range_start, range_end) = self.range.clone().into_inner(); + let amount_filled = + (self.value - range_start) / (range_end - range_start).max(1.0); + + let style = self.style.style(); + + let bar = div(bump) + .attr( + "style", + bumpalo::format!( + in bump, + "width: {}%; height: 100%; background: {}", + amount_filled * 100.0, + css::background(style.bar) + ) + .into_bump_str(), + ) + .finish(); + + let node = div(bump).attr( + "style", + bumpalo::format!( + in bump, + "width: {}; height: {}; background: {}; border-radius: {}px; overflow: hidden;", + css::length(self.width), + css::length(self.height.unwrap_or(Length::Units(30))), + css::background(style.background), + style.border_radius + ) + .into_bump_str(), + ).children(vec![bar]); + + node.finish() + } +} + +impl<'a, Message> From for Element<'a, Message> +where + Message: 'static, +{ + fn from(container: ProgressBar) -> Element<'a, Message> { + Element::new(container) + } +} From 38a35ab639de3cb14cafa2cecef7ecf56d29b631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 01:25:03 +0100 Subject: [PATCH 17/29] Fix `Button` styling in `iced_web` --- web/src/widget/button.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index e47b971d..3a5afe60 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -144,9 +144,6 @@ where // TODO: State-based styling let style = self.style.active(); - let width = css::length(self.width); - let color = css::color(style.text_color); - let padding_class = style_sheet.insert(bump, css::Rule::Padding(self.padding)); @@ -166,12 +163,12 @@ where "style", bumpalo::format!( in bump, - "background: {}; border-radius: {}px; width:{}; min-width: {}px; color: {}", + "background: {}; border-radius: {}px; width:{}; min-width: {}; color: {}", background, style.border_radius, - width, - self.min_width, - color + css::length(self.width), + css::min_length(self.min_width), + css::color(style.text_color) ) .into_bump_str(), ) From 6c8fdfefbe6ea8cd1cc11a4a822285c28c278880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 01:25:22 +0100 Subject: [PATCH 18/29] Fix `Checkbox` styling in `iced_web` --- web/src/widget/checkbox.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 5160e221..0657ccfb 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -1,5 +1,5 @@ //! Show toggle controls using checkboxes. -use crate::{Bus, Css, Element, Length, Widget}; +use crate::{css, Bus, Css, Element, Length, Widget}; pub use iced_style::checkbox::{Style, StyleSheet}; @@ -81,7 +81,7 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus, - _style_sheet: &mut Css<'b>, + style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; @@ -91,10 +91,23 @@ where let on_toggle = self.on_toggle.clone(); let is_checked = self.is_checked; - // TODO: Styling + let row_class = style_sheet.insert(bump, css::Rule::Row); + + let spacing_class = style_sheet.insert(bump, css::Rule::Spacing(5)); label(bump) + .attr( + "class", + bumpalo::format!(in bump, "{} {}", row_class, spacing_class) + .into_bump_str(), + ) + .attr( + "style", + bumpalo::format!(in bump, "width: {}; align-items: center", css::length(self.width)) + .into_bump_str(), + ) .children(vec![ + // TODO: Checkbox styling input(bump) .attr("type", "checkbox") .bool_attr("checked", self.is_checked) @@ -105,7 +118,8 @@ where vdom.schedule_render(); }) .finish(), - text(checkbox_label.into_bump_str()), + span(bump).children(vec![ + text(checkbox_label.into_bump_str())]).finish(), ]) .finish() } From 8e83f3632c7b06370ce47d975750313a56221d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 01:25:46 +0100 Subject: [PATCH 19/29] Fix `Column` and `Row` styling for `iced_web` --- web/src/widget/column.rs | 17 ++++++----------- web/src/widget/row.rs | 17 ++++++----------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs index 640d3673..6454ffba 100644 --- a/web/src/widget/column.rs +++ b/web/src/widget/column.rs @@ -130,11 +130,6 @@ impl<'a, Message> Widget for Column<'a, Message> { let padding_class = style_sheet.insert(bump, css::Rule::Padding(self.padding)); - let width = css::length(self.width); - let height = css::length(self.height); - - let align_items = css::align(self.align_items); - // TODO: Complete styling div(bump) .attr( @@ -144,12 +139,12 @@ impl<'a, Message> Widget for Column<'a, Message> { ) .attr("style", bumpalo::format!( in bump, - "width: {}; height: {}; max-width: {}px; max-height: {}px; align-items: {}", - width, - height, - self.max_width, - self.max_height, - align_items + "width: {}; height: {}; max-width: {}; max-height: {}; align-items: {}", + css::length(self.width), + css::length(self.height), + css::max_length(self.max_width), + css::max_length(self.max_height), + css::align(self.align_items) ).into_bump_str() ) .children(children) diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs index b360bc45..02035113 100644 --- a/web/src/widget/row.rs +++ b/web/src/widget/row.rs @@ -131,11 +131,6 @@ impl<'a, Message> Widget for Row<'a, Message> { let padding_class = style_sheet.insert(bump, css::Rule::Padding(self.padding)); - let width = css::length(self.width); - let height = css::length(self.height); - - let justify_content = css::align(self.align_items); - // TODO: Complete styling div(bump) .attr( @@ -145,12 +140,12 @@ impl<'a, Message> Widget for Row<'a, Message> { ) .attr("style", bumpalo::format!( in bump, - "width: {}; height: {}; max-width: {}px; max-height: {}px; justify-content: {}", - width, - height, - self.max_width, - self.max_height, - justify_content + "width: {}; height: {}; max-width: {}; max-height: {}; align-items: {}", + css::length(self.width), + css::length(self.height), + css::max_length(self.max_width), + css::max_length(self.max_height), + css::align(self.align_items) ).into_bump_str() ) .children(children) From e953733323c1c2a50cc511408167c7982f84bf06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 01:26:08 +0100 Subject: [PATCH 20/29] Add style to `TextInput` in `iced_web` --- web/src/widget/text_input.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index 2b22b93f..c1034e43 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -42,6 +42,7 @@ pub struct TextInput<'a, Message> { size: Option, on_change: Rc Message>>, on_submit: Option, + style: Box, } impl<'a, Message> TextInput<'a, Message> { @@ -75,6 +76,7 @@ impl<'a, Message> TextInput<'a, Message> { size: None, on_change: Rc::new(Box::new(on_change)), on_submit: None, + style: Default::default(), } } @@ -126,6 +128,14 @@ impl<'a, Message> TextInput<'a, Message> { self.on_submit = Some(message); self } + + /// Sets the style of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); + self + } } impl<'a, Message> Widget for TextInput<'a, Message> From cf3ca442104994411529dfe21f7dced08f4ee1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 02:24:18 +0100 Subject: [PATCH 21/29] Implement `Container` styling in `iced_web` --- web/src/widget/container.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs index 767fed67..8e4318f9 100644 --- a/web/src/widget/container.rs +++ b/web/src/widget/container.rs @@ -14,7 +14,7 @@ pub struct Container<'a, Message> { max_height: u32, horizontal_alignment: Align, vertical_alignment: Align, - style: Box, + style_sheet: Box, content: Element<'a, Message>, } @@ -35,7 +35,7 @@ impl<'a, Message> Container<'a, Message> { max_height: u32::MAX, horizontal_alignment: Align::Start, vertical_alignment: Align::Start, - style: Default::default(), + style_sheet: Default::default(), content: content.into(), } } @@ -94,7 +94,7 @@ impl<'a, Message> Container<'a, Message> { /// /// [`Container`]: struct.Container.html pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); + self.style_sheet = style.into(); self } } @@ -113,11 +113,7 @@ where let column_class = style_sheet.insert(bump, css::Rule::Column); - let width = css::length(self.width); - let height = css::length(self.height); - - let align_items = css::align(self.horizontal_alignment); - let justify_content = css::align(self.vertical_alignment); + let style = self.style_sheet.style(); let node = div(bump) .attr( @@ -128,12 +124,17 @@ where "style", bumpalo::format!( in bump, - "width: {}; height: {}; max-width: {}px; align-items: {}; justify-content: {}", - width, - height, - self.max_width, - align_items, - justify_content + "width: {}; height: {}; max-width: {}; align-items: {}; justify-content: {}; background: {}; color: {}; border-width: {}px; border-color: {}; border-radius: {}px", + css::length(self.width), + css::length(self.height), + css::max_length(self.max_width), + css::align(self.horizontal_alignment), + css::align(self.vertical_alignment), + style.background.map(css::background).unwrap_or(String::from("initial")), + style.text_color.map(css::color).unwrap_or(String::from("inherit")), + style.border_width, + css::color(style.border_color), + style.border_radius ) .into_bump_str(), ) From 07e62ae5dacdab6a9bb7d4c32844286764753acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 02:29:19 +0100 Subject: [PATCH 22/29] Fix `ProgressBar` doc example in `iced_web` --- web/src/widget/progress_bar.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/web/src/widget/progress_bar.rs b/web/src/widget/progress_bar.rs index b1fcb4d8..856203c0 100644 --- a/web/src/widget/progress_bar.rs +++ b/web/src/widget/progress_bar.rs @@ -9,15 +9,14 @@ use std::ops::RangeInclusive; /// /// # Example /// ``` -/// # use iced_native::renderer::Null; -/// # -/// # pub type ProgressBar = iced_native::ProgressBar; +/// use iced_web::ProgressBar; +/// /// let value = 50.0; /// /// ProgressBar::new(0.0..=100.0, value); /// ``` /// -/// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png) +/// ![Progress bar](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png) #[allow(missing_debug_implementations)] pub struct ProgressBar { range: RangeInclusive, From 282ae1dc9e4d0aa4dec4cee575b5eaa5f9a90b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 02:37:49 +0100 Subject: [PATCH 23/29] Implement `TextInput` styling in `iced_web` --- native/src/widget/text_input.rs | 9 ++++++--- web/src/widget/text_input.rs | 30 +++++++++++++++++------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 7e212add..04118755 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -9,6 +9,8 @@ use crate::{ layout, Clipboard, Element, Event, Font, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; + +use std::u32; use unicode_segmentation::UnicodeSegmentation; /// A field that can be filled with text. @@ -43,7 +45,7 @@ pub struct TextInput<'a, Message, Renderer: self::Renderer> { is_secure: bool, font: Font, width: Length, - max_width: Length, + max_width: u32, padding: u16, size: Option, on_change: Box Message>, @@ -78,7 +80,7 @@ impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> { is_secure: false, font: Font::Default, width: Length::Fill, - max_width: Length::Shrink, + max_width: u32::MAX, padding: 0, size: None, on_change: Box::new(on_change), @@ -114,7 +116,7 @@ impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> { /// Sets the maximum width of the [`TextInput`]. /// /// [`TextInput`]: struct.TextInput.html - pub fn max_width(mut self, max_width: Length) -> Self { + pub fn max_width(mut self, max_width: u32) -> Self { self.max_width = max_width; self } @@ -178,6 +180,7 @@ where let limits = limits .pad(padding) .width(self.width) + .max_width(self.max_width) .height(Length::Units(text_size)); let mut text = layout::Node::new(limits.resolve(Size::ZERO)); diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index c1034e43..0992208b 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -8,7 +8,7 @@ use crate::{bumpalo, css, Bus, Css, Element, Length, Widget}; pub use iced_style::text_input::{Style, StyleSheet}; -use std::rc::Rc; +use std::{rc::Rc, u32}; /// A field that can be filled with text. /// @@ -37,12 +37,12 @@ pub struct TextInput<'a, Message> { value: String, is_secure: bool, width: Length, - max_width: Length, + max_width: u32, padding: u16, size: Option, on_change: Rc Message>>, on_submit: Option, - style: Box, + style_sheet: Box, } impl<'a, Message> TextInput<'a, Message> { @@ -71,12 +71,12 @@ impl<'a, Message> TextInput<'a, Message> { value: String::from(value), is_secure: false, width: Length::Fill, - max_width: Length::Shrink, + max_width: u32::MAX, padding: 0, size: None, on_change: Rc::new(Box::new(on_change)), on_submit: None, - style: Default::default(), + style_sheet: Default::default(), } } @@ -99,7 +99,7 @@ impl<'a, Message> TextInput<'a, Message> { /// Sets the maximum width of the [`TextInput`]. /// /// [`TextInput`]: struct.TextInput.html - pub fn max_width(mut self, max_width: Length) -> Self { + pub fn max_width(mut self, max_width: u32) -> Self { self.max_width = max_width; self } @@ -133,7 +133,7 @@ impl<'a, Message> TextInput<'a, Message> { /// /// [`TextInput`]: struct.TextInput.html pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); + self.style_sheet = style.into(); self } } @@ -151,13 +151,12 @@ where use dodrio::builder::*; use wasm_bindgen::JsCast; - let width = css::length(self.width); - let max_width = css::length(self.max_width); let padding_class = style_sheet.insert(bump, css::Rule::Padding(self.padding)); let on_change = self.on_change.clone(); let event_bus = bus.clone(); + let style = self.style_sheet.active(); input(bump) .attr( @@ -168,10 +167,15 @@ where "style", bumpalo::format!( in bump, - "width: {}; max-width: {}; font-size: {}px", - width, - max_width, - self.size.unwrap_or(20) + "width: {}; max-width: {}; font-size: {}px; background: {}; border-width: {}px; border-color: {}; border-radius: {}px; color: {}", + css::length(self.width), + css::max_length(self.max_width), + self.size.unwrap_or(20), + css::background(style.background), + style.border_width, + css::color(style.border_color), + style.border_radius, + css::color(self.style_sheet.value_color()) ) .into_bump_str(), ) From 57aed1d5c61a14fa6337e9f838fc519efdd75e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 03:06:06 +0100 Subject: [PATCH 24/29] Implement `TextInput::on_submit` support in `iced_web` --- web/Cargo.toml | 1 + web/src/widget/text_input.rs | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/web/Cargo.toml b/web/Cargo.toml index 7ddf3400..c043c697 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -39,4 +39,5 @@ features = [ "Event", "EventTarget", "InputEvent", + "KeyboardEvent", ] diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index 0992208b..3fa458bd 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -155,7 +155,9 @@ where style_sheet.insert(bump, css::Rule::Padding(self.padding)); let on_change = self.on_change.clone(); - let event_bus = bus.clone(); + let on_submit = self.on_submit.clone(); + let input_event_bus = bus.clone(); + let submit_event_bus = bus.clone(); let style = self.style_sheet.active(); input(bump) @@ -200,7 +202,17 @@ where Some(text_input) => text_input, }; - event_bus.publish(on_change(text_input.value())); + input_event_bus.publish(on_change(text_input.value())); + }) + .on("keypress", move |_root, _vdom, event| { + if let Some(on_submit) = on_submit.clone() { + let event = event.unchecked_into::(); + + match event.key_code() { + 13 => { submit_event_bus.publish(on_submit); } + _ => {} + } + } }) .finish() } @@ -228,4 +240,12 @@ impl State { pub fn new() -> Self { Self::default() } + + /// Creates a new [`State`], representing a focused [`TextInput`]. + /// + /// [`State`]: struct.State.html + pub fn focused() -> Self { + // TODO + Self::default() + } } From e8316b208705910958152b2ef6c4c5d7110b4e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 03:06:39 +0100 Subject: [PATCH 25/29] Allow `todos` example to compile to `wasm32` --- examples/todos/Cargo.toml | 4 +++- examples/todos/src/main.rs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml index 21acd5d6..cfb8e97d 100644 --- a/examples/todos/Cargo.toml +++ b/examples/todos/Cargo.toml @@ -8,9 +8,11 @@ publish = false [dependencies] iced = { path = "../.." } iced_futures = { path = "../../futures", features = ["async-std"] } -async-std = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +async-std = "1.0" directories = "2.0" [package.metadata.deb] diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index bfae5e88..8262b537 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -38,7 +38,12 @@ enum Message { } impl Application for Todos { + #[cfg(not(target_arch = "wasm32"))] type Executor = iced_futures::executor::AsyncStd; + + #[cfg(target_arch = "wasm32")] + type Executor = iced_futures::executor::WasmBindgen; + type Message = Message; fn new() -> (Todos, Command) { @@ -377,6 +382,7 @@ impl Controls { ) .push( Row::new() + .width(Length::Shrink) .spacing(10) .push(filter_button( all_button, @@ -493,6 +499,7 @@ enum SaveError { FormatError, } +#[cfg(not(target_arch = "wasm32"))] impl SavedState { fn path() -> std::path::PathBuf { let mut path = if let Some(project_dirs) = @@ -555,6 +562,18 @@ impl SavedState { } } +// TODO +#[cfg(target_arch = "wasm32")] +impl SavedState { + async fn load() -> Result { + Err(LoadError::FileError) + } + + async fn save(self) -> Result<(), SaveError> { + Err(SaveError::FileError) + } +} + mod style { use iced::{button, Background, Color, Vector}; From ad500441afc355f0b8ca2a463248d350d74f0f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 03:56:21 +0100 Subject: [PATCH 26/29] Allow switching `executor::Default` with features --- Cargo.toml | 11 +++++++++-- examples/pokedex/Cargo.toml | 3 +-- examples/pokedex/src/main.rs | 7 +------ examples/todos/Cargo.toml | 3 +-- examples/todos/src/main.rs | 7 +------ src/executor.rs | 31 +++++++++++++++++++++---------- 6 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7bfce09a..39c5957a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,16 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [features] -# Enables the Image widget +# Enables the `Image` widget image = ["iced_wgpu/image"] -# Enables the Svg widget +# Enables the `Svg` widget svg = ["iced_wgpu/svg"] # Enables a debug view in native platforms (press F12) debug = ["iced_winit/debug"] +# Enables `tokio` as the `executor::Default` on native platforms +tokio = ["iced_futures/tokio"] +# Enables `async-std` as the `executor::Default` on native platforms +async-std = ["iced_futures/async-std"] [badges] maintenance = { status = "actively-developed" } @@ -45,6 +49,9 @@ members = [ "examples/tour", ] +[dependencies] +iced_futures = { version = "0.1.0-alpha", path = "futures" } + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iced_winit = { version = "0.1.0-alpha", path = "winit" } iced_wgpu = { version = "0.1.0", path = "wgpu" } diff --git a/examples/pokedex/Cargo.toml b/examples/pokedex/Cargo.toml index f8668be0..c1e3edb5 100644 --- a/examples/pokedex/Cargo.toml +++ b/examples/pokedex/Cargo.toml @@ -6,8 +6,7 @@ edition = "2018" publish = false [dependencies] -iced = { path = "../..", features = ["image", "debug"] } -iced_futures = { path = "../../futures", features = ["tokio"] } +iced = { path = "../..", features = ["image", "debug", "tokio"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rand = { version = "0.7", features = ["wasm-bindgen"] } diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index 13e420a9..b0cb3c55 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -27,12 +27,7 @@ enum Message { } impl Application for Pokedex { - #[cfg(not(target_arch = "wasm32"))] - type Executor = iced_futures::executor::Tokio; - - #[cfg(target_arch = "wasm32")] - type Executor = iced_futures::executor::WasmBindgen; - + type Executor = iced::executor::Default; type Message = Message; fn new() -> (Pokedex, Command) { diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml index cfb8e97d..c905fc38 100644 --- a/examples/todos/Cargo.toml +++ b/examples/todos/Cargo.toml @@ -6,8 +6,7 @@ edition = "2018" publish = false [dependencies] -iced = { path = "../.." } -iced_futures = { path = "../../futures", features = ["async-std"] } +iced = { path = "../..", features = ["async-std"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 8262b537..a4009874 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -38,12 +38,7 @@ enum Message { } impl Application for Todos { - #[cfg(not(target_arch = "wasm32"))] - type Executor = iced_futures::executor::AsyncStd; - - #[cfg(target_arch = "wasm32")] - type Executor = iced_futures::executor::WasmBindgen; - + type Executor = iced::executor::Default; type Message = Message; fn new() -> (Todos, Command) { diff --git a/src/executor.rs b/src/executor.rs index 539bfd5d..e31bd93d 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,38 +5,49 @@ pub use platform::Default; #[cfg(not(target_arch = "wasm32"))] mod platform { - use iced_winit::{executor::ThreadPool, futures, Executor}; + use iced_futures::{executor, futures}; + + #[cfg(feature = "tokio")] + type Executor = executor::Tokio; + + #[cfg(all(not(feature = "tokio"), feature = "async-std"))] + type Executor = executor::AsyncStd; + + #[cfg(not(any(feature = "tokio", feature = "async-std")))] + type Executor = executor::ThreadPool; /// A default cross-platform executor. /// - /// - On native platforms, it will use a - /// `iced_futures::executor::ThreadPool`. + /// - On native platforms, it will use: + /// - `iced_futures::executor::Tokio` when the `tokio` feature is enabled. + /// - `iced_futures::executor::AsyncStd` when the `async-std` feature is + /// enabled. + /// - `iced_futures::executor::ThreadPool` otherwise. /// - On the Web, it will use `iced_futures::executor::WasmBindgen`. #[derive(Debug)] - pub struct Default(ThreadPool); + pub struct Default(Executor); - impl Executor for Default { + impl super::Executor for Default { fn new() -> Result { - Ok(Default(ThreadPool::new()?)) + Ok(Default(Executor::new()?)) } fn spawn( &self, future: impl futures::Future + Send + 'static, ) { - self.0.spawn(future); + let _ = self.0.spawn(future); } } } #[cfg(target_arch = "wasm32")] mod platform { - use iced_web::{executor::WasmBindgen, futures, Executor}; + use iced_futures::{executor::WasmBindgen, futures, Executor}; /// A default cross-platform executor. /// - /// - On native platforms, it will use a - /// `iced_futures::executor::ThreadPool`. + /// - On native platforms, it will use `iced_futures::executor::ThreadPool`. /// - On the Web, it will use `iced_futures::executor::WasmBindgen`. #[derive(Debug)] pub struct Default(WasmBindgen); From f719ba3f4efebdf29cd5922d9e083f8dea96c486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 05:03:11 +0100 Subject: [PATCH 27/29] Add `font-family` to `Text` style in `iced_web` --- examples/pokedex/src/main.rs | 1 + web/src/widget/text.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index b0cb3c55..4449b901 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -79,6 +79,7 @@ impl Application for Pokedex { fn view(&mut self) -> Element { let content = match self { Pokedex::Loading => Column::new() + .width(Length::Shrink) .push(Text::new("Searching for Pokémon...").size(40)), Pokedex::Loaded { pokemon, search } => Column::new() .max_width(500) diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs index 46230fef..3ec565a8 100644 --- a/web/src/widget/text.rs +++ b/web/src/widget/text.rs @@ -133,12 +133,16 @@ impl<'a, Message> Widget for Text { let style = bumpalo::format!( in bump, - "width: {}; height: {}; font-size: {}px; color: {}; text-align: {}", + "width: {}; height: {}; font-size: {}px; color: {}; text-align: {}; font-family: {}", width, height, self.size.unwrap_or(20), color, - text_align + text_align, + match self.font { + Font::Default => "inherit", + Font::External { name, .. } => name, + } ); // TODO: Complete styling From 679d758627f6500de4b2220c0dba3460871b83b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 05:23:05 +0100 Subject: [PATCH 28/29] Remove `wasm_bindgen(start)` from `tour` example --- examples/tour/Cargo.toml | 3 --- examples/tour/src/main.rs | 13 ------------- 2 files changed, 16 deletions(-) diff --git a/examples/tour/Cargo.toml b/examples/tour/Cargo.toml index 45105c31..96749e90 100644 --- a/examples/tour/Cargo.toml +++ b/examples/tour/Cargo.toml @@ -8,6 +8,3 @@ publish = false [dependencies] iced = { path = "../..", features = ["image", "debug"] } env_logger = "0.7" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = "0.2.51" diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 43c7e50f..800254ed 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -779,16 +779,3 @@ mod style { } } } - -// This should be gracefully handled by Iced in the future. Probably using our -// own proc macro, or maybe the whole process is streamlined by `wasm-pack` at -// some point. -#[cfg(target_arch = "wasm32")] -mod wasm { - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(start)] - pub fn run() { - super::main() - } -} From 36e617ae70cc7a86ce998cbd61f6aa702bb42933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 6 Feb 2020 05:56:23 +0100 Subject: [PATCH 29/29] Implement local storage for `todos` example in Wasm --- examples/todos/Cargo.toml | 4 ++++ examples/todos/src/main.rs | 29 ++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml index c905fc38..f945cde5 100644 --- a/examples/todos/Cargo.toml +++ b/examples/todos/Cargo.toml @@ -14,6 +14,10 @@ serde_json = "1.0" async-std = "1.0" directories = "2.0" +[target.'cfg(target_arch = "wasm32")'.dependencies] +web-sys = { version = "0.3", features = ["Window", "Storage"] } +wasm-timer = "0.2" + [package.metadata.deb] assets = [ ["target/release/todos", "usr/bin/iced-todos", "755"], diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index a4009874..7e866b19 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -557,15 +557,38 @@ impl SavedState { } } -// TODO #[cfg(target_arch = "wasm32")] impl SavedState { + fn storage() -> Option { + let window = web_sys::window()?; + + window.local_storage().ok()? + } + async fn load() -> Result { - Err(LoadError::FileError) + let storage = Self::storage().ok_or(LoadError::FileError)?; + + let contents = storage + .get_item("state") + .map_err(|_| LoadError::FileError)? + .ok_or(LoadError::FileError)?; + + serde_json::from_str(&contents).map_err(|_| LoadError::FormatError) } async fn save(self) -> Result<(), SaveError> { - Err(SaveError::FileError) + let storage = Self::storage().ok_or(SaveError::FileError)?; + + let json = serde_json::to_string_pretty(&self) + .map_err(|_| SaveError::FormatError)?; + + storage + .set_item("state", &json) + .map_err(|_| SaveError::WriteError)?; + + let _ = wasm_timer::Delay::new(std::time::Duration::from_secs(2)).await; + + Ok(()) } }