diff options
author | 2021-07-22 12:37:39 -0500 | |
---|---|---|
committer | 2021-07-22 12:37:39 -0500 | |
commit | e822f654e44d2d7375b7fda966bb772055f377d4 (patch) | |
tree | 8707561f1bb09c9e58cc9d9884bfb16d956f9f65 /web | |
parent | 1c06920158e1a47977b2762bf8b34e56fd1a935a (diff) | |
parent | dc0b96ce407283f2ffd9add5ad339f89097555d3 (diff) | |
download | iced-e822f654e44d2d7375b7fda966bb772055f377d4.tar.gz iced-e822f654e44d2d7375b7fda966bb772055f377d4.tar.bz2 iced-e822f654e44d2d7375b7fda966bb772055f377d4.zip |
Merge branch 'master' of https://github.com/hecrj/iced into wgpu_outdatedframe
Diffstat (limited to 'web')
-rw-r--r-- | web/Cargo.toml | 8 | ||||
-rw-r--r-- | web/README.md | 2 | ||||
-rw-r--r-- | web/src/clipboard.rs | 21 | ||||
-rw-r--r-- | web/src/css.rs | 67 | ||||
-rw-r--r-- | web/src/lib.rs | 19 | ||||
-rw-r--r-- | web/src/widget.rs | 3 | ||||
-rw-r--r-- | web/src/widget/button.rs | 52 | ||||
-rw-r--r-- | web/src/widget/checkbox.rs | 1 | ||||
-rw-r--r-- | web/src/widget/column.rs | 20 | ||||
-rw-r--r-- | web/src/widget/container.rs | 21 | ||||
-rw-r--r-- | web/src/widget/radio.rs | 1 | ||||
-rw-r--r-- | web/src/widget/row.rs | 20 | ||||
-rw-r--r-- | web/src/widget/scrollable.rs | 11 | ||||
-rw-r--r-- | web/src/widget/slider.rs | 2 | ||||
-rw-r--r-- | web/src/widget/text_input.rs | 32 | ||||
-rw-r--r-- | web/src/widget/toggler.rs | 171 |
16 files changed, 356 insertions, 95 deletions
diff --git a/web/Cargo.toml b/web/Cargo.toml index e063a021..06573b95 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_web" -version = "0.3.0" +version = "0.4.0" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "A web backend for Iced" @@ -22,15 +22,15 @@ url = "2.0" num-traits = "0.2" [dependencies.iced_core] -version = "0.3" +version = "0.4" path = "../core" [dependencies.iced_futures] -version = "0.2" +version = "0.3" path = "../futures" [dependencies.iced_style] -version = "0.2" +version = "0.3" path = "../style" [dependencies.web-sys] diff --git a/web/README.md b/web/README.md index 0e770589..58ad8235 100644 --- a/web/README.md +++ b/web/README.md @@ -16,7 +16,7 @@ The crate is currently a __very experimental__, simple abstraction layer over [` Add `iced_web` as a dependency in your `Cargo.toml`: ```toml -iced_web = "0.3" +iced_web = "0.4" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If diff --git a/web/src/clipboard.rs b/web/src/clipboard.rs new file mode 100644 index 00000000..167a1e53 --- /dev/null +++ b/web/src/clipboard.rs @@ -0,0 +1,21 @@ +/// A buffer for short-term storage and transfer within and between +/// applications. +#[derive(Debug, Clone, Copy)] +pub struct Clipboard; + +impl Clipboard { + /// Creates a new [`Clipboard`]. + pub fn new() -> Self { + Self + } + + /// Reads the current content of the [`Clipboard`] as text. + pub fn read(&self) -> Option<String> { + unimplemented! {} + } + + /// Writes the given text contents to the [`Clipboard`]. + pub fn write(&mut self, _contents: String) { + unimplemented! {} + } +} diff --git a/web/src/css.rs b/web/src/css.rs index bdde23f3..21f51f85 100644 --- a/web/src/css.rs +++ b/web/src/css.rs @@ -1,5 +1,5 @@ //! Style your widgets. -use crate::{bumpalo, Align, Background, Color, Length}; +use crate::{bumpalo, Align, Background, Color, Length, Padding}; use std::collections::BTreeMap; @@ -12,11 +12,11 @@ pub enum Rule { /// Container with horizonal distribution Row, - /// Padding of the container - Padding(u16), - /// Spacing between elements Spacing(u16), + + /// Toggler input for a specific size + Toggler(u16), } impl Rule { @@ -25,8 +25,8 @@ impl Rule { match self { Rule::Column => String::from("c"), Rule::Row => String::from("r"), - Rule::Padding(padding) => format!("p-{}", padding), Rule::Spacing(spacing) => format!("s-{}", spacing), + Rule::Toggler(size) => format!("toggler-{}", size), } } @@ -45,13 +45,6 @@ impl Rule { bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str() } - Rule::Padding(padding) => bumpalo::format!( - in bump, - ".{} {{ box-sizing: border-box; padding: {}px }}", - class, - padding - ) - .into_bump_str(), Rule::Spacing(spacing) => bumpalo::format!( in bump, ".c.{} > * {{ margin-bottom: {}px }} \ @@ -66,6 +59,46 @@ impl Rule { class ) .into_bump_str(), + Rule::Toggler(size) => bumpalo::format!( + in bump, + ".toggler-{} {{ display: flex; cursor: pointer; justify-content: space-between; }} \ + .toggler-{} input {{ display:none; }} \ + .toggler-{} span {{ background-color: #b1b1b1; position: relative; display: inline-flex; width:{}px; height: {}px; border-radius: {}px;}} \ + .toggler-{} span > span {{ background-color: #FFFFFF; width: {}px; height: {}px; border-radius: 50%; top: 1px; left: 1px;}} \ + .toggler-{}:hover span > span {{ background-color: #f1f1f1 !important; }} \ + .toggler-{} input:checked + span {{ background-color: #00FF00; }} \ + .toggler-{} input:checked + span > span {{ -webkit-transform: translateX({}px); -ms-transform:translateX({}px); transform: translateX({}px); }} + ", + // toggler + size, + + // toggler input + size, + + // toggler span + size, + size*2, + size, + size, + + // toggler span > span + size, + size-2, + size-2, + + // toggler: hover + span > span + size, + + // toggler input:checked + span + size, + + // toggler input:checked + span > span + size, + size, + size, + size + ) + .into_bump_str(), } } } @@ -170,3 +203,13 @@ pub fn align(align: Align) -> &'static str { Align::End => "flex-end", } } + +/// Returns the style value for the given [`Padding`]. +/// +/// [`Padding`]: struct.Padding.html +pub fn padding(padding: Padding) -> String { + format!( + "{}px {}px {}px {}px", + padding.top, padding.right, padding.bottom, padding.left + ) +} diff --git a/web/src/lib.rs b/web/src/lib.rs index 58f6591d..6b7d0115 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -49,7 +49,7 @@ //! //! [`wasm-pack`]: https://github.com/rustwasm/wasm-pack //! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen -//! [`tour` example]: https://github.com/hecrj/iced/tree/0.2/examples/tour +//! [`tour` example]: https://github.com/hecrj/iced/tree/0.3/examples/tour #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] @@ -59,6 +59,7 @@ use dodrio::bumpalo; use std::{cell::RefCell, rc::Rc}; mod bus; +mod clipboard; mod element; mod hasher; @@ -67,13 +68,14 @@ pub mod subscription; pub mod widget; pub use bus::Bus; +pub use clipboard::Clipboard; pub use css::Css; pub use dodrio; pub use element::Element; pub use hasher::Hasher; pub use iced_core::{ - keyboard, mouse, Align, Background, Color, Font, HorizontalAlignment, - Length, Point, Rectangle, Size, Vector, VerticalAlignment, + keyboard, menu, mouse, Align, Background, Color, Font, HorizontalAlignment, + Length, Menu, Padding, Point, Rectangle, Size, Vector, VerticalAlignment, }; pub use iced_futures::{executor, futures, Command}; pub use subscription::Subscription; @@ -126,7 +128,11 @@ pub trait Application { /// this method. /// /// Any [`Command`] returned will be executed immediately in the background. - fn update(&mut self, message: Self::Message) -> Command<Self::Message>; + fn update( + &mut self, + message: Self::Message, + clipboard: &mut Clipboard, + ) -> Command<Self::Message>; /// Returns the widgets to display in the [`Application`]. /// @@ -156,6 +162,8 @@ pub trait Application { let document = window.document().unwrap(); let body = document.body().unwrap(); + let mut clipboard = Clipboard::new(); + let (sender, receiver) = iced_futures::futures::channel::mpsc::unbounded(); @@ -182,7 +190,8 @@ pub trait Application { let event_loop = receiver.for_each(move |message| { let (command, subscription) = runtime.enter(|| { - let command = application.borrow_mut().update(message); + let command = + application.borrow_mut().update(message, &mut clipboard); let subscription = application.borrow().subscription(); (command, subscription) diff --git a/web/src/widget.rs b/web/src/widget.rs index 023f5f13..4cb0a9cc 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -24,6 +24,7 @@ pub mod radio; pub mod scrollable; pub mod slider; pub mod text_input; +pub mod toggler; mod column; mod row; @@ -40,6 +41,8 @@ pub use slider::Slider; pub use text::Text; #[doc(no_inline)] pub use text_input::TextInput; +#[doc(no_inline)] +pub use toggler::Toggler; pub use checkbox::Checkbox; pub use column::Column; diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index e7cff6a0..cd450b55 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -1,7 +1,7 @@ //! Allow your users to perform actions by pressing a button. //! //! A [`Button`] has some local [`State`]. -use crate::{css, Background, Bus, Css, Element, Length, Widget}; +use crate::{css, Background, Bus, Css, Element, Length, Padding, Widget}; pub use iced_style::button::{Style, StyleSheet}; @@ -20,15 +20,37 @@ use dodrio::bumpalo; /// let button = Button::new(&mut state, Text::new("Press me!")) /// .on_press(Message::ButtonPressed); /// ``` +/// +/// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will +/// be disabled: +/// +/// ``` +/// # use iced_web::{button, Button, Text}; +/// # +/// #[derive(Clone)] +/// enum Message { +/// ButtonPressed, +/// } +/// +/// fn disabled_button(state: &mut button::State) -> Button<'_, Message> { +/// Button::new(state, Text::new("I'm disabled!")) +/// } +/// +/// fn enabled_button(state: &mut button::State) -> Button<'_, Message> { +/// disabled_button(state).on_press(Message::ButtonPressed) +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Button<'a, Message> { content: Element<'a, Message>, on_press: Option<Message>, width: Length, + #[allow(dead_code)] height: Length, min_width: u32, + #[allow(dead_code)] min_height: u32, - padding: u16, + padding: Padding, style: Box<dyn StyleSheet>, } @@ -46,7 +68,7 @@ impl<'a, Message> Button<'a, Message> { height: Length::Shrink, min_width: 0, min_height: 0, - padding: 5, + padding: Padding::new(5), style: Default::default(), } } @@ -75,9 +97,9 @@ impl<'a, Message> Button<'a, Message> { self } - /// Sets the padding of the [`Button`]. - pub fn padding(mut self, padding: u16) -> Self { - self.padding = padding; + /// Sets the [`Padding`] of the [`Button`]. + pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { + self.padding = padding.into(); self } @@ -88,6 +110,7 @@ impl<'a, Message> Button<'a, Message> { } /// Sets the message that will be produced when the [`Button`] is pressed. + /// If on_press isn't set, button will be disabled. pub fn on_press(mut self, msg: Message) -> Self { self.on_press = Some(msg); self @@ -120,9 +143,6 @@ where // TODO: State-based styling let style = self.style.active(); - let padding_class = - style_sheet.insert(bump, css::Rule::Padding(self.padding)); - let background = match style.background { None => String::from("none"), Some(background) => match background { @@ -130,25 +150,19 @@ 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", class) .attr( "style", bumpalo::format!( in bump, "background: {}; border-radius: {}px; width:{}; \ - min-width: {}; color: {}", + min-width: {}; color: {}; padding: {}", background, style.border_radius, css::length(self.width), css::min_length(self.min_width), - css::color(style.text_color) + css::color(style.text_color), + css::padding(self.padding) ) .into_bump_str(), ) @@ -160,6 +174,8 @@ where node = node.on("click", move |_root, _vdom, _event| { event_bus.publish(on_press.clone()); }); + } else { + node = node.attr("disabled", ""); } node.finish() diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 543af99a..43110aa7 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -30,6 +30,7 @@ pub struct Checkbox<Message> { label: String, id: Option<String>, width: Length, + #[allow(dead_code)] style: Box<dyn StyleSheet>, } diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs index d832fdcb..8738c2af 100644 --- a/web/src/widget/column.rs +++ b/web/src/widget/column.rs @@ -1,4 +1,4 @@ -use crate::{css, Align, Bus, Css, Element, Length, Widget}; +use crate::{css, Align, Bus, Css, Element, Length, Padding, Widget}; use dodrio::bumpalo; use std::u32; @@ -9,7 +9,7 @@ use std::u32; #[allow(missing_debug_implementations)] pub struct Column<'a, Message> { spacing: u16, - padding: u16, + padding: Padding, width: Length, height: Length, max_width: u32, @@ -28,7 +28,7 @@ impl<'a, Message> Column<'a, Message> { pub fn with_children(children: Vec<Element<'a, Message>>) -> Self { Column { spacing: 0, - padding: 0, + padding: Padding::ZERO, width: Length::Fill, height: Length::Shrink, max_width: u32::MAX, @@ -48,9 +48,9 @@ impl<'a, Message> Column<'a, Message> { self } - /// Sets the padding of the [`Column`]. - pub fn padding(mut self, units: u16) -> Self { - self.padding = units; + /// Sets the [`Padding`] of the [`Column`]. + pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { + self.padding = padding.into(); self } @@ -114,23 +114,21 @@ impl<'a, Message> Widget<Message> for Column<'a, Message> { let spacing_class = style_sheet.insert(bump, css::Rule::Spacing(self.spacing)); - let padding_class = - style_sheet.insert(bump, css::Rule::Padding(self.padding)); - // TODO: Complete styling div(bump) .attr( "class", - bumpalo::format!(in bump, "{} {} {}", column_class, spacing_class, padding_class) + bumpalo::format!(in bump, "{} {}", column_class, spacing_class) .into_bump_str(), ) .attr("style", bumpalo::format!( in bump, - "width: {}; height: {}; max-width: {}; max-height: {}; align-items: {}", + "width: {}; height: {}; max-width: {}; max-height: {}; padding: {}; align-items: {}", css::length(self.width), css::length(self.height), css::max_length(self.max_width), css::max_length(self.max_height), + css::padding(self.padding), css::align(self.align_items) ).into_bump_str() ) diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs index 7187a4f0..c006e011 100644 --- a/web/src/widget/container.rs +++ b/web/src/widget/container.rs @@ -1,5 +1,5 @@ //! Decorate content and apply alignment. -use crate::{bumpalo, css, Align, Bus, Css, Element, Length, Widget}; +use crate::{bumpalo, css, Align, Bus, Css, Element, Length, Padding, Widget}; pub use iced_style::container::{Style, StyleSheet}; @@ -8,10 +8,11 @@ pub use iced_style::container::{Style, StyleSheet}; /// It is normally used for alignment purposes. #[allow(missing_debug_implementations)] pub struct Container<'a, Message> { - padding: u16, + padding: Padding, width: Length, height: Length, max_width: u32, + #[allow(dead_code)] max_height: u32, horizontal_alignment: Align, vertical_alignment: Align, @@ -28,7 +29,7 @@ impl<'a, Message> Container<'a, Message> { use std::u32; Container { - padding: 0, + padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, @@ -40,9 +41,9 @@ impl<'a, Message> Container<'a, Message> { } } - /// Sets the padding of the [`Container`]. - pub fn padding(mut self, units: u16) -> Self { - self.padding = units; + /// Sets the [`Padding`] of the [`Container`]. + pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { + self.padding = padding.into(); self } @@ -105,24 +106,22 @@ where let column_class = style_sheet.insert(bump, css::Rule::Column); - let padding_class = - style_sheet.insert(bump, css::Rule::Padding(self.padding)); - let style = self.style_sheet.style(); let node = div(bump) .attr( "class", - bumpalo::format!(in bump, "{} {}", column_class, padding_class).into_bump_str(), + bumpalo::format!(in bump, "{}", column_class).into_bump_str(), ) .attr( "style", bumpalo::format!( in bump, - "width: {}; height: {}; max-width: {}; align-items: {}; justify-content: {}; background: {}; color: {}; border-width: {}px; border-color: {}; border-radius: {}px", + "width: {}; height: {}; max-width: {}; padding: {}; 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::padding(self.padding), css::align(self.horizontal_alignment), css::align(self.vertical_alignment), style.background.map(css::background).unwrap_or(String::from("initial")), diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index 5a9bc379..fbc88d29 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -37,6 +37,7 @@ pub struct Radio<Message> { label: String, id: Option<String>, name: Option<String>, + #[allow(dead_code)] style: Box<dyn StyleSheet>, } diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs index f00a544a..ffb515cf 100644 --- a/web/src/widget/row.rs +++ b/web/src/widget/row.rs @@ -1,4 +1,4 @@ -use crate::{css, Align, Bus, Css, Element, Length, Widget}; +use crate::{css, Align, Bus, Css, Element, Length, Padding, Widget}; use dodrio::bumpalo; use std::u32; @@ -9,7 +9,7 @@ use std::u32; #[allow(missing_debug_implementations)] pub struct Row<'a, Message> { spacing: u16, - padding: u16, + padding: Padding, width: Length, height: Length, max_width: u32, @@ -28,7 +28,7 @@ impl<'a, Message> Row<'a, Message> { pub fn with_children(children: Vec<Element<'a, Message>>) -> Self { Row { spacing: 0, - padding: 0, + padding: Padding::ZERO, width: Length::Fill, height: Length::Shrink, max_width: u32::MAX, @@ -48,9 +48,9 @@ impl<'a, Message> Row<'a, Message> { self } - /// Sets the padding of the [`Row`]. - pub fn padding(mut self, units: u16) -> Self { - self.padding = units; + /// Sets the [`Padding`] of the [`Row`]. + pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { + self.padding = padding.into(); self } @@ -114,23 +114,21 @@ impl<'a, Message> Widget<Message> for Row<'a, Message> { let spacing_class = style_sheet.insert(bump, css::Rule::Spacing(self.spacing)); - let padding_class = - style_sheet.insert(bump, css::Rule::Padding(self.padding)); - // TODO: Complete styling div(bump) .attr( "class", - bumpalo::format!(in bump, "{} {} {}", row_class, spacing_class, padding_class) + bumpalo::format!(in bump, "{} {}", row_class, spacing_class) .into_bump_str(), ) .attr("style", bumpalo::format!( in bump, - "width: {}; height: {}; max-width: {}; max-height: {}; align-items: {}", + "width: {}; height: {}; max-width: {}; max-height: {}; padding: {}; align-items: {}", css::length(self.width), css::length(self.height), css::max_length(self.max_width), css::max_length(self.max_height), + css::padding(self.padding), css::align(self.align_items) ).into_bump_str() ) diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs index f9135dd6..ce0a10d4 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, css, Align, Bus, Column, Css, Element, Length, Widget}; +use crate::{ + bumpalo, css, Align, Bus, Column, Css, Element, Length, Padding, Widget, +}; pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; @@ -11,6 +13,7 @@ pub struct Scrollable<'a, Message> { height: Length, max_height: u32, content: Column<'a, Message>, + #[allow(dead_code)] style: Box<dyn StyleSheet>, } @@ -38,9 +41,9 @@ impl<'a, Message> Scrollable<'a, Message> { self } - /// Sets the padding of the [`Scrollable`]. - pub fn padding(mut self, units: u16) -> Self { - self.content = self.content.padding(units); + /// Sets the [`Padding`] of the [`Scrollable`]. + pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { + self.content = self.content.padding(padding); self } diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs index 91a4d2ec..f457aa4c 100644 --- a/web/src/widget/slider.rs +++ b/web/src/widget/slider.rs @@ -38,7 +38,9 @@ pub struct Slider<'a, T, Message> { step: T, value: T, on_change: Rc<Box<dyn Fn(T) -> Message>>, + #[allow(dead_code)] width: Length, + #[allow(dead_code)] style: Box<dyn StyleSheet>, } diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index bc2048a8..e4877f2a 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -1,7 +1,7 @@ //! Display fields that can be filled with text. //! //! A [`TextInput`] has some local [`State`]. -use crate::{bumpalo, css, Bus, Css, Element, Length, Widget}; +use crate::{bumpalo, css, Bus, Css, Element, Length, Padding, Widget}; pub use iced_style::text_input::{Style, StyleSheet}; @@ -35,7 +35,7 @@ pub struct TextInput<'a, Message> { is_secure: bool, width: Length, max_width: u32, - padding: u16, + padding: Padding, size: Option<u16>, on_change: Rc<Box<dyn Fn(String) -> Message>>, on_submit: Option<Message>, @@ -66,7 +66,7 @@ impl<'a, Message> TextInput<'a, Message> { is_secure: false, width: Length::Fill, max_width: u32::MAX, - padding: 0, + padding: Padding::ZERO, size: None, on_change: Rc::new(Box::new(on_change)), on_submit: None, @@ -92,9 +92,9 @@ impl<'a, Message> TextInput<'a, Message> { self } - /// Sets the padding of the [`TextInput`]. - pub fn padding(mut self, units: u16) -> Self { - self.padding = units; + /// Sets the [`Padding`] of the [`TextInput`]. + pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { + self.padding = padding.into(); self } @@ -126,20 +126,11 @@ where &self, bump: &'b bumpalo::Bump, bus: &Bus<Message>, - style_sheet: &mut Css<'b>, + _style_sheet: &mut Css<'b>, ) -> dodrio::Node<'b> { use dodrio::builder::*; use wasm_bindgen::JsCast; - 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; @@ -159,16 +150,16 @@ where let style = self.style_sheet.active(); input(bump) - .attr("class", class) .attr( "style", bumpalo::format!( in bump, - "width: {}; max-width: {}; font-size: {}px; \ + "width: {}; max-width: {}; padding: {}; font-size: {}px; \ background: {}; border-width: {}px; border-color: {}; \ border-radius: {}px; color: {}", css::length(self.width), css::max_length(self.max_width), + css::padding(self.padding), self.size.unwrap_or(20), css::background(style.background), style.border_width, @@ -232,4 +223,9 @@ impl State { // TODO Self::default() } + + /// Selects all the content of the [`TextInput`]. + pub fn select_all(&mut self) { + // TODO + } } diff --git a/web/src/widget/toggler.rs b/web/src/widget/toggler.rs new file mode 100644 index 00000000..0a198079 --- /dev/null +++ b/web/src/widget/toggler.rs @@ -0,0 +1,171 @@ +//! Show toggle controls using togglers. +use crate::{css, Bus, Css, Element, Length, Widget}; + +pub use iced_style::toggler::{Style, StyleSheet}; + +use dodrio::bumpalo; +use std::rc::Rc; + +/// A toggler that can be toggled. +/// +/// # Example +/// +/// ``` +/// # use iced_web::Toggler; +/// +/// pub enum Message { +/// TogglerToggled(bool), +/// } +/// +/// let is_active = true; +/// +/// Toggler::new(is_active, String::from("Toggle me!"), Message::TogglerToggled); +/// ``` +/// +#[allow(missing_debug_implementations)] +pub struct Toggler<Message> { + is_active: bool, + on_toggle: Rc<dyn Fn(bool) -> Message>, + label: Option<String>, + id: Option<String>, + width: Length, + style: Box<dyn StyleSheet>, +} + +impl<Message> Toggler<Message> { + /// Creates a new [`Toggler`]. + /// + /// It expects: + /// * a boolean describing whether the [`Toggler`] is active or not + /// * An optional label for the [`Toggler`] + /// * a function that will be called when the [`Toggler`] is toggled. It + /// will receive the new state of the [`Toggler`] and must produce a + /// `Message`. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn new<F>( + is_active: bool, + label: impl Into<Option<String>>, + f: F, + ) -> Self + where + F: 'static + Fn(bool) -> Message, + { + Toggler { + is_active, + on_toggle: Rc::new(f), + label: label.into(), + id: None, + width: Length::Shrink, + style: Default::default(), + } + } + + /// Sets the width of the [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the style of the [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self { + self.style = style.into(); + self + } + + /// Sets the id of the [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn id(mut self, id: impl Into<String>) -> Self { + self.id = Some(id.into()); + self + } +} + +impl<Message> Widget<Message> for Toggler<Message> +where + Message: 'static, +{ + fn node<'b>( + &self, + bump: &'b bumpalo::Bump, + bus: &Bus<Message>, + style_sheet: &mut Css<'b>, + ) -> dodrio::Node<'b> { + use dodrio::builder::*; + use dodrio::bumpalo::collections::String; + + let toggler_label = &self + .label + .as_ref() + .map(|label| String::from_str_in(&label, bump).into_bump_str()); + + let event_bus = bus.clone(); + let on_toggle = self.on_toggle.clone(); + let is_active = self.is_active; + + let row_class = style_sheet.insert(bump, css::Rule::Row); + let toggler_class = style_sheet.insert(bump, css::Rule::Toggler(16)); + + 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 checkbox = input + .attr("type", "checkbox") + .bool_attr("checked", self.is_active) + .on("click", move |_root, vdom, _event| { + let msg = on_toggle(!is_active); + event_bus.publish(msg); + + vdom.schedule_render(); + }) + .finish(); + + let toggler = span(bump).children(vec![span(bump).finish()]).finish(); + + label + .attr( + "class", + bumpalo::format!(in bump, "{} {}", row_class, toggler_class) + .into_bump_str(), + ) + .attr( + "style", + bumpalo::format!(in bump, "width: {}; align-items: center", css::length(self.width)) + .into_bump_str() + ) + .children( + if let Some(label) = toggler_label { + vec![ + text(label), + checkbox, + toggler, + ] + } else { + vec![ + checkbox, + toggler, + ] + } + ) + .finish() + } +} + +impl<'a, Message> From<Toggler<Message>> for Element<'a, Message> +where + Message: 'static, +{ + fn from(toggler: Toggler<Message>) -> Element<'a, Message> { + Element::new(toggler) + } +} |