diff options
author | 2020-02-06 10:21:52 -0600 | |
---|---|---|
committer | 2020-02-06 10:21:52 -0600 | |
commit | 97c308076ff93d09eb874f8e954aae4c7c5deaf7 (patch) | |
tree | c3a4ec76931c8153c932c364fa393e25d39d74f0 /web/src/widget | |
parent | 7b892eb3e11596925a2993bcc4175ac09ff3768a (diff) | |
parent | 36e617ae70cc7a86ce998cbd61f6aa702bb42933 (diff) | |
download | iced-97c308076ff93d09eb874f8e954aae4c7c5deaf7.tar.gz iced-97c308076ff93d09eb874f8e954aae4c7c5deaf7.tar.bz2 iced-97c308076ff93d09eb874f8e954aae4c7c5deaf7.zip |
Merge pull request #180 from hecrj/feature/web-styling
Custom styling for `iced_web`
Diffstat (limited to 'web/src/widget')
-rw-r--r-- | web/src/widget/button.rs | 66 | ||||
-rw-r--r-- | web/src/widget/checkbox.rs | 46 | ||||
-rw-r--r-- | web/src/widget/column.rs | 29 | ||||
-rw-r--r-- | web/src/widget/container.rs | 42 | ||||
-rw-r--r-- | web/src/widget/image.rs | 91 | ||||
-rw-r--r-- | web/src/widget/progress_bar.rs | 124 | ||||
-rw-r--r-- | web/src/widget/radio.rs | 17 | ||||
-rw-r--r-- | web/src/widget/row.rs | 29 | ||||
-rw-r--r-- | web/src/widget/scrollable.rs | 24 | ||||
-rw-r--r-- | web/src/widget/slider.rs | 18 | ||||
-rw-r--r-- | web/src/widget/space.rs | 8 | ||||
-rw-r--r-- | web/src/widget/text.rs | 21 | ||||
-rw-r--r-- | web/src/widget/text_input.rs | 67 |
13 files changed, 450 insertions, 132 deletions
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); +/// ``` +/// +///  +#[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() + } } |