diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/Cargo.toml | 5 | ||||
| -rw-r--r-- | web/src/lib.rs | 19 | ||||
| -rw-r--r-- | web/src/widget/button.rs | 14 | ||||
| -rw-r--r-- | web/src/widget/checkbox.rs | 29 | ||||
| -rw-r--r-- | web/src/widget/image.rs | 27 | ||||
| -rw-r--r-- | web/src/widget/radio.rs | 46 | ||||
| -rw-r--r-- | web/src/widget/slider.rs | 70 | ||||
| -rw-r--r-- | web/src/widget/text.rs | 10 | ||||
| -rw-r--r-- | web/src/widget/text_input.rs | 54 | 
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};  ///  ///   #[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); +                        }                          _ => {}                      }                  }  | 
