summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-07-08 11:44:40 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-07-08 11:44:40 +0200
commitf3dfaa2c43bad16fc91660b2b73cb9173549e7ec (patch)
tree353365f4dd1e3136bc651ac8c1572f62fff1304b /web
parent072ec69d53d2708d8fd1693151bcec7305efccf8 (diff)
parent5c4f5ae5ecb36703a95cafb2cd58692529c9466d (diff)
downloadiced-f3dfaa2c43bad16fc91660b2b73cb9173549e7ec.tar.gz
iced-f3dfaa2c43bad16fc91660b2b73cb9173549e7ec.tar.bz2
iced-f3dfaa2c43bad16fc91660b2b73cb9173549e7ec.zip
Merge branch 'master' into feature/pane-grid-titlebar
Diffstat (limited to '')
-rw-r--r--web/Cargo.toml5
-rw-r--r--web/src/lib.rs19
-rw-r--r--web/src/widget/button.rs14
-rw-r--r--web/src/widget/checkbox.rs29
-rw-r--r--web/src/widget/image.rs27
-rw-r--r--web/src/widget/radio.rs46
-rw-r--r--web/src/widget/slider.rs70
-rw-r--r--web/src/widget/text.rs10
-rw-r--r--web/src/widget/text_input.rs54
9 files changed, 200 insertions, 74 deletions
diff --git a/web/Cargo.toml b/web/Cargo.toml
index 12d3865e..e03d2b63 100644
--- a/web/Cargo.toml
+++ b/web/Cargo.toml
@@ -15,10 +15,11 @@ categories = ["web-programming"]
maintenance = { status = "actively-developed" }
[dependencies]
-dodrio = "0.1.0"
-wasm-bindgen = "0.2.51"
+dodrio = "0.2"
+wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
url = "2.0"
+num-traits = "0.2"
[dependencies.iced_core]
version = "0.2"
diff --git a/web/src/lib.rs b/web/src/lib.rs
index 53b54b7e..b7970c56 100644
--- a/web/src/lib.rs
+++ b/web/src/lib.rs
@@ -238,28 +238,25 @@ struct Instance<A: Application> {
bus: Bus<A::Message>,
}
-impl<A> dodrio::Render for Instance<A>
+impl<'a, A> dodrio::Render<'a> for Instance<A>
where
A: Application,
{
- fn render<'a, 'bump>(
- &'a self,
- bump: &'bump bumpalo::Bump,
- ) -> dodrio::Node<'bump>
- where
- 'a: 'bump,
- {
+ fn render(
+ &self,
+ context: &mut dodrio::RenderContext<'a>,
+ ) -> dodrio::Node<'a> {
use dodrio::builder::*;
let mut ui = self.application.borrow_mut();
let element = ui.view();
let mut css = Css::new();
- let node = element.widget.node(bump, &self.bus, &mut css);
+ let node = element.widget.node(context.bump, &self.bus, &mut css);
- div(bump)
+ div(context.bump)
.attr("style", "width: 100%; height: 100%")
- .children(vec![css.node(bump), node])
+ .children(vec![css.node(context.bump), node])
.finish()
}
}
diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs
index 3a5afe60..a4bcc33d 100644
--- a/web/src/widget/button.rs
+++ b/web/src/widget/button.rs
@@ -154,16 +154,20 @@ where
},
};
+ let class = {
+ use dodrio::bumpalo::collections::String;
+
+ String::from_str_in(&padding_class, bump).into_bump_str()
+ };
+
let mut node = button(bump)
- .attr(
- "class",
- bumpalo::format!(in bump, "{}", padding_class).into_bump_str(),
- )
+ .attr("class", class)
.attr(
"style",
bumpalo::format!(
in bump,
- "background: {}; border-radius: {}px; width:{}; min-width: {}; color: {}",
+ "background: {}; border-radius: {}px; width:{}; \
+ min-width: {}; color: {}",
background,
style.border_radius,
css::length(self.width),
diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs
index 5ebc26c8..21801e39 100644
--- a/web/src/widget/checkbox.rs
+++ b/web/src/widget/checkbox.rs
@@ -28,6 +28,7 @@ pub struct Checkbox<Message> {
is_checked: bool,
on_toggle: Rc<dyn Fn(bool) -> Message>,
label: String,
+ id: Option<String>,
width: Length,
style: Box<dyn StyleSheet>,
}
@@ -51,6 +52,7 @@ impl<Message> Checkbox<Message> {
is_checked,
on_toggle: Rc::new(f),
label: label.into(),
+ id: None,
width: Length::Shrink,
style: Default::default(),
}
@@ -71,6 +73,14 @@ impl<Message> Checkbox<Message> {
self.style = style.into();
self
}
+
+ /// Sets the id of the [`Checkbox`].
+ ///
+ /// [`Checkbox`]: struct.Checkbox.html
+ pub fn id(mut self, id: impl Into<String>) -> Self {
+ self.id = Some(id.into());
+ self
+ }
}
impl<Message> Widget<Message> for Checkbox<Message>
@@ -84,8 +94,10 @@ where
style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
+ use dodrio::bumpalo::collections::String;
- let checkbox_label = bumpalo::format!(in bump, "{}", self.label);
+ let checkbox_label =
+ String::from_str_in(&self.label, bump).into_bump_str();
let event_bus = bus.clone();
let on_toggle = self.on_toggle.clone();
@@ -95,7 +107,15 @@ where
let spacing_class = style_sheet.insert(bump, css::Rule::Spacing(5));
- label(bump)
+ let (label, input) = if let Some(id) = &self.id {
+ let id = String::from_str_in(id, bump).into_bump_str();
+
+ (label(bump).attr("for", id), input(bump).attr("id", id))
+ } else {
+ (label(bump), input(bump))
+ };
+
+ label
.attr(
"class",
bumpalo::format!(in bump, "{} {}", row_class, spacing_class)
@@ -108,7 +128,7 @@ where
)
.children(vec![
// TODO: Checkbox styling
- input(bump)
+ input
.attr("type", "checkbox")
.bool_attr("checked", self.is_checked)
.on("click", move |_root, vdom, _event| {
@@ -118,8 +138,7 @@ where
vdom.schedule_render();
})
.finish(),
- span(bump).children(vec![
- text(checkbox_label.into_bump_str())]).finish(),
+ text(checkbox_label),
])
.finish()
}
diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs
index 029ab352..a595c29a 100644
--- a/web/src/widget/image.rs
+++ b/web/src/widget/image.rs
@@ -22,6 +22,9 @@ pub struct Image {
/// The image path
pub handle: Handle,
+ /// The alt text of the image
+ pub alt: String,
+
/// The width of the image
pub width: Length,
@@ -36,6 +39,7 @@ impl Image {
pub fn new<T: Into<Handle>>(handle: T) -> Self {
Image {
handle: handle.into(),
+ alt: Default::default(),
width: Length::Shrink,
height: Length::Shrink,
}
@@ -56,6 +60,14 @@ impl Image {
self.height = height;
self
}
+
+ /// Sets the alt text of the [`Image`].
+ ///
+ /// [`Image`]: struct.Image.html
+ pub fn alt(mut self, alt: impl Into<String>) -> Self {
+ self.alt = alt.into();
+ self
+ }
}
impl<Message> Widget<Message> for Image {
@@ -66,12 +78,19 @@ impl<Message> Widget<Message> for Image {
_style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
+ use dodrio::bumpalo::collections::String;
+
+ let src = String::from_str_in(
+ match self.handle.data.as_ref() {
+ Data::Path(path) => path.to_str().unwrap_or(""),
+ },
+ bump,
+ )
+ .into_bump_str();
- let src = bumpalo::format!(in bump, "{}", match self.handle.data.as_ref() {
- Data::Path(path) => path.to_str().unwrap_or("")
- });
+ let alt = String::from_str_in(&self.alt, bump).into_bump_str();
- let mut image = img(bump).attr("src", src.into_bump_str());
+ let mut image = img(bump).attr("src", src).attr("alt", alt);
match self.width {
Length::Shrink => {}
diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs
index 520b24cd..c9d0a00e 100644
--- a/web/src/widget/radio.rs
+++ b/web/src/widget/radio.rs
@@ -35,6 +35,8 @@ pub struct Radio<Message> {
is_selected: bool,
on_click: Message,
label: String,
+ id: Option<String>,
+ name: Option<String>,
style: Box<dyn StyleSheet>,
}
@@ -63,6 +65,8 @@ impl<Message> Radio<Message> {
is_selected: Some(value) == selected,
on_click: f(value),
label: label.into(),
+ id: None,
+ name: None,
style: Default::default(),
}
}
@@ -74,6 +78,22 @@ impl<Message> Radio<Message> {
self.style = style.into();
self
}
+
+ /// Sets the name attribute of the [`Radio`] button.
+ ///
+ /// [`Radio`]: struct.Radio.html
+ pub fn name(mut self, name: impl Into<String>) -> Self {
+ self.name = Some(name.into());
+ self
+ }
+
+ /// Sets the id of the [`Radio`] button.
+ ///
+ /// [`Radio`]: struct.Radio.html
+ pub fn id(mut self, id: impl Into<String>) -> Self {
+ self.id = Some(id.into());
+ self
+ }
}
impl<Message> Widget<Message> for Radio<Message>
@@ -87,17 +107,35 @@ where
_style_sheet: &mut Css<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
+ use dodrio::bumpalo::collections::String;
- let radio_label = bumpalo::format!(in bump, "{}", self.label);
+ let radio_label =
+ String::from_str_in(&self.label, bump).into_bump_str();
let event_bus = bus.clone();
let on_click = self.on_click.clone();
+ let (label, input) = if let Some(id) = &self.id {
+ let id = String::from_str_in(id, bump).into_bump_str();
+
+ (label(bump).attr("for", id), input(bump).attr("id", id))
+ } else {
+ (label(bump), input(bump))
+ };
+
+ let input = if let Some(name) = &self.name {
+ let name = String::from_str_in(name, bump).into_bump_str();
+
+ dodrio::builder::input(bump).attr("name", name)
+ } else {
+ input
+ };
+
// TODO: Complete styling
- label(bump)
+ label
.attr("style", "display: block; font-size: 20px")
.children(vec![
- input(bump)
+ input
.attr("type", "radio")
.attr("style", "margin-right: 10px")
.bool_attr("checked", self.is_selected)
@@ -105,7 +143,7 @@ where
event_bus.publish(on_click.clone());
})
.finish(),
- text(radio_label.into_bump_str()),
+ text(radio_label),
])
.finish()
}
diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs
index 5aa6439e..a0d9df00 100644
--- a/web/src/widget/slider.rs
+++ b/web/src/widget/slider.rs
@@ -16,6 +16,9 @@ use std::{ops::RangeInclusive, rc::Rc};
///
/// A [`Slider`] will try to fill the horizontal space of its container.
///
+/// The [`Slider`] range of numeric values is generic and its step size defaults
+/// to 1 unit.
+///
/// [`Slider`]: struct.Slider.html
///
/// # Example
@@ -34,16 +37,20 @@ use std::{ops::RangeInclusive, rc::Rc};
///
/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct Slider<'a, Message> {
+pub struct Slider<'a, T, Message> {
_state: &'a mut State,
- range: RangeInclusive<f32>,
- value: f32,
- on_change: Rc<Box<dyn Fn(f32) -> Message>>,
+ range: RangeInclusive<T>,
+ step: T,
+ value: T,
+ on_change: Rc<Box<dyn Fn(T) -> Message>>,
width: Length,
style: Box<dyn StyleSheet>,
}
-impl<'a, Message> Slider<'a, Message> {
+impl<'a, T, Message> Slider<'a, T, Message>
+where
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+{
/// Creates a new [`Slider`].
///
/// It expects:
@@ -58,17 +65,30 @@ impl<'a, Message> Slider<'a, Message> {
/// [`State`]: struct.State.html
pub fn new<F>(
state: &'a mut State,
- range: RangeInclusive<f32>,
- value: f32,
+ range: RangeInclusive<T>,
+ value: T,
on_change: F,
) -> Self
where
- F: 'static + Fn(f32) -> Message,
+ F: 'static + Fn(T) -> Message,
{
+ let value = if value >= *range.start() {
+ value
+ } else {
+ *range.start()
+ };
+
+ let value = if value <= *range.end() {
+ value
+ } else {
+ *range.end()
+ };
+
Slider {
_state: state,
- value: value.max(*range.start()).min(*range.end()),
+ value,
range,
+ step: T::from(1),
on_change: Rc::new(Box::new(on_change)),
width: Length::Fill,
style: Default::default(),
@@ -90,10 +110,19 @@ impl<'a, Message> Slider<'a, Message> {
self.style = style.into();
self
}
+
+ /// Sets the step size of the [`Slider`].
+ ///
+ /// [`Slider`]: struct.Slider.html
+ pub fn step(mut self, step: T) -> Self {
+ self.step = step;
+ self
+ }
}
-impl<'a, Message> Widget<Message> for Slider<'a, Message>
+impl<'a, T, Message> Widget<Message> for Slider<'a, T, Message>
where
+ T: 'static + Copy + Into<f64> + num_traits::FromPrimitive,
Message: 'static,
{
fn node<'b>(
@@ -107,18 +136,18 @@ where
let (start, end) = self.range.clone().into_inner();
- let min = bumpalo::format!(in bump, "{}", start);
- let max = bumpalo::format!(in bump, "{}", end);
- let value = bumpalo::format!(in bump, "{}", self.value);
+ let min = bumpalo::format!(in bump, "{}", start.into());
+ let max = bumpalo::format!(in bump, "{}", end.into());
+ let value = bumpalo::format!(in bump, "{}", self.value.into());
+ let step = bumpalo::format!(in bump, "{}", self.step.into());
let on_change = self.on_change.clone();
let event_bus = bus.clone();
- // TODO: Make `step` configurable
// TODO: Styling
input(bump)
.attr("type", "range")
- .attr("step", "0.01")
+ .attr("step", step.into_bump_str())
.attr("min", min.into_bump_str())
.attr("max", max.into_bump_str())
.attr("value", value.into_bump_str())
@@ -131,19 +160,22 @@ where
Some(slider) => slider,
};
- if let Ok(value) = slider.value().parse::<f32>() {
- event_bus.publish(on_change(value));
+ if let Ok(value) = slider.value().parse::<f64>() {
+ if let Some(value) = T::from_f64(value) {
+ event_bus.publish(on_change(value));
+ }
}
})
.finish()
}
}
-impl<'a, Message> From<Slider<'a, Message>> for Element<'a, Message>
+impl<'a, T, Message> From<Slider<'a, T, Message>> for Element<'a, Message>
where
+ T: 'static + Copy + Into<f64> + num_traits::FromPrimitive,
Message: 'static,
{
- fn from(slider: Slider<'a, Message>) -> Element<'a, Message> {
+ fn from(slider: Slider<'a, T, Message>) -> Element<'a, Message> {
Element::new(slider)
}
}
diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs
index 3ec565a8..2f7308ee 100644
--- a/web/src/widget/text.rs
+++ b/web/src/widget/text.rs
@@ -116,7 +116,12 @@ impl<'a, Message> Widget<Message> for Text {
) -> dodrio::Node<'b> {
use dodrio::builder::*;
- let content = bumpalo::format!(in bump, "{}", self.content);
+ let content = {
+ use dodrio::bumpalo::collections::String;
+
+ String::from_str_in(&self.content, bump)
+ };
+
let color = self
.color
.map(css::color)
@@ -133,7 +138,8 @@ impl<'a, Message> Widget<Message> for Text {
let style = bumpalo::format!(
in bump,
- "width: {}; height: {}; font-size: {}px; color: {}; text-align: {}; font-family: {}",
+ "width: {}; height: {}; font-size: {}px; color: {}; \
+ text-align: {}; font-family: {}",
width,
height,
self.size.unwrap_or(20),
diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs
index 3fa458bd..0049a553 100644
--- a/web/src/widget/text_input.rs
+++ b/web/src/widget/text_input.rs
@@ -151,8 +151,26 @@ where
use dodrio::builder::*;
use wasm_bindgen::JsCast;
- let padding_class =
- style_sheet.insert(bump, css::Rule::Padding(self.padding));
+ let class = {
+ use dodrio::bumpalo::collections::String;
+
+ let padding_class =
+ style_sheet.insert(bump, css::Rule::Padding(self.padding));
+
+ String::from_str_in(&padding_class, bump).into_bump_str()
+ };
+
+ let placeholder = {
+ use dodrio::bumpalo::collections::String;
+
+ String::from_str_in(&self.placeholder, bump).into_bump_str()
+ };
+
+ let value = {
+ use dodrio::bumpalo::collections::String;
+
+ String::from_str_in(&self.value, bump).into_bump_str()
+ };
let on_change = self.on_change.clone();
let on_submit = self.on_submit.clone();
@@ -161,15 +179,14 @@ where
let style = self.style_sheet.active();
input(bump)
- .attr(
- "class",
- bumpalo::format!(in bump, "{}", padding_class).into_bump_str(),
- )
+ .attr("class", class)
.attr(
"style",
bumpalo::format!(
in bump,
- "width: {}; max-width: {}; font-size: {}px; background: {}; border-width: {}px; border-color: {}; border-radius: {}px; color: {}",
+ "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),
@@ -181,19 +198,9 @@ where
)
.into_bump_str(),
)
- .attr(
- "placeholder",
- bumpalo::format!(in bump, "{}", self.placeholder)
- .into_bump_str(),
- )
- .attr(
- "value",
- bumpalo::format!(in bump, "{}", self.value).into_bump_str(),
- )
- .attr(
- "type",
- bumpalo::format!(in bump, "{}", if self.is_secure { "password" } else { "text" }).into_bump_str(),
- )
+ .attr("placeholder", placeholder)
+ .attr("value", value)
+ .attr("type", if self.is_secure { "password" } else { "text" })
.on("input", move |_root, _vdom, event| {
let text_input = match event.target().and_then(|t| {
t.dyn_into::<web_sys::HtmlInputElement>().ok()
@@ -206,10 +213,13 @@ where
})
.on("keypress", move |_root, _vdom, event| {
if let Some(on_submit) = on_submit.clone() {
- let event = event.unchecked_into::<web_sys::KeyboardEvent>();
+ let event =
+ event.unchecked_into::<web_sys::KeyboardEvent>();
match event.key_code() {
- 13 => { submit_event_bus.publish(on_submit); }
+ 13 => {
+ submit_event_bus.publish(on_submit);
+ }
_ => {}
}
}