summaryrefslogtreecommitdiffstats
path: root/web/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web/src/css.rs (renamed from web/src/style.rs)83
-rw-r--r--web/src/element.rs6
-rw-r--r--web/src/lib.rs10
-rw-r--r--web/src/widget.rs14
-rw-r--r--web/src/widget/button.rs66
-rw-r--r--web/src/widget/checkbox.rs46
-rw-r--r--web/src/widget/column.rs29
-rw-r--r--web/src/widget/container.rs42
-rw-r--r--web/src/widget/image.rs91
-rw-r--r--web/src/widget/progress_bar.rs124
-rw-r--r--web/src/widget/radio.rs17
-rw-r--r--web/src/widget/row.rs29
-rw-r--r--web/src/widget/scrollable.rs24
-rw-r--r--web/src/widget/slider.rs18
-rw-r--r--web/src/widget/space.rs8
-rw-r--r--web/src/widget/text.rs21
-rw-r--r--web/src/widget/text_input.rs67
17 files changed, 522 insertions, 173 deletions
diff --git a/web/src/style.rs b/web/src/css.rs
index 4f72b22c..6a307770 100644
--- a/web/src/style.rs
+++ b/web/src/css.rs
@@ -1,11 +1,11 @@
//! Style your widgets.
-use crate::{bumpalo, Align, Color, Length};
+use crate::{bumpalo, Align, Background, 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<String, &'a str>,
+pub struct Css<'a> {
+ rules: BTreeMap<String, &'a str>,
}
-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
@@ -119,12 +119,12 @@ impl<'a> Sheet<'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 }",
));
- for declaration in self.styles.values() {
+ for declaration in self.rules.values() {
declarations.push(text(*declaration));
}
@@ -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
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<Message>,
- 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<B>,
- 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..025cf22f 100644
--- a/web/src/widget.rs
+++ b/web/src/widget.rs
@@ -14,19 +14,20 @@
//! ```
//!
//! [`Widget`]: trait.Widget.html
-use crate::{style, Bus};
+use crate::{Bus, Css};
use dodrio::bumpalo;
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;
pub mod text_input;
-mod checkbox;
mod column;
-mod container;
-mod image;
-mod radio;
mod row;
mod space;
mod text;
@@ -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;
@@ -64,6 +66,6 @@ pub trait Widget<Message> {
&self,
bump: &'b bumpalo::Bump,
_bus: &Bus<Message>,
- 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..3a5afe60 100644
--- a/web/src/widget/button.rs
+++ b/web/src/widget/button.rs
@@ -4,7 +4,9 @@
//!
//! [`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};
+
+pub use iced_style::button::{Style, StyleSheet};
use dodrio::bumpalo;
@@ -26,10 +28,11 @@ pub struct Button<'a, Message> {
content: Element<'a, Message>,
on_press: Option<Message>,
width: Length,
+ height: Length,
min_width: u32,
+ min_height: u32,
padding: u16,
- background: Option<Background>,
- border_radius: u16,
+ style: Box<dyn StyleSheet>,
}
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,28 +81,27 @@ impl<'a, Message> Button<'a, Message> {
self
}
- /// Sets the padding of the [`Button`].
+ /// Sets the minimum height of the [`Button`].
///
/// [`Button`]: struct.Button.html
- pub fn padding(mut self, padding: u16) -> Self {
- self.padding = padding;
+ pub fn min_height(mut self, min_height: u32) -> Self {
+ self.min_height = min_height;
self
}
- /// Sets the [`Background`] of the [`Button`].
+ /// Sets the padding of the [`Button`].
///
/// [`Button`]: struct.Button.html
- /// [`Background`]: ../../struct.Background.html
- pub fn background<T: Into<Background>>(mut self, background: T) -> Self {
- self.background = Some(background.into());
+ pub fn padding(mut self, padding: u16) -> Self {
+ self.padding = padding;
self
}
- /// Sets the border radius of the [`Button`].
+ /// Sets the style 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<Box<dyn StyleSheet>>) -> Self {
+ self.style = style.into();
self
}
@@ -126,18 +137,20 @@ where
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
- style_sheet: &mut style::Sheet<'b>,
+ style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
- let width = style::length(self.width);
+ // TODO: State-based styling
+ let style = self.style.active();
+
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 {
+ let background = match style.background {
None => String::from("none"),
Some(background) => match background {
- Background::Color(color) => style::color(color),
+ Background::Color(color) => css::color(color),
},
};
@@ -150,11 +163,12 @@ where
"style",
bumpalo::format!(
in bump,
- "background: {}; border-radius: {}px; width:{}; min-width: {}px",
+ "background: {}; border-radius: {}px; width:{}; min-width: {}; color: {}",
background,
- self.border_radius,
- width,
- self.min_width
+ style.border_radius,
+ css::length(self.width),
+ css::min_length(self.min_width),
+ css::color(style.text_color)
)
.into_bump_str(),
)
@@ -168,8 +182,6 @@ where
});
}
- // TODO: Complete styling
-
node.finish()
}
}
diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs
index 1e864875..0657ccfb 100644
--- a/web/src/widget/checkbox.rs
+++ b/web/src/widget/checkbox.rs
@@ -1,4 +1,7 @@
-use crate::{style, Bus, Color, Element, Widget};
+//! Show toggle controls using checkboxes.
+use crate::{css, Bus, Css, Element, Length, Widget};
+
+pub use iced_style::checkbox::{Style, StyleSheet};
use dodrio::bumpalo;
use std::rc::Rc;
@@ -25,7 +28,8 @@ pub struct Checkbox<Message> {
is_checked: bool,
on_toggle: Rc<dyn Fn(bool) -> Message>,
label: String,
- label_color: Option<Color>,
+ width: Length,
+ style: Box<dyn StyleSheet>,
}
impl<Message> Checkbox<Message> {
@@ -47,15 +51,24 @@ impl<Message> Checkbox<Message> {
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 width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the style of the [`Checkbox`].
///
/// [`Checkbox`]: struct.Checkbox.html
- pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self {
- self.label_color = Some(color.into());
+ pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self {
+ self.style = style.into();
self
}
}
@@ -68,7 +81,7 @@ where
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
- _style_sheet: &mut style::Sheet<'b>,
+ style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
@@ -78,9 +91,23 @@ where
let on_toggle = self.on_toggle.clone();
let is_checked = self.is_checked;
- // TODO: Complete 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)
@@ -91,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()
}
diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs
index 9aa988ff..6454ffba 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;
@@ -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,
@@ -112,7 +112,7 @@ impl<'a, Message> Widget<Message> for Column<'a, Message> {
&self,
bump: &'b bumpalo::Bump,
publish: &Bus<Message>,
- style_sheet: &mut style::Sheet<'b>,
+ style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
@@ -122,18 +122,13 @@ impl<'a, Message> Widget<Message> 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));
-
- let width = style::length(self.width);
- let height = style::length(self.height);
-
- let align_items = style::align(self.align_items);
+ style_sheet.insert(bump, css::Rule::Padding(self.padding));
// TODO: Complete styling
div(bump)
@@ -144,12 +139,12 @@ impl<'a, Message> Widget<Message> 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/container.rs b/web/src/widget/container.rs
index bdc88979..8e4318f9 100644
--- a/web/src/widget/container.rs
+++ b/web/src/widget/container.rs
@@ -1,4 +1,7 @@
-use crate::{bumpalo, style, Align, Bus, Element, Length, Style, Widget};
+//! 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.
///
@@ -11,6 +14,7 @@ pub struct Container<'a, Message> {
max_height: u32,
horizontal_alignment: Align,
vertical_alignment: Align,
+ style_sheet: Box<dyn StyleSheet>,
content: Element<'a, Message>,
}
@@ -31,6 +35,7 @@ impl<'a, Message> Container<'a, Message> {
max_height: u32::MAX,
horizontal_alignment: Align::Start,
vertical_alignment: Align::Start,
+ style_sheet: Default::default(),
content: content.into(),
}
}
@@ -84,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<Box<dyn StyleSheet>>) -> Self {
+ self.style_sheet = style.into();
+ self
+ }
}
impl<'a, Message> Widget<Message> for Container<'a, Message>
@@ -94,17 +107,13 @@ where
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
- 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 width = style::length(self.width);
- let height = style::length(self.height);
+ let column_class = style_sheet.insert(bump, css::Rule::Column);
- let align_items = style::align(self.horizontal_alignment);
- let justify_content = style::align(self.vertical_alignment);
+ let style = self.style_sheet.style();
let node = div(bump)
.attr(
@@ -115,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(),
)
diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs
index 413b663e..029ab352 100644
--- a/web/src/widget/image.rs
+++ b/web/src/widget/image.rs
@@ -1,6 +1,12 @@
-use crate::{style, Bus, 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<T: Into<String>>(path: T) -> Self {
+ pub fn new<T: Into<Handle>>(handle: T) -> Self {
Image {
- path: path.into(),
+ handle: handle.into(),
width: Length::Shrink,
height: Length::Shrink,
}
@@ -57,11 +63,13 @@ impl<Message> Widget<Message> for Image {
&self,
bump: &'b bumpalo::Bump,
_bus: &Bus<Message>,
- _style_sheet: &mut style::Sheet<'b>,
+ _style_sheet: &mut Css<'b>,
) -> 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,74 @@ impl<'a, Message> From<Image> for Element<'a, Message> {
Element::new(image)
}
}
+
+/// An [`Image`] handle.
+///
+/// [`Image`]: struct.Image.html
+#[derive(Debug, Clone)]
+pub struct Handle {
+ id: u64,
+ data: Arc<Data>,
+}
+
+impl Handle {
+ /// Creates an image [`Handle`] pointing to the image of the given path.
+ ///
+ /// [`Handle`]: struct.Handle.html
+ pub fn from_path<T: Into<PathBuf>>(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<String> 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)
+ }
+}
+
+/// 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),
+ }
+ }
+}
diff --git a/web/src/widget/progress_bar.rs b/web/src/widget/progress_bar.rs
new file mode 100644
index 00000000..856203c0
--- /dev/null
+++ b/web/src/widget/progress_bar.rs
@@ -0,0 +1,124 @@
+//! 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_web::ProgressBar;
+///
+/// let value = 50.0;
+///
+/// ProgressBar::new(0.0..=100.0, value);
+/// ```
+///
+/// ![Progress bar](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
+#[allow(missing_debug_implementations)]
+pub struct ProgressBar {
+ range: RangeInclusive<f32>,
+ value: f32,
+ width: Length,
+ height: Option<Length>,
+ style: Box<dyn StyleSheet>,
+}
+
+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<f32>, 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<Box<dyn StyleSheet>>) -> Self {
+ self.style = style.into();
+ self
+ }
+}
+
+impl<Message> Widget<Message> for ProgressBar {
+ fn node<'b>(
+ &self,
+ bump: &'b bumpalo::Bump,
+ _bus: &Bus<Message>,
+ _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<ProgressBar> for Element<'a, Message>
+where
+ Message: 'static,
+{
+ fn from(container: ProgressBar) -> Element<'a, Message> {
+ Element::new(container)
+ }
+}
diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs
index 6dd0ad45..e00e26db 100644
--- a/web/src/widget/radio.rs
+++ b/web/src/widget/radio.rs
@@ -1,4 +1,7 @@
-use crate::{style, Bus, Color, Element, Widget};
+//! Create choices using radio buttons.
+use crate::{Bus, Css, Element, Widget};
+
+pub use iced_style::radio::{Style, StyleSheet};
use dodrio::bumpalo;
@@ -32,7 +35,7 @@ pub struct Radio<Message> {
is_selected: bool,
on_click: Message,
label: String,
- label_color: Option<Color>,
+ style: Box<dyn StyleSheet>,
}
impl<Message> Radio<Message> {
@@ -55,15 +58,15 @@ impl<Message> Radio<Message> {
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<C: Into<Color>>(mut self, color: C) -> Self {
- self.label_color = Some(color.into());
+ pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self {
+ self.style = style.into();
self
}
}
@@ -76,7 +79,7 @@ where
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
- _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..02035113 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;
@@ -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,
@@ -113,7 +113,7 @@ impl<'a, Message> Widget<Message> for Row<'a, Message> {
&self,
bump: &'b bumpalo::Bump,
publish: &Bus<Message>,
- style_sheet: &mut style::Sheet<'b>,
+ style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
@@ -123,18 +123,13 @@ impl<'a, Message> Widget<Message> 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));
-
- let width = style::length(self.width);
- let height = style::length(self.height);
-
- let justify_content = style::align(self.align_items);
+ style_sheet.insert(bump, css::Rule::Padding(self.padding));
// TODO: Complete styling
div(bump)
@@ -145,12 +140,12 @@ impl<'a, Message> Widget<Message> 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)
diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs
index f146e007..07b38aad 100644
--- a/web/src/widget/scrollable.rs
+++ b/web/src/widget/scrollable.rs
@@ -1,5 +1,7 @@
//! 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};
+
+pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet};
/// A widget that can vertically display an infinite amount of content with a
/// scrollbar.
@@ -9,6 +11,7 @@ pub struct Scrollable<'a, Message> {
height: Length,
max_height: u32,
content: Column<'a, Message>,
+ style: Box<dyn StyleSheet>,
}
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<Box<dyn StyleSheet>>) -> Self {
+ self.style = style.into();
+ self
+ }
+
/// Adds an element to the [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
@@ -105,12 +117,14 @@ where
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
- 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);
+
+ // TODO: Scrollbar styling
let node = div(bump)
.attr(
@@ -126,8 +140,6 @@ where
)
.children(vec![self.content.node(bump, bus, style_sheet)]);
- // TODO: Complete styling
-
node.finish()
}
}
diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs
index 25c57933..5aa6439e 100644
--- a/web/src/widget/slider.rs
+++ b/web/src/widget/slider.rs
@@ -4,7 +4,9 @@
//!
//! [`Slider`]: struct.Slider.html
//! [`State`]: struct.State.html
-use crate::{style, Bus, Element, Length, Widget};
+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<Box<dyn Fn(f32) -> Message>>,
width: Length,
+ style: Box<dyn StyleSheet>,
}
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<Box<dyn StyleSheet>>) -> Self {
+ self.style = style.into();
+ self
+ }
}
impl<'a, Message> Widget<Message> for Slider<'a, Message>
@@ -88,7 +100,7 @@ where
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
- _style_sheet: &mut style::Sheet<'b>,
+ _style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
use wasm_bindgen::JsCast;
@@ -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")
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<Message> for Space {
&self,
bump: &'b bumpalo::Bump,
_publish: &Bus<Message>,
- _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..3ec565a8 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,18 @@ impl<'a, Message> Widget<Message> for Text {
&self,
bump: &'b bumpalo::Bump,
_publish: &Bus<Message>,
- _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 = self
+ .color
+ .map(css::color)
+ .unwrap_or(String::from("inherit"));
- 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",
@@ -130,12 +133,16 @@ impl<'a, Message> Widget<Message> 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
diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs
index 078e05b2..3fa458bd 100644
--- a/web/src/widget/text_input.rs
+++ b/web/src/widget/text_input.rs
@@ -4,8 +4,11 @@
//!
//! [`TextInput`]: struct.TextInput.html
//! [`State`]: struct.State.html
-use crate::{bumpalo, style, Bus, Element, Length, Style, Widget};
-use std::rc::Rc;
+use crate::{bumpalo, css, Bus, Css, Element, Length, Widget};
+
+pub use iced_style::text_input::{Style, StyleSheet};
+
+use std::{rc::Rc, u32};
/// A field that can be filled with text.
///
@@ -34,11 +37,12 @@ pub struct TextInput<'a, Message> {
value: String,
is_secure: bool,
width: Length,
- max_width: Length,
+ max_width: u32,
padding: u16,
size: Option<u16>,
on_change: Rc<Box<dyn Fn(String) -> Message>>,
on_submit: Option<Message>,
+ style_sheet: Box<dyn StyleSheet>,
}
impl<'a, Message> TextInput<'a, Message> {
@@ -67,11 +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_sheet: Default::default(),
}
}
@@ -94,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
}
@@ -123,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<Box<dyn StyleSheet>>) -> Self {
+ self.style_sheet = style.into();
+ self
+ }
}
impl<'a, Message> Widget<Message> for TextInput<'a, Message>
@@ -133,18 +146,19 @@ where
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
- 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 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();
+ 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)
.attr(
@@ -155,10 +169,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(),
)
@@ -183,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::<web_sys::KeyboardEvent>();
+
+ match event.key_code() {
+ 13 => { submit_event_bus.publish(on_submit); }
+ _ => {}
+ }
+ }
})
.finish()
}
@@ -211,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()
+ }
}