summaryrefslogtreecommitdiffstats
path: root/web/src/widget
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/widget')
-rw-r--r--web/src/widget/button.rs70
-rw-r--r--web/src/widget/checkbox.rs63
-rw-r--r--web/src/widget/column.rs23
-rw-r--r--web/src/widget/container.rs44
-rw-r--r--web/src/widget/image.rs93
-rw-r--r--web/src/widget/progress_bar.rs124
-rw-r--r--web/src/widget/radio.rs23
-rw-r--r--web/src/widget/row.rs23
-rw-r--r--web/src/widget/scrollable.rs26
-rw-r--r--web/src/widget/slider.rs27
-rw-r--r--web/src/widget/space.rs69
-rw-r--r--web/src/widget/text.rs24
-rw-r--r--web/src/widget/text_input.rs80
13 files changed, 553 insertions, 136 deletions
diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs
index 889c0ab1..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(mut self, background: Background) -> Self {
- self.background = Some(background);
+ 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,17 +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::*;
+ // 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),
},
};
@@ -149,10 +163,12 @@ where
"style",
bumpalo::format!(
in bump,
- "background: {}; border-radius: {}px; min-width: {}px",
+ "background: {}; border-radius: {}px; width:{}; min-width: {}; color: {}",
background,
- self.border_radius,
- self.min_width
+ style.border_radius,
+ css::length(self.width),
+ css::min_length(self.min_width),
+ css::color(style.text_color)
)
.into_bump_str(),
)
@@ -161,15 +177,11 @@ where
if let Some(on_press) = self.on_press.clone() {
let event_bus = bus.clone();
- node = node.on("click", move |root, vdom, _event| {
- event_bus.publish(on_press.clone(), root);
-
- vdom.schedule_render();
+ node = node.on("click", move |_root, _vdom, _event| {
+ event_bus.publish(on_press.clone());
});
}
- // TODO: Complete styling
-
node.finish()
}
}
diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs
index b81a0d52..0657ccfb 100644
--- a/web/src/widget/checkbox.rs
+++ b/web/src/widget/checkbox.rs
@@ -1,6 +1,10 @@
-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;
/// A box that can be checked.
///
@@ -22,9 +26,10 @@ use dodrio::bumpalo;
#[allow(missing_debug_implementations)]
pub struct Checkbox<Message> {
is_checked: bool,
- on_toggle: Box<dyn Fn(bool) -> Message>,
+ on_toggle: Rc<dyn Fn(bool) -> Message>,
label: String,
- label_color: Option<Color>,
+ width: Length,
+ style: Box<dyn StyleSheet>,
}
impl<Message> Checkbox<Message> {
@@ -44,51 +49,77 @@ impl<Message> Checkbox<Message> {
{
Checkbox {
is_checked,
- on_toggle: Box::new(f),
+ 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
}
}
impl<Message> Widget<Message> for Checkbox<Message>
where
- Message: 'static + Clone,
+ Message: 'static,
{
fn node<'b>(
&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 checkbox_label = bumpalo::format!(in bump, "{}", self.label);
let event_bus = bus.clone();
- let msg = (self.on_toggle)(!self.is_checked);
+ let on_toggle = self.on_toggle.clone();
+ let is_checked = self.is_checked;
+
+ let row_class = style_sheet.insert(bump, css::Rule::Row);
+
+ let spacing_class = style_sheet.insert(bump, css::Rule::Spacing(5));
- // TODO: Complete styling
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)
- .on("click", move |root, vdom, _event| {
- event_bus.publish(msg.clone(), root);
+ .on("click", move |_root, vdom, _event| {
+ let msg = on_toggle(!is_checked);
+ event_bus.publish(msg);
vdom.schedule_render();
})
.finish(),
- text(checkbox_label.into_bump_str()),
+ span(bump).children(vec![
+ text(checkbox_label.into_bump_str())]).finish(),
])
.finish()
}
@@ -96,7 +127,7 @@ where
impl<'a, Message> From<Checkbox<Message>> for Element<'a, Message>
where
- Message: 'static + Clone,
+ Message: 'static,
{
fn from(checkbox: Checkbox<Message>) -> Element<'a, Message> {
Element::new(checkbox)
diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs
index cc850f5f..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;
@@ -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,16 +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);
+ style_sheet.insert(bump, css::Rule::Padding(self.padding));
// TODO: Complete styling
div(bump)
@@ -142,10 +139,12 @@ impl<'a, Message> Widget<Message> for Column<'a, Message> {
)
.attr("style", bumpalo::format!(
in bump,
- "width: {}; height: {}; max-width: {}px",
- width,
- height,
- self.max_width
+ "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 25e4ebf8..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 column_class = style_sheet.insert(bump, css::Rule::Column);
- let width = style::length(self.width);
- let height = style::length(self.height);
-
- 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(),
)
@@ -134,7 +148,7 @@ where
impl<'a, Message> From<Container<'a, Message>> for Element<'a, Message>
where
- Message: 'static + Clone,
+ Message: 'static,
{
fn from(container: Container<'a, Message>) -> Element<'a, Message> {
Element::new(container)
diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs
index ed8b7ecf..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,17 +63,19 @@ 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());
match self.width {
Length::Shrink => {}
- Length::Fill => {
+ Length::Fill | Length::FillPortion(_) => {
image = image.attr("width", "100%");
}
Length::Units(px) => {
@@ -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 4e7d02b8..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::*;
@@ -93,10 +96,8 @@ where
.attr("type", "radio")
.attr("style", "margin-right: 10px")
.bool_attr("checked", self.is_selected)
- .on("click", move |root, vdom, _event| {
- event_bus.publish(on_click.clone(), root);
-
- vdom.schedule_render();
+ .on("click", move |_root, _vdom, _event| {
+ event_bus.publish(on_click.clone());
})
.finish(),
text(radio_label.into_bump_str()),
diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs
index e47478be..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;
@@ -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,16 +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);
+ style_sheet.insert(bump, css::Rule::Padding(self.padding));
// TODO: Complete styling
div(bump)
@@ -143,10 +140,12 @@ impl<'a, Message> Widget<Message> for Row<'a, Message> {
)
.attr("style", bumpalo::format!(
in bump,
- "width: {}; height: {}; max-width: {}px",
- width,
- height,
- self.max_width
+ "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 710bb70a..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,15 +140,13 @@ where
)
.children(vec![self.content.node(bump, bus, style_sheet)]);
- // TODO: Complete styling
-
node.finish()
}
}
impl<'a, Message> From<Scrollable<'a, Message>> for Element<'a, Message>
where
- Message: 'static + Clone,
+ Message: 'static,
{
fn from(scrollable: Scrollable<'a, Message>) -> Element<'a, Message> {
Element::new(scrollable)
diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs
index 5b203e07..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,17 +82,25 @@ 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>
where
- Message: 'static + Clone,
+ Message: 'static,
{
fn node<'b>(
&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")
@@ -111,7 +123,7 @@ where
.attr("max", max.into_bump_str())
.attr("value", value.into_bump_str())
.attr("style", "width: 100%")
- .on("input", move |root, vdom, event| {
+ .on("input", move |_root, _vdom, event| {
let slider = match event.target().and_then(|t| {
t.dyn_into::<web_sys::HtmlInputElement>().ok()
}) {
@@ -120,8 +132,7 @@ where
};
if let Ok(value) = slider.value().parse::<f32>() {
- event_bus.publish(on_change(value), root);
- vdom.schedule_render();
+ event_bus.publish(on_change(value));
}
})
.finish()
@@ -130,7 +141,7 @@ where
impl<'a, Message> From<Slider<'a, Message>> for Element<'a, Message>
where
- Message: 'static + Clone,
+ Message: 'static,
{
fn from(slider: Slider<'a, Message>) -> Element<'a, Message> {
Element::new(slider)
diff --git a/web/src/widget/space.rs b/web/src/widget/space.rs
new file mode 100644
index 00000000..4ce52595
--- /dev/null
+++ b/web/src/widget/space.rs
@@ -0,0 +1,69 @@
+use crate::{css, Bus, Css, Element, Length, Widget};
+use dodrio::bumpalo;
+
+/// An amount of empty space.
+///
+/// It can be useful if you want to fill some space with nothing.
+#[derive(Debug)]
+pub struct Space {
+ width: Length,
+ height: Length,
+}
+
+impl Space {
+ /// Creates an amount of empty [`Space`] with the given width and height.
+ ///
+ /// [`Space`]: struct.Space.html
+ pub fn new(width: Length, height: Length) -> Self {
+ Space { width, height }
+ }
+
+ /// Creates an amount of horizontal [`Space`].
+ ///
+ /// [`Space`]: struct.Space.html
+ pub fn with_width(width: Length) -> Self {
+ Space {
+ width,
+ height: Length::Shrink,
+ }
+ }
+
+ /// Creates an amount of vertical [`Space`].
+ ///
+ /// [`Space`]: struct.Space.html
+ pub fn with_height(height: Length) -> Self {
+ Space {
+ width: Length::Shrink,
+ height,
+ }
+ }
+}
+
+impl<'a, Message> Widget<Message> for Space {
+ fn node<'b>(
+ &self,
+ bump: &'b bumpalo::Bump,
+ _publish: &Bus<Message>,
+ _css: &mut Css<'b>,
+ ) -> dodrio::Node<'b> {
+ use dodrio::builder::*;
+
+ let width = css::length(self.width);
+ let height = css::length(self.height);
+
+ let style = bumpalo::format!(
+ in bump,
+ "width: {}; height: {};",
+ width,
+ height
+ );
+
+ div(bump).attr("style", style.into_bump_str()).finish()
+ }
+}
+
+impl<'a, Message> From<Space> for Element<'a, Message> {
+ fn from(space: Space) -> Element<'a, Message> {
+ Element::new(space)
+ }
+}
diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs
index 6194a12e..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;
@@ -36,7 +36,7 @@ impl Text {
size: None,
color: None,
font: Font::Default,
- width: Length::Fill,
+ width: Length::Shrink,
height: Length::Shrink,
horizontal_alignment: HorizontalAlignment::Left,
vertical_alignment: VerticalAlignment::Top,
@@ -112,12 +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 = css::length(self.width);
+ let height = css::length(self.height);
let text_align = match self.horizontal_alignment {
HorizontalAlignment::Left => "left",
@@ -127,10 +133,16 @@ impl<'a, Message> Widget<Message> for Text {
let style = bumpalo::format!(
in bump,
- "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 d6357512..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.
///
@@ -32,12 +35,14 @@ pub struct TextInput<'a, Message> {
_state: &'a mut State,
placeholder: String,
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> {
@@ -64,15 +69,25 @@ impl<'a, Message> TextInput<'a, Message> {
_state: state,
placeholder: String::from(placeholder),
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(),
}
}
+ /// Converts the [`TextInput`] into a secure password input.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn password(mut self) -> Self {
+ self.is_secure = true;
+ self
+ }
+
/// Sets the width of the [`TextInput`].
///
/// [`TextInput`]: struct.TextInput.html
@@ -84,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
}
@@ -113,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>
@@ -123,16 +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 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(
@@ -143,8 +169,15 @@ where
"style",
bumpalo::format!(
in bump,
- "font-size: {}px",
- 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(),
)
@@ -157,7 +190,11 @@ where
"value",
bumpalo::format!(in bump, "{}", self.value).into_bump_str(),
)
- .on("input", move |root, vdom, event| {
+ .attr(
+ "type",
+ bumpalo::format!(in bump, "{}", if self.is_secure { "password" } else { "text" }).into_bump_str(),
+ )
+ .on("input", move |_root, _vdom, event| {
let text_input = match event.target().and_then(|t| {
t.dyn_into::<web_sys::HtmlInputElement>().ok()
}) {
@@ -165,8 +202,17 @@ where
Some(text_input) => text_input,
};
- event_bus.publish(on_change(text_input.value()), root);
- vdom.schedule_render();
+ 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()
}
@@ -194,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()
+ }
}