diff options
Diffstat (limited to 'web')
-rw-r--r-- | web/CHANGELOG.md | 19 | ||||
-rw-r--r-- | web/Cargo.toml | 16 | ||||
-rw-r--r-- | web/README.md | 7 | ||||
-rw-r--r-- | web/src/bus.rs | 6 | ||||
-rw-r--r-- | web/src/css.rs | 29 | ||||
-rw-r--r-- | web/src/element.rs | 9 | ||||
-rw-r--r-- | web/src/lib.rs | 90 | ||||
-rw-r--r-- | web/src/subscription.rs | 3 | ||||
-rw-r--r-- | web/src/widget.rs | 6 | ||||
-rw-r--r-- | web/src/widget/button.rs | 38 | ||||
-rw-r--r-- | web/src/widget/checkbox.rs | 37 | ||||
-rw-r--r-- | web/src/widget/column.rs | 20 | ||||
-rw-r--r-- | web/src/widget/container.rs | 29 | ||||
-rw-r--r-- | web/src/widget/image.rs | 41 | ||||
-rw-r--r-- | web/src/widget/progress_bar.rs | 8 | ||||
-rw-r--r-- | web/src/widget/radio.rs | 55 | ||||
-rw-r--r-- | web/src/widget/row.rs | 21 | ||||
-rw-r--r-- | web/src/widget/scrollable.rs | 23 | ||||
-rw-r--r-- | web/src/widget/slider.rs | 82 | ||||
-rw-r--r-- | web/src/widget/space.rs | 6 | ||||
-rw-r--r-- | web/src/widget/text.rs | 30 | ||||
-rw-r--r-- | web/src/widget/text_input.rs | 80 |
22 files changed, 258 insertions, 397 deletions
diff --git a/web/CHANGELOG.md b/web/CHANGELOG.md deleted file mode 100644 index ed02519a..00000000 --- a/web/CHANGELOG.md +++ /dev/null @@ -1,19 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] -### Changed -- `Button::background` takes an `Into<Background>` now instead of a `Background`. - -### Fixed -- Render not being scheduled after `Command` futures finishing. - -## [0.1.0] - 2019-11-25 -### Added -- First release! :tada: - -[Unreleased]: https://github.com/hecrj/iced/compare/web-0.1.0...HEAD -[0.1.0]: https://github.com/hecrj/iced/releases/tag/web-0.1.0 diff --git a/web/Cargo.toml b/web/Cargo.toml index c043c697..e063a021 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_web" -version = "0.1.0" +version = "0.3.0" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "A web backend for Iced" @@ -15,20 +15,24 @@ categories = ["web-programming"] maintenance = { status = "actively-developed" } [dependencies] -iced_style = { version = "0.1.0-alpha", path = "../style" } -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.1.0" +version = "0.3" path = "../core" [dependencies.iced_futures] -version = "0.1.0-alpha" +version = "0.2" path = "../futures" +[dependencies.iced_style] +version = "0.2" +path = "../style" + [dependencies.web-sys] version = "0.3.27" features = [ diff --git a/web/README.md b/web/README.md index aceb4a97..0e770589 100644 --- a/web/README.md +++ b/web/README.md @@ -8,8 +8,6 @@ The crate is currently a __very experimental__, simple abstraction layer over [`dodrio`]. - - [documentation]: https://docs.rs/iced_web [`iced_core`]: ../core [`dodrio`]: https://github.com/fitzgen/dodrio @@ -18,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.1.0" +iced_web = "0.3" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If @@ -36,7 +34,7 @@ For instance, let's say we want to build the [`tour` example]: ``` cd examples cargo build --package tour --target wasm32-unknown-unknown -wasm-bindgen ../target/wasm32-unknown-unknown/debug/examples/tour.wasm --out-dir tour --web +wasm-bindgen ../target/wasm32-unknown-unknown/debug/tour.wasm --out-dir tour --web ``` *__Note:__ Keep in mind that Iced is still in early exploration stages and most of the work needs to happen on the native side of the ecosystem. At this stage, it is important to be able to batch work without having to constantly jump back and forth. Because of this, there is currently no requirement for the `master` branch to contain a cross-platform API at all times. If you hit an issue when building an example and want to help, it may be a good way to [start contributing]!* @@ -50,6 +48,7 @@ Once the example is compiled, we need to create an `.html` file to load our appl <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Tour - Iced</title> </head> <body> diff --git a/web/src/bus.rs b/web/src/bus.rs index c66e9659..5ce8e810 100644 --- a/web/src/bus.rs +++ b/web/src/bus.rs @@ -5,7 +5,7 @@ use std::rc::Rc; /// /// It can be used to route messages back to the [`Application`]. /// -/// [`Application`]: trait.Application.html +/// [`Application`]: crate::Application #[allow(missing_debug_implementations)] pub struct Bus<Message> { publish: Rc<Box<dyn Fn(Message) -> ()>>, @@ -33,15 +33,13 @@ where /// Publishes a new message for the [`Application`]. /// - /// [`Application`]: trait.Application.html + /// [`Application`]: crate::Application pub fn publish(&self, message: Message) { (self.publish)(message) } /// Creates a new [`Bus`] that applies the given function to the messages /// before publishing. - /// - /// [`Bus`]: struct.Bus.html pub fn map<B>(&self, mapper: Rc<Box<dyn Fn(B) -> Message>>) -> Bus<B> where B: 'static, diff --git a/web/src/css.rs b/web/src/css.rs index 6a307770..bdde23f3 100644 --- a/web/src/css.rs +++ b/web/src/css.rs @@ -20,9 +20,7 @@ pub enum Rule { } impl Rule { - /// Returns the class name of the [`Style`]. - /// - /// [`Style`]: enum.Style.html + /// Returns the class name of the [`Rule`]. pub fn class<'a>(&self) -> String { match self { Rule::Column => String::from("c"), @@ -32,9 +30,7 @@ impl Rule { } } - /// Returns the declaration of the [`Style`]. - /// - /// [`Style`]: enum.Style.html + /// Returns the declaration of the [`Rule`]. pub fn declaration<'a>(&self, bump: &'a bumpalo::Bump) -> &'a str { let class = self.class(); @@ -81,22 +77,17 @@ pub struct Css<'a> { } impl<'a> Css<'a> { - /// Creates an empty style [`Sheet`]. - /// - /// [`Sheet`]: struct.Sheet.html + /// Creates an empty [`Css`]. pub fn new() -> Self { Css { rules: BTreeMap::new(), } } - /// Inserts the [`rule`] in the [`Sheet`], if it was not previously + /// Inserts the [`Rule`] in the [`Css`], if it was not previously /// inserted. /// /// It returns the class name of the provided [`Rule`]. - /// - /// [`Sheet`]: struct.Sheet.html - /// [`Rule`]: enum.Rule.html pub fn insert(&mut self, bump: &'a bumpalo::Bump, rule: Rule) -> String { let class = rule.class(); @@ -107,9 +98,7 @@ impl<'a> Css<'a> { class } - /// Produces the VDOM node of the style [`Sheet`]. - /// - /// [`Sheet`]: struct.Sheet.html + /// Produces the VDOM node of the [`Css`]. pub fn node(self, bump: &'a bumpalo::Bump) -> dodrio::Node<'a> { use dodrio::builder::*; @@ -133,8 +122,6 @@ impl<'a> Css<'a> { } /// Returns the style value for the given [`Length`]. -/// -/// [`Length`]: ../enum.Length.html pub fn length(length: Length) -> String { match length { Length::Shrink => String::from("auto"), @@ -164,15 +151,11 @@ pub fn min_length(units: u32) -> String { } /// Returns the style value for the given [`Color`]. -/// -/// [`Color`]: ../struct.Color.html pub fn color(Color { r, g, b, a }: Color) -> String { format!("rgba({}, {}, {}, {})", 255.0 * r, 255.0 * g, 255.0 * b, a) } /// Returns the style value for the given [`Background`]. -/// -/// [`Background`]: ../struct.Background.html pub fn background(background: Background) -> String { match background { Background::Color(c) => color(c), @@ -180,8 +163,6 @@ pub fn background(background: Background) -> String { } /// Returns the style value for the given [`Align`]. -/// -/// [`Align`]: ../enum.Align.html pub fn align(align: Align) -> &'static str { match align { Align::Start => "flex-start", diff --git a/web/src/element.rs b/web/src/element.rs index 93e73713..6bb90177 100644 --- a/web/src/element.rs +++ b/web/src/element.rs @@ -11,9 +11,7 @@ use std::rc::Rc; /// If you have a [built-in widget], you should be able to use `Into<Element>` /// to turn it into an [`Element`]. /// -/// [built-in widget]: widget/index.html -/// [`Widget`]: widget/trait.Widget.html -/// [`Element`]: struct.Element.html +/// [built-in widget]: mod@crate::widget #[allow(missing_debug_implementations)] pub struct Element<'a, Message> { pub(crate) widget: Box<dyn Widget<Message> + 'a>, @@ -21,9 +19,6 @@ pub struct Element<'a, Message> { impl<'a, Message> Element<'a, Message> { /// Create a new [`Element`] containing the given [`Widget`]. - /// - /// [`Element`]: struct.Element.html - /// [`Widget`]: widget/trait.Widget.html pub fn new(widget: impl Widget<Message> + 'a) -> Self { Self { widget: Box::new(widget), @@ -34,8 +29,6 @@ impl<'a, Message> Element<'a, Message> { /// /// This method is useful when you want to decouple different parts of your /// UI and make them __composable__. - /// - /// [`Element`]: struct.Element.html pub fn map<F, B>(self, f: F) -> Element<'a, B> where Message: 'static, diff --git a/web/src/lib.rs b/web/src/lib.rs index 258ad9e7..58f6591d 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -1,7 +1,5 @@ //! A web runtime for Iced, targetting the DOM. //! -//!  -//! //! `iced_web` takes [`iced_core`] and builds a WebAssembly runtime on top. It //! achieves this by introducing a `Widget` trait that can be used to produce //! VDOM nodes. @@ -23,8 +21,8 @@ //! //! ```bash //! cd examples -//! cargo build --example tour --target wasm32-unknown-unknown -//! wasm-bindgen ../target/wasm32-unknown-unknown/debug/examples/tour.wasm --out-dir tour --web +//! cargo build --package tour --target wasm32-unknown-unknown +//! wasm-bindgen ../target/wasm32-unknown-unknown/debug/tour.wasm --out-dir tour --web //! ``` //! //! Then, we need to create an `.html` file to load our application: @@ -34,6 +32,7 @@ //! <html> //! <head> //! <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> +//! <meta name="viewport" content="width=device-width, initial-scale=1"> //! <title>Tour - Iced</title> //! </head> //! <body> @@ -50,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/blob/master/examples/tour.rs +//! [`tour` example]: https://github.com/hecrj/iced/tree/0.2/examples/tour #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] @@ -73,8 +72,8 @@ pub use dodrio; pub use element::Element; pub use hasher::Hasher; pub use iced_core::{ - Align, Background, Color, Font, HorizontalAlignment, Length, Point, Size, - Vector, VerticalAlignment, + keyboard, mouse, Align, Background, Color, Font, HorizontalAlignment, + Length, Point, Rectangle, Size, Vector, VerticalAlignment, }; pub use iced_futures::{executor, futures, Command}; pub use subscription::Subscription; @@ -94,30 +93,23 @@ pub use executor::Executor; /// An [`Application`](trait.Application.html) can execute asynchronous actions /// by returning a [`Command`](struct.Command.html) in some of its methods. pub trait Application { + /// The [`Executor`] that will run commands and subscriptions. + type Executor: Executor; + /// The type of __messages__ your [`Application`] will produce. - /// - /// [`Application`]: trait.Application.html type Message: Send; - /// The [`Executor`] that will run commands and subscriptions. - /// - /// The [`executor::WasmBindgen`] can be a good choice for the Web. - /// - /// [`Executor`]: trait.Executor.html - /// [`executor::Default`]: executor/struct.Default.html - type Executor: Executor; + /// The data needed to initialize your [`Application`]. + type Flags; /// Initializes the [`Application`]. /// /// Here is where you should return the initial state of your app. /// - /// Additionally, you can return a [`Command`](struct.Command.html) if you - /// need to perform some async action in the background on startup. This is - /// useful if you want to load state from a file, perform an initial HTTP - /// request, etc. - /// - /// [`Application`]: trait.Application.html - fn new() -> (Self, Command<Self::Message>) + /// Additionally, you can return a [`Command`] if you need to perform some + /// async action in the background on startup. This is useful if you want to + /// load state from a file, perform an initial HTTP request, etc. + fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) where Self: Sized; @@ -125,8 +117,6 @@ pub trait Application { /// /// This title can be dynamic! The runtime will automatically update the /// title of your application when necessary. - /// - /// [`Application`]: trait.Application.html fn title(&self) -> String; /// Handles a __message__ and updates the state of the [`Application`]. @@ -136,16 +126,11 @@ pub trait Application { /// this method. /// /// Any [`Command`] returned will be executed immediately in the background. - /// - /// [`Application`]: trait.Application.html - /// [`Command`]: struct.Command.html fn update(&mut self, message: Self::Message) -> Command<Self::Message>; /// Returns the widgets to display in the [`Application`]. /// /// These widgets can produce __messages__ based on user interaction. - /// - /// [`Application`]: trait.Application.html fn view(&mut self) -> Element<'_, Self::Message>; /// Returns the event [`Subscription`] for the current state of the @@ -156,30 +141,21 @@ pub trait Application { /// [`update`](#tymethod.update). /// /// By default, this method returns an empty [`Subscription`]. - /// - /// [`Subscription`]: struct.Subscription.html fn subscription(&self) -> Subscription<Self::Message> { Subscription::none() } /// Runs the [`Application`]. - /// - /// [`Application`]: trait.Application.html - fn run() + fn run(flags: Self::Flags) where Self: 'static + Sized, { use futures::stream::StreamExt; - let (app, command) = Self::new(); - let window = web_sys::window().unwrap(); let document = window.document().unwrap(); let body = document.body().unwrap(); - let mut title = app.title(); - document.set_title(&title); - let (sender, receiver) = iced_futures::futures::channel::mpsc::unbounded(); @@ -187,6 +163,12 @@ pub trait Application { Self::Executor::new().expect("Create executor"), sender.clone(), ); + + let (app, command) = runtime.enter(|| Self::new(flags)); + + let mut title = app.title(); + document.set_title(&title); + runtime.spawn(command); let application = Rc::new(RefCell::new(app)); @@ -199,8 +181,13 @@ pub trait Application { let vdom = dodrio::Vdom::new(&body, instance); let event_loop = receiver.for_each(move |message| { - let command = application.borrow_mut().update(message); - let subscription = application.borrow().subscription(); + let (command, subscription) = runtime.enter(|| { + let command = application.borrow_mut().update(message); + let subscription = application.borrow().subscription(); + + (command, subscription) + }); + let new_title = application.borrow().title(); runtime.spawn(command); @@ -226,28 +213,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/subscription.rs b/web/src/subscription.rs index 6b8415c0..fb54f7e3 100644 --- a/web/src/subscription.rs +++ b/web/src/subscription.rs @@ -12,8 +12,7 @@ use crate::Hasher; /// For instance, you can use a [`Subscription`] to listen to a WebSocket /// connection, keyboard presses, mouse events, time ticks, etc. /// -/// [`Command`]: ../struct.Command.html -/// [`Subscription`]: struct.Subscription.html +/// [`Command`]: crate::Command pub type Subscription<T> = iced_futures::Subscription<Hasher, (), T>; pub use iced_futures::subscription::Recipe; diff --git a/web/src/widget.rs b/web/src/widget.rs index 025cf22f..023f5f13 100644 --- a/web/src/widget.rs +++ b/web/src/widget.rs @@ -12,8 +12,6 @@ //! ``` //! use iced_web::{button, Button, Widget}; //! ``` -//! -//! [`Widget`]: trait.Widget.html use crate::{Bus, Css}; use dodrio::bumpalo; @@ -56,12 +54,8 @@ pub use space::Space; /// /// If you want to build your own widgets, you will need to implement this /// trait. -/// -/// [`Widget`]: trait.Widget.html pub trait Widget<Message> { /// Produces a VDOM node for the [`Widget`]. - /// - /// [`Widget`]: trait.Widget.html fn node<'b>( &self, bump: &'b bumpalo::Bump, diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index 3a5afe60..e7cff6a0 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -1,9 +1,6 @@ //! Allow your users to perform actions by pressing a button. //! //! A [`Button`] has some local [`State`]. -//! -//! [`Button`]: struct.Button.html -//! [`State`]: struct.State.html use crate::{css, Background, Bus, Css, Element, Length, Widget}; pub use iced_style::button::{Style, StyleSheet}; @@ -38,9 +35,6 @@ pub struct Button<'a, Message> { impl<'a, Message> Button<'a, Message> { /// Creates a new [`Button`] with some local [`State`] and the given /// content. - /// - /// [`Button`]: struct.Button.html - /// [`State`]: struct.State.html pub fn new<E>(_state: &'a mut State, content: E) -> Self where E: Into<Element<'a, Message>>, @@ -58,56 +52,42 @@ impl<'a, Message> Button<'a, Message> { } /// Sets the width of the [`Button`]. - /// - /// [`Button`]: struct.Button.html pub fn width(mut self, width: Length) -> Self { self.width = width; 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 pub fn min_width(mut self, min_width: u32) -> Self { self.min_width = min_width; self } /// Sets the minimum height of the [`Button`]. - /// - /// [`Button`]: struct.Button.html pub fn min_height(mut self, min_height: u32) -> Self { self.min_height = min_height; self } /// Sets the padding of the [`Button`]. - /// - /// [`Button`]: struct.Button.html pub fn padding(mut self, padding: u16) -> Self { self.padding = padding; self } /// Sets the style of the [`Button`]. - /// - /// [`Button`]: struct.Button.html pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self { self.style = style.into(); self } /// Sets the message that will be produced when the [`Button`] is pressed. - /// - /// [`Button`]: struct.Button.html pub fn on_press(mut self, msg: Message) -> Self { self.on_press = Some(msg); self @@ -115,15 +95,11 @@ impl<'a, Message> Button<'a, Message> { } /// The local state of a [`Button`]. -/// -/// [`Button`]: struct.Button.html #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct State; impl State { /// Creates a new [`State`]. - /// - /// [`State`]: struct.State.html pub fn new() -> State { State::default() } @@ -154,16 +130,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 0657ccfb..543af99a 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>, } @@ -41,36 +42,37 @@ impl<Message> Checkbox<Message> { /// * a function that will be called when the [`Checkbox`] is toggled. It /// will receive the new state of the [`Checkbox`] and must produce a /// `Message`. - /// - /// [`Checkbox`]: struct.Checkbox.html - pub fn new<F>(is_checked: bool, label: &str, f: F) -> Self + pub fn new<F>(is_checked: bool, label: impl Into<String>, f: F) -> Self where F: 'static + Fn(bool) -> Message, { Checkbox { is_checked, on_toggle: Rc::new(f), - label: String::from(label), + label: label.into(), + id: None, width: Length::Shrink, style: Default::default(), } } /// 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 style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self { self.style = style.into(); self } + + /// Sets the id of the [`Checkbox`]. + 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 +86,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 +99,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 +120,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 +130,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/column.rs b/web/src/widget/column.rs index 25b88b0e..d832fdcb 100644 --- a/web/src/widget/column.rs +++ b/web/src/widget/column.rs @@ -6,8 +6,6 @@ use std::u32; /// A container that distributes its contents vertically. /// /// A [`Column`] will try to fill the horizontal space of its container. -/// -/// [`Column`]: struct.Column.html #[allow(missing_debug_implementations)] pub struct Column<'a, Message> { spacing: u16, @@ -22,15 +20,11 @@ pub struct Column<'a, Message> { impl<'a, Message> Column<'a, Message> { /// Creates an empty [`Column`]. - /// - /// [`Column`]: struct.Column.html pub fn new() -> Self { Self::with_children(Vec::new()) } /// Creates a [`Column`] with the given elements. - /// - /// [`Column`]: struct.Column.html pub fn with_children(children: Vec<Element<'a, Message>>) -> Self { Column { spacing: 0, @@ -55,56 +49,42 @@ impl<'a, Message> Column<'a, Message> { } /// Sets the padding of the [`Column`]. - /// - /// [`Column`]: struct.Column.html pub fn padding(mut self, units: u16) -> Self { self.padding = units; self } /// Sets the width of the [`Column`]. - /// - /// [`Column`]: struct.Column.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Column`]. - /// - /// [`Column`]: struct.Column.html pub fn height(mut self, height: Length) -> Self { self.height = height; self } /// Sets the maximum width of the [`Column`]. - /// - /// [`Column`]: struct.Column.html pub fn max_width(mut self, max_width: u32) -> Self { self.max_width = max_width; self } /// Sets the maximum height of the [`Column`] in pixels. - /// - /// [`Column`]: struct.Column.html pub fn max_height(mut self, max_height: u32) -> Self { self.max_height = max_height; self } /// Sets the horizontal alignment of the contents of the [`Column`] . - /// - /// [`Column`]: struct.Column.html pub fn align_items(mut self, align: Align) -> Self { self.align_items = align; self } /// Adds an element to the [`Column`]. - /// - /// [`Column`]: struct.Column.html pub fn push<E>(mut self, child: E) -> Self where E: Into<Element<'a, Message>>, diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs index 8e4318f9..7187a4f0 100644 --- a/web/src/widget/container.rs +++ b/web/src/widget/container.rs @@ -8,6 +8,7 @@ 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, width: Length, height: Length, max_width: u32, @@ -20,8 +21,6 @@ pub struct Container<'a, Message> { impl<'a, Message> Container<'a, Message> { /// Creates an empty [`Container`]. - /// - /// [`Container`]: struct.Container.html pub fn new<T>(content: T) -> Self where T: Into<Element<'a, Message>>, @@ -29,6 +28,7 @@ impl<'a, Message> Container<'a, Message> { use std::u32; Container { + padding: 0, width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, @@ -40,41 +40,37 @@ impl<'a, Message> Container<'a, Message> { } } + /// Sets the padding of the [`Container`]. + pub fn padding(mut self, units: u16) -> Self { + self.padding = units; + self + } + /// Sets the width of the [`Container`]. - /// - /// [`Container`]: struct.Container.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Container`]. - /// - /// [`Container`]: struct.Container.html pub fn height(mut self, height: Length) -> Self { self.height = height; self } /// Sets the maximum width of the [`Container`]. - /// - /// [`Container`]: struct.Container.html pub fn max_width(mut self, max_width: u32) -> Self { self.max_width = max_width; self } /// Sets the maximum height of the [`Container`] in pixels. - /// - /// [`Container`]: struct.Container.html pub fn max_height(mut self, max_height: u32) -> Self { self.max_height = max_height; self } /// Centers the contents in the horizontal axis of the [`Container`]. - /// - /// [`Container`]: struct.Container.html pub fn center_x(mut self) -> Self { self.horizontal_alignment = Align::Center; @@ -82,8 +78,6 @@ impl<'a, Message> Container<'a, Message> { } /// Centers the contents in the vertical axis of the [`Container`]. - /// - /// [`Container`]: struct.Container.html pub fn center_y(mut self) -> Self { self.vertical_alignment = Align::Center; @@ -91,8 +85,6 @@ impl<'a, Message> Container<'a, Message> { } /// 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 @@ -113,12 +105,15 @@ 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).into_bump_str(), + bumpalo::format!(in bump, "{} {}", column_class, padding_class).into_bump_str(), ) .attr( "style", diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs index 029ab352..05c89ea5 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, @@ -31,31 +34,32 @@ pub struct Image { impl Image { /// Creates a new [`Image`] with the given path. - /// - /// [`Image`]: struct.Image.html pub fn new<T: Into<Handle>>(handle: T) -> Self { Image { handle: handle.into(), + alt: Default::default(), width: Length::Shrink, height: Length::Shrink, } } /// Sets the width of the [`Image`] boundaries. - /// - /// [`Image`]: struct.Image.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Image`] boundaries. - /// - /// [`Image`]: struct.Image.html pub fn height(mut self, height: Length) -> Self { self.height = height; self } + + /// Sets the alt text of the [`Image`]. + pub fn alt(mut self, alt: impl Into<String>) -> Self { + self.alt = alt.into(); + self + } } impl<Message> Widget<Message> for Image { @@ -66,12 +70,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 = bumpalo::format!(in bump, "{}", match self.handle.data.as_ref() { - Data::Path(path) => path.to_str().unwrap_or("") - }); + 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 mut image = img(bump).attr("src", src.into_bump_str()); + let alt = String::from_str_in(&self.alt, bump).into_bump_str(); + + let mut image = img(bump).attr("src", src).attr("alt", alt); match self.width { Length::Shrink => {} @@ -99,8 +110,6 @@ impl<'a, Message> From<Image> for Element<'a, Message> { } /// An [`Image`] handle. -/// -/// [`Image`]: struct.Image.html #[derive(Debug, Clone)] pub struct Handle { id: u64, @@ -109,8 +118,6 @@ pub struct Handle { 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())) } @@ -126,15 +133,11 @@ impl Handle { } /// 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 } @@ -153,8 +156,6 @@ impl From<&str> for Handle { } /// The data of an [`Image`]. -/// -/// [`Image`]: struct.Image.html #[derive(Clone, Hash)] pub enum Data { /// A remote image diff --git a/web/src/widget/progress_bar.rs b/web/src/widget/progress_bar.rs index 856203c0..7d77616e 100644 --- a/web/src/widget/progress_bar.rs +++ b/web/src/widget/progress_bar.rs @@ -32,8 +32,6 @@ impl 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()), @@ -45,24 +43,18 @@ impl ProgressBar { } /// 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 diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index e00e26db..5a9bc379 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>, } @@ -47,9 +49,12 @@ impl<Message> Radio<Message> { /// * the current selected value /// * a function that will be called when the [`Radio`] is selected. It /// receives the value of the radio and must produce a `Message`. - /// - /// [`Radio`]: struct.Radio.html - pub fn new<F, V>(value: V, label: &str, selected: Option<V>, f: F) -> Self + pub fn new<F, V>( + value: V, + label: impl Into<String>, + selected: Option<V>, + f: F, + ) -> Self where V: Eq + Copy, F: 'static + Fn(V) -> Message, @@ -57,18 +62,30 @@ impl<Message> Radio<Message> { Radio { is_selected: Some(value) == selected, on_click: f(value), - label: String::from(label), + label: label.into(), + id: None, + name: None, style: Default::default(), } } /// Sets the style of the [`Radio`] button. - /// - /// [`Radio`]: struct.Radio.html pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self { self.style = style.into(); self } + + /// Sets the name attribute of the [`Radio`] button. + pub fn name(mut self, name: impl Into<String>) -> Self { + self.name = Some(name.into()); + self + } + + /// Sets the id of the [`Radio`] button. + pub fn id(mut self, id: impl Into<String>) -> Self { + self.id = Some(id.into()); + self + } } impl<Message> Widget<Message> for Radio<Message> @@ -82,17 +99,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) @@ -100,7 +135,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/row.rs b/web/src/widget/row.rs index cfa10fdf..f00a544a 100644 --- a/web/src/widget/row.rs +++ b/web/src/widget/row.rs @@ -6,8 +6,6 @@ use std::u32; /// A container that distributes its contents horizontally. /// /// A [`Row`] will try to fill the horizontal space of its container. -/// -/// [`Row`]: struct.Row.html #[allow(missing_debug_implementations)] pub struct Row<'a, Message> { spacing: u16, @@ -22,15 +20,11 @@ pub struct Row<'a, Message> { impl<'a, Message> Row<'a, Message> { /// Creates an empty [`Row`]. - /// - /// [`Row`]: struct.Row.html pub fn new() -> Self { Self::with_children(Vec::new()) } /// Creates a [`Row`] with the given elements. - /// - /// [`Row`]: struct.Row.html pub fn with_children(children: Vec<Element<'a, Message>>) -> Self { Row { spacing: 0, @@ -55,57 +49,42 @@ impl<'a, Message> Row<'a, Message> { } /// Sets the padding of the [`Row`]. - /// - /// [`Row`]: struct.Row.html pub fn padding(mut self, units: u16) -> Self { self.padding = units; self } /// Sets the width of the [`Row`]. - /// - /// [`Row`]: struct.Row.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Row`]. - /// - /// [`Row`]: struct.Row.html pub fn height(mut self, height: Length) -> Self { self.height = height; self } /// Sets the maximum width of the [`Row`]. - /// - /// [`Row`]: struct.Row.html pub fn max_width(mut self, max_width: u32) -> Self { self.max_width = max_width; self } /// Sets the maximum height of the [`Row`]. - /// - /// [`Row`]: struct.Row.html pub fn max_height(mut self, max_height: u32) -> Self { self.max_height = max_height; self } /// Sets the vertical alignment of the contents of the [`Row`] . - /// - /// [`Row`]: struct.Row.html pub fn align_items(mut self, align: Align) -> Self { self.align_items = align; self } /// Adds an [`Element`] to the [`Row`]. - /// - /// [`Element`]: ../struct.Element.html - /// [`Row`]: struct.Row.html pub fn push<E>(mut self, child: E) -> Self where E: Into<Element<'a, Message>>, diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs index 07b38aad..f9135dd6 100644 --- a/web/src/widget/scrollable.rs +++ b/web/src/widget/scrollable.rs @@ -16,9 +16,6 @@ pub struct Scrollable<'a, Message> { impl<'a, Message> Scrollable<'a, Message> { /// Creates a new [`Scrollable`] with the given [`State`]. - /// - /// [`Scrollable`]: struct.Scrollable.html - /// [`State`]: struct.State.html pub fn new(_state: &'a mut State) -> Self { use std::u32; @@ -42,64 +39,48 @@ impl<'a, Message> Scrollable<'a, Message> { } /// Sets the padding of the [`Scrollable`]. - /// - /// [`Scrollable`]: struct.Scrollable.html pub fn padding(mut self, units: u16) -> Self { self.content = self.content.padding(units); self } /// Sets the width of the [`Scrollable`]. - /// - /// [`Scrollable`]: struct.Scrollable.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Scrollable`]. - /// - /// [`Scrollable`]: struct.Scrollable.html pub fn height(mut self, height: Length) -> Self { self.height = height; self } /// Sets the maximum width of the [`Scrollable`]. - /// - /// [`Scrollable`]: struct.Scrollable.html pub fn max_width(mut self, max_width: u32) -> Self { self.content = self.content.max_width(max_width); self } /// Sets the maximum height of the [`Scrollable`] in pixels. - /// - /// [`Scrollable`]: struct.Scrollable.html pub fn max_height(mut self, max_height: u32) -> Self { self.max_height = max_height; self } /// Sets the horizontal alignment of the contents of the [`Scrollable`] . - /// - /// [`Scrollable`]: struct.Scrollable.html pub fn align_items(mut self, align_items: Align) -> Self { self.content = self.content.align_items(align_items); 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 pub fn push<E>(mut self, child: E) -> Self where E: Into<Element<'a, Message>>, @@ -154,15 +135,11 @@ where } /// The local state of a [`Scrollable`]. -/// -/// [`Scrollable`]: struct.Scrollable.html #[derive(Debug, Clone, Copy, Default)] pub struct State; impl State { /// Creates a new [`State`] with the scrollbar located at the top. - /// - /// [`State`]: struct.State.html pub fn new() -> Self { State::default() } diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs index 5aa6439e..91a4d2ec 100644 --- a/web/src/widget/slider.rs +++ b/web/src/widget/slider.rs @@ -1,9 +1,6 @@ //! Display an interactive selector of a single value from a range of values. //! //! A [`Slider`] has some local [`State`]. -//! -//! [`Slider`]: struct.Slider.html -//! [`State`]: struct.State.html use crate::{Bus, Css, Element, Length, Widget}; pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; @@ -16,7 +13,8 @@ use std::{ops::RangeInclusive, rc::Rc}; /// /// A [`Slider`] will try to fill the horizontal space of its container. /// -/// [`Slider`]: struct.Slider.html +/// The [`Slider`] range of numeric values is generic and its step size defaults +/// to 1 unit. /// /// # Example /// ``` @@ -34,16 +32,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: @@ -53,22 +55,32 @@ impl<'a, Message> Slider<'a, Message> { /// * a function that will be called when the [`Slider`] is dragged. /// It receives the new value of the [`Slider`] and must produce a /// `Message`. - /// - /// [`Slider`]: struct.Slider.html - /// [`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(), @@ -76,24 +88,27 @@ impl<'a, Message> Slider<'a, Message> { } /// Sets the width of the [`Slider`]. - /// - /// [`Slider`]: struct.Slider.html pub fn width(mut self, width: Length) -> Self { 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 } + + /// Sets the step size of the [`Slider`]. + 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 +122,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,33 +146,32 @@ 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) } } /// The local state of a [`Slider`]. -/// -/// [`Slider`]: struct.Slider.html #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct State; impl State { /// Creates a new [`State`]. - /// - /// [`State`]: struct.State.html pub fn new() -> Self { Self } diff --git a/web/src/widget/space.rs b/web/src/widget/space.rs index 4ce52595..a8571fdb 100644 --- a/web/src/widget/space.rs +++ b/web/src/widget/space.rs @@ -12,15 +12,11 @@ pub struct Space { 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, @@ -29,8 +25,6 @@ impl Space { } /// Creates an amount of vertical [`Space`]. - /// - /// [`Space`]: struct.Space.html pub fn with_height(height: Length) -> Self { Space { width: Length::Shrink, diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs index 3ec565a8..72232dc0 100644 --- a/web/src/widget/text.rs +++ b/web/src/widget/text.rs @@ -28,8 +28,6 @@ pub struct Text { impl Text { /// Create a new fragment of [`Text`] with the given contents. - /// - /// [`Text`]: struct.Text.html pub fn new<T: Into<String>>(label: T) -> Self { Text { content: label.into(), @@ -44,51 +42,36 @@ impl Text { } /// Sets the size of the [`Text`]. - /// - /// [`Text`]: struct.Text.html pub fn size(mut self, size: u16) -> Self { self.size = Some(size); self } /// Sets the [`Color`] of the [`Text`]. - /// - /// [`Text`]: struct.Text.html - /// [`Color`]: ../../struct.Color.html pub fn color<C: Into<Color>>(mut self, color: C) -> Self { self.color = Some(color.into()); self } /// Sets the [`Font`] of the [`Text`]. - /// - /// [`Text`]: struct.Text.html - /// [`Font`]: ../../struct.Font.html pub fn font(mut self, font: Font) -> Self { self.font = font; self } /// Sets the width of the [`Text`] boundaries. - /// - /// [`Text`]: struct.Text.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Text`] boundaries. - /// - /// [`Text`]: struct.Text.html pub fn height(mut self, height: Length) -> Self { self.height = height; self } /// Sets the [`HorizontalAlignment`] of the [`Text`]. - /// - /// [`Text`]: struct.Text.html - /// [`HorizontalAlignment`]: enum.HorizontalAlignment.html pub fn horizontal_alignment( mut self, alignment: HorizontalAlignment, @@ -98,9 +81,6 @@ impl Text { } /// Sets the [`VerticalAlignment`] of the [`Text`]. - /// - /// [`Text`]: struct.Text.html - /// [`VerticalAlignment`]: enum.VerticalAlignment.html pub fn vertical_alignment(mut self, alignment: VerticalAlignment) -> Self { self.vertical_alignment = alignment; self @@ -116,7 +96,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 +118,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..bc2048a8 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -1,9 +1,6 @@ //! Display fields that can be filled with text. //! //! A [`TextInput`] has some local [`State`]. -//! -//! [`TextInput`]: struct.TextInput.html -//! [`State`]: struct.State.html use crate::{bumpalo, css, Bus, Css, Element, Length, Widget}; pub use iced_style::text_input::{Style, StyleSheet}; @@ -53,9 +50,6 @@ impl<'a, Message> TextInput<'a, Message> { /// - a placeholder /// - the current value /// - a function that produces a message when the [`TextInput`] changes - /// - /// [`TextInput`]: struct.TextInput.html - /// [`State`]: struct.State.html pub fn new<F>( state: &'a mut State, placeholder: &str, @@ -81,40 +75,30 @@ impl<'a, Message> TextInput<'a, Message> { } /// 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 pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the maximum width of the [`TextInput`]. - /// - /// [`TextInput`]: struct.TextInput.html pub fn max_width(mut self, max_width: u32) -> Self { self.max_width = max_width; self } /// Sets the padding of the [`TextInput`]. - /// - /// [`TextInput`]: struct.TextInput.html pub fn padding(mut self, units: u16) -> Self { self.padding = units; self } /// Sets the text size of the [`TextInput`]. - /// - /// [`TextInput`]: struct.TextInput.html pub fn size(mut self, size: u16) -> Self { self.size = Some(size); self @@ -122,16 +106,12 @@ impl<'a, Message> TextInput<'a, Message> { /// Sets the message that should be produced when the [`TextInput`] is /// focused and the enter key is pressed. - /// - /// [`TextInput`]: struct.TextInput.html pub fn on_submit(mut self, message: Message) -> Self { 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 @@ -151,8 +131,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 +159,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 +178,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 +193,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); + } _ => {} } } @@ -228,22 +218,16 @@ where } /// The state of a [`TextInput`]. -/// -/// [`TextInput`]: struct.TextInput.html #[derive(Debug, Clone, Copy, Default)] pub struct State; impl State { /// Creates a new [`State`], representing an unfocused [`TextInput`]. - /// - /// [`State`]: struct.State.html 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() |