summaryrefslogtreecommitdiffstats
path: root/web/src
diff options
context:
space:
mode:
Diffstat (limited to 'web/src')
-rw-r--r--web/src/bus.rs53
-rw-r--r--web/src/command.rs72
-rw-r--r--web/src/command/action.rs28
-rw-r--r--web/src/css.rs217
-rw-r--r--web/src/element.rs90
-rw-r--r--web/src/hasher.rs21
-rw-r--r--web/src/lib.rs431
-rw-r--r--web/src/subscription.rs18
-rw-r--r--web/src/widget.rs68
-rw-r--r--web/src/widget/button.rs192
-rw-r--r--web/src/widget/checkbox.rs150
-rw-r--r--web/src/widget/column.rs148
-rw-r--r--web/src/widget/container.rs153
-rw-r--r--web/src/widget/image.rs186
-rw-r--r--web/src/widget/progress_bar.rs116
-rw-r--r--web/src/widget/radio.rs155
-rw-r--r--web/src/widget/row.rs148
-rw-r--r--web/src/widget/scrollable.rs152
-rw-r--r--web/src/widget/slider.rs183
-rw-r--r--web/src/widget/space.rs63
-rw-r--r--web/src/widget/text.rs148
-rw-r--r--web/src/widget/text_input.rs234
-rw-r--r--web/src/widget/toggler.rs171
23 files changed, 0 insertions, 3197 deletions
diff --git a/web/src/bus.rs b/web/src/bus.rs
deleted file mode 100644
index 5ce8e810..00000000
--- a/web/src/bus.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use iced_futures::futures::channel::mpsc;
-use std::rc::Rc;
-
-/// A publisher of messages.
-///
-/// It can be used to route messages back to the [`Application`].
-///
-/// [`Application`]: crate::Application
-#[allow(missing_debug_implementations)]
-pub struct Bus<Message> {
- publish: Rc<Box<dyn Fn(Message) -> ()>>,
-}
-
-impl<Message> Clone for Bus<Message> {
- fn clone(&self) -> Self {
- Bus {
- publish: self.publish.clone(),
- }
- }
-}
-
-impl<Message> Bus<Message>
-where
- Message: 'static,
-{
- pub(crate) fn new(publish: mpsc::UnboundedSender<Message>) -> Self {
- Self {
- publish: Rc::new(Box::new(move |message| {
- publish.unbounded_send(message).expect("Send message");
- })),
- }
- }
-
- /// Publishes a new message for the [`Application`].
- ///
- /// [`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.
- pub fn map<B>(&self, mapper: Rc<Box<dyn Fn(B) -> Message>>) -> Bus<B>
- where
- B: 'static,
- {
- let publish = self.publish.clone();
-
- Bus {
- publish: Rc::new(Box::new(move |message| publish(mapper(message)))),
- }
- }
-}
diff --git a/web/src/command.rs b/web/src/command.rs
deleted file mode 100644
index 33e49e70..00000000
--- a/web/src/command.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-mod action;
-
-pub use action::Action;
-
-use std::fmt;
-
-#[cfg(target_arch = "wasm32")]
-use std::future::Future;
-
-/// A set of asynchronous actions to be performed by some runtime.
-pub struct Command<T>(iced_futures::Command<Action<T>>);
-
-impl<T> Command<T> {
- /// Creates an empty [`Command`].
- ///
- /// In other words, a [`Command`] that does nothing.
- pub const fn none() -> Self {
- Self(iced_futures::Command::none())
- }
-
- /// Creates a [`Command`] that performs a single [`Action`].
- pub const fn single(action: Action<T>) -> Self {
- Self(iced_futures::Command::single(action))
- }
-
- /// Creates a [`Command`] that performs the action of the given future.
- #[cfg(target_arch = "wasm32")]
- pub fn perform<A>(
- future: impl Future<Output = T> + 'static,
- f: impl Fn(T) -> A + 'static + Send,
- ) -> Command<A> {
- use iced_futures::futures::FutureExt;
-
- Command::single(Action::Future(Box::pin(future.map(f))))
- }
-
- /// Creates a [`Command`] that performs the actions of all the given
- /// commands.
- ///
- /// Once this command is run, all the commands will be executed at once.
- pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
- Self(iced_futures::Command::batch(
- commands.into_iter().map(|Command(command)| command),
- ))
- }
-
- /// Applies a transformation to the result of a [`Command`].
- #[cfg(target_arch = "wasm32")]
- pub fn map<A>(self, f: impl Fn(T) -> A + 'static + Clone) -> Command<A>
- where
- T: 'static,
- {
- let Command(command) = self;
-
- Command(command.map(move |action| action.map(f.clone())))
- }
-
- /// Returns all of the actions of the [`Command`].
- pub fn actions(self) -> Vec<Action<T>> {
- let Command(command) = self;
-
- command.actions()
- }
-}
-
-impl<T> fmt::Debug for Command<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let Command(command) = self;
-
- command.fmt(f)
- }
-}
diff --git a/web/src/command/action.rs b/web/src/command/action.rs
deleted file mode 100644
index c0223e50..00000000
--- a/web/src/command/action.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-pub enum Action<T> {
- Future(iced_futures::BoxFuture<T>),
-}
-
-use std::fmt;
-
-impl<T> Action<T> {
- /// Applies a transformation to the result of a [`Command`].
- #[cfg(target_arch = "wasm32")]
- pub fn map<A>(self, f: impl Fn(T) -> A + 'static) -> Action<A>
- where
- T: 'static,
- {
- use iced_futures::futures::FutureExt;
-
- match self {
- Self::Future(future) => Action::Future(Box::pin(future.map(f))),
- }
- }
-}
-
-impl<T> fmt::Debug for Action<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Future(_) => write!(f, "Action::Future"),
- }
- }
-}
diff --git a/web/src/css.rs b/web/src/css.rs
deleted file mode 100644
index 07589150..00000000
--- a/web/src/css.rs
+++ /dev/null
@@ -1,217 +0,0 @@
-//! Style your widgets.
-use crate::bumpalo;
-use crate::{Alignment, Background, Color, Length, Padding};
-
-use std::collections::BTreeMap;
-
-/// A CSS rule of a VDOM node.
-#[derive(Debug)]
-pub enum Rule {
- /// Container with vertical distribution
- Column,
-
- /// Container with horizonal distribution
- Row,
-
- /// Spacing between elements
- Spacing(u16),
-
- /// Toggler input for a specific size
- Toggler(u16),
-}
-
-impl Rule {
- /// Returns the class name of the [`Rule`].
- pub fn class<'a>(&self) -> String {
- match self {
- Rule::Column => String::from("c"),
- Rule::Row => String::from("r"),
- Rule::Spacing(spacing) => format!("s-{}", spacing),
- Rule::Toggler(size) => format!("toggler-{}", size),
- }
- }
-
- /// Returns the declaration of the [`Rule`].
- pub fn declaration<'a>(&self, bump: &'a bumpalo::Bump) -> &'a str {
- let class = self.class();
-
- match self {
- Rule::Column => {
- let body = "{ display: flex; flex-direction: column; }";
-
- bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
- }
- Rule::Row => {
- let body = "{ display: flex; flex-direction: row; }";
-
- bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
- }
- Rule::Spacing(spacing) => bumpalo::format!(
- in bump,
- ".c.{} > * {{ margin-bottom: {}px }} \
- .r.{} > * {{ margin-right: {}px }} \
- .c.{} > *:last-child {{ margin-bottom: 0 }} \
- .r.{} > *:last-child {{ margin-right: 0 }}",
- class,
- spacing,
- class,
- spacing,
- class,
- 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(),
- }
- }
-}
-
-/// A cascading style sheet.
-#[derive(Debug)]
-pub struct Css<'a> {
- rules: BTreeMap<String, &'a str>,
-}
-
-impl<'a> Css<'a> {
- /// Creates an empty [`Css`].
- pub fn new() -> Self {
- Css {
- rules: BTreeMap::new(),
- }
- }
-
- /// Inserts the [`Rule`] in the [`Css`], if it was not previously
- /// inserted.
- ///
- /// It returns the class name of the provided [`Rule`].
- pub fn insert(&mut self, bump: &'a bumpalo::Bump, rule: Rule) -> String {
- let class = rule.class();
-
- if !self.rules.contains_key(&class) {
- let _ = self.rules.insert(class.clone(), rule.declaration(bump));
- }
-
- class
- }
-
- /// Produces the VDOM node of the [`Css`].
- pub fn node(self, bump: &'a bumpalo::Bump) -> dodrio::Node<'a> {
- use dodrio::builder::*;
-
- let mut declarations = bumpalo::collections::Vec::new_in(bump);
-
- declarations.push(text("html { height: 100% }"));
- declarations.push(text(
- "body { height: 100%; margin: 0; padding: 0; font-family: sans-serif }",
- ));
- declarations.push(text("* { margin: 0; padding: 0 }"));
- declarations.push(text(
- "button { border: none; cursor: pointer; outline: none }",
- ));
-
- for declaration in self.rules.values() {
- declarations.push(text(*declaration));
- }
-
- style(bump).children(declarations).finish()
- }
-}
-
-/// Returns the style value for the given [`Length`].
-pub fn length(length: Length) -> String {
- match length {
- Length::Shrink => String::from("auto"),
- Length::Units(px) => format!("{}px", px),
- Length::Fill | Length::FillPortion(_) => String::from("100%"),
- }
-}
-
-/// Returns the style value for the given maximum length in units.
-pub fn max_length(units: u32) -> String {
- use std::u32;
-
- if units == u32::MAX {
- String::from("initial")
- } else {
- format!("{}px", units)
- }
-}
-
-/// Returns the style value for the given minimum length in units.
-pub fn min_length(units: u32) -> String {
- if units == 0 {
- String::from("initial")
- } else {
- format!("{}px", units)
- }
-}
-
-/// Returns the style value for the given [`Color`].
-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`].
-pub fn background(background: Background) -> String {
- match background {
- Background::Color(c) => color(c),
- }
-}
-
-/// Returns the style value for the given [`Alignment`].
-pub fn alignment(alignment: Alignment) -> &'static str {
- match alignment {
- Alignment::Start => "flex-start",
- Alignment::Center => "center",
- Alignment::End => "flex-end",
- Alignment::Fill => "stretch",
- }
-}
-
-/// 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/element.rs b/web/src/element.rs
deleted file mode 100644
index 6bb90177..00000000
--- a/web/src/element.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use crate::{Bus, Color, Css, Widget};
-
-use dodrio::bumpalo;
-use std::rc::Rc;
-
-/// A generic [`Widget`].
-///
-/// It is useful to build composable user interfaces that do not leak
-/// implementation details in their __view logic__.
-///
-/// If you have a [built-in widget], you should be able to use `Into<Element>`
-/// to turn it into an [`Element`].
-///
-/// [built-in widget]: mod@crate::widget
-#[allow(missing_debug_implementations)]
-pub struct Element<'a, Message> {
- pub(crate) widget: Box<dyn Widget<Message> + 'a>,
-}
-
-impl<'a, Message> Element<'a, Message> {
- /// Create a new [`Element`] containing the given [`Widget`].
- pub fn new(widget: impl Widget<Message> + 'a) -> Self {
- Self {
- widget: Box::new(widget),
- }
- }
-
- /// Applies a transformation to the produced message of the [`Element`].
- ///
- /// This method is useful when you want to decouple different parts of your
- /// UI and make them __composable__.
- pub fn map<F, B>(self, f: F) -> Element<'a, B>
- where
- Message: 'static,
- B: 'static,
- F: 'static + Fn(Message) -> B,
- {
- Element {
- widget: Box::new(Map::new(self.widget, f)),
- }
- }
-
- /// Marks the [`Element`] as _to-be-explained_.
- pub fn explain(self, _color: Color) -> Element<'a, Message> {
- self
- }
-
- /// Produces a VDOM node for the [`Element`].
- pub fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- bus: &Bus<Message>,
- style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- self.widget.node(bump, bus, style_sheet)
- }
-}
-
-struct Map<'a, A, B> {
- widget: Box<dyn Widget<A> + 'a>,
- mapper: Rc<Box<dyn Fn(A) -> B>>,
-}
-
-impl<'a, A, B> Map<'a, A, B> {
- pub fn new<F>(widget: Box<dyn Widget<A> + 'a>, mapper: F) -> Map<'a, A, B>
- where
- F: 'static + Fn(A) -> B,
- {
- Map {
- widget,
- mapper: Rc::new(Box::new(mapper)),
- }
- }
-}
-
-impl<'a, A, B> Widget<B> for Map<'a, A, B>
-where
- A: 'static,
- B: 'static,
-{
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- bus: &Bus<B>,
- style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- self.widget
- .node(bump, &bus.map(self.mapper.clone()), style_sheet)
- }
-}
diff --git a/web/src/hasher.rs b/web/src/hasher.rs
deleted file mode 100644
index 1a28a2f9..00000000
--- a/web/src/hasher.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use std::collections::hash_map::DefaultHasher;
-
-/// The hasher used to compare subscriptions.
-#[derive(Debug)]
-pub struct Hasher(DefaultHasher);
-
-impl Default for Hasher {
- fn default() -> Self {
- Hasher(DefaultHasher::default())
- }
-}
-
-impl core::hash::Hasher for Hasher {
- fn write(&mut self, bytes: &[u8]) {
- self.0.write(bytes)
- }
-
- fn finish(&self) -> u64 {
- self.0.finish()
- }
-}
diff --git a/web/src/lib.rs b/web/src/lib.rs
deleted file mode 100644
index 6311dd12..00000000
--- a/web/src/lib.rs
+++ /dev/null
@@ -1,431 +0,0 @@
-//! 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.
-//!
-//! The crate is currently a __very experimental__, simple abstraction layer
-//! over [`dodrio`].
-//!
-//! [`iced_core`]: https://github.com/iced-rs/iced/tree/master/core
-//! [`dodrio`]: https://github.com/fitzgen/dodrio
-//!
-//! # Usage
-//! The current build process is a bit involved, as [`wasm-pack`] does not
-//! currently [support building binary crates](https://github.com/rustwasm/wasm-pack/issues/734).
-//!
-//! Therefore, we instead build using the `wasm32-unknown-unknown` target and
-//! use the [`wasm-bindgen`] CLI to generate appropriate bindings.
-//!
-//! For instance, let's say we want to build the [`tour` example]:
-//!
-//! ```bash
-//! cd examples
-//! 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:
-//!
-//! ```html
-//! <!DOCTYPE html>
-//! <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>
-//! <script type="module">
-//! import init from "./tour/tour.js";
-//!
-//! init('./tour/tour_bg.wasm');
-//! </script>
-//! </body>
-//! </html>
-//! ```
-//!
-//! Finally, we serve it using an HTTP server and access it with our browser.
-//!
-//! [`wasm-pack`]: https://github.com/rustwasm/wasm-pack
-//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen
-//! [`tour` example]: https://github.com/iced-rs/iced/tree/0.3/examples/tour
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
-)]
-#![deny(missing_docs)]
-#![deny(missing_debug_implementations)]
-#![deny(unused_results)]
-#![forbid(unsafe_code)]
-#![forbid(rust_2018_idioms)]
-use dodrio::bumpalo;
-use std::{cell::RefCell, rc::Rc};
-
-mod bus;
-mod command;
-mod element;
-mod hasher;
-
-pub mod css;
-pub mod subscription;
-pub mod widget;
-
-pub use bus::Bus;
-pub use command::Command;
-pub use css::Css;
-pub use dodrio;
-pub use element::Element;
-pub use hasher::Hasher;
-pub use subscription::Subscription;
-
-pub use iced_core::alignment;
-pub use iced_core::keyboard;
-pub use iced_core::mouse;
-pub use iced_futures::executor;
-pub use iced_futures::futures;
-
-pub use iced_core::{
- Alignment, Background, Color, Font, Length, Padding, Point, Rectangle,
- Size, Vector,
-};
-
-#[doc(no_inline)]
-pub use widget::*;
-
-#[doc(no_inline)]
-pub use executor::Executor;
-
-/// An interactive web application.
-///
-/// This trait is the main entrypoint of Iced. Once implemented, you can run
-/// your GUI application by simply calling [`run`](#method.run). It will take
-/// control of the `<title>` and the `<body>` of the document.
-///
-/// 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.
- type Message: Send;
-
- /// 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`] 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;
-
- /// Returns the current title of the [`Application`].
- ///
- /// This title can be dynamic! The runtime will automatically update the
- /// title of your application when necessary.
- fn title(&self) -> String;
-
- /// Handles a __message__ and updates the state of the [`Application`].
- ///
- /// This is where you define your __update logic__. All the __messages__,
- /// produced by either user interactions or commands, will be handled by
- /// this method.
- ///
- /// Any [`Command`] returned will be executed immediately in the background.
- 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.
- fn view(&mut self) -> Element<'_, Self::Message>;
-
- /// Returns the event [`Subscription`] for the current state of the
- /// application.
- ///
- /// A [`Subscription`] will be kept alive as long as you keep returning it,
- /// and the __messages__ produced will be handled by
- /// [`update`](#tymethod.update).
- ///
- /// By default, this method returns an empty [`Subscription`].
- fn subscription(&self) -> Subscription<Self::Message> {
- Subscription::none()
- }
-
- /// Runs the [`Application`].
- fn run(flags: Self::Flags)
- where
- Self: 'static + Sized,
- {
- use futures::stream::StreamExt;
-
- let window = web_sys::window().unwrap();
- let document = window.document().unwrap();
- let body = document.body().unwrap();
-
- let (sender, receiver) =
- iced_futures::futures::channel::mpsc::unbounded();
-
- let mut runtime = iced_futures::Runtime::new(
- 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);
-
- run_command(command, &mut runtime);
-
- let application = Rc::new(RefCell::new(app));
-
- let instance = Instance {
- application: application.clone(),
- bus: Bus::new(sender),
- };
-
- let vdom = dodrio::Vdom::new(&body, instance);
-
- let event_loop = receiver.for_each(move |message| {
- 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();
-
- run_command(command, &mut runtime);
- runtime.track(subscription);
-
- if title != new_title {
- document.set_title(&new_title);
-
- title = new_title;
- }
-
- vdom.weak().schedule_render();
-
- futures::future::ready(())
- });
-
- wasm_bindgen_futures::spawn_local(event_loop);
- }
-}
-
-struct Instance<A: Application> {
- application: Rc<RefCell<A>>,
- bus: Bus<A::Message>,
-}
-
-impl<'a, A> dodrio::Render<'a> for Instance<A>
-where
- A: Application,
-{
- 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(context.bump, &self.bus, &mut css);
-
- div(context.bump)
- .attr("style", "width: 100%; height: 100%")
- .children(vec![css.node(context.bump), node])
- .finish()
- }
-}
-
-/// An interactive embedded web application.
-///
-/// This trait is the main entrypoint of Iced. Once implemented, you can run
-/// your GUI application by simply calling [`run`](#method.run). It will either
-/// take control of the `<body>' or of an HTML element of the document specified
-/// by `container_id`.
-///
-/// An [`Embedded`](trait.Embedded.html) can execute asynchronous actions
-/// by returning a [`Command`](struct.Command.html) in some of its methods.
-pub trait Embedded {
- /// 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 type of __messages__ your [`Embedded`] application will produce.
- ///
- /// [`Embedded`]: trait.Embedded.html
- type Message: Send;
-
- /// The data needed to initialize your [`Embedded`] application.
- ///
- /// [`Embedded`]: trait.Embedded.html
- type Flags;
-
- /// Initializes the [`Embedded`] 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.
- ///
- /// [`Embedded`]: trait.Embedded.html
- fn new(flags: Self::Flags) -> (Self, Command<Self::Message>)
- where
- Self: Sized;
-
- /// Handles a __message__ and updates the state of the [`Embedded`]
- /// application.
- ///
- /// This is where you define your __update logic__. All the __messages__,
- /// produced by either user interactions or commands, will be handled by
- /// this method.
- ///
- /// Any [`Command`] returned will be executed immediately in the background.
- ///
- /// [`Embedded`]: trait.Embedded.html
- /// [`Command`]: struct.Command.html
- fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
-
- /// Returns the widgets to display in the [`Embedded`] application.
- ///
- /// These widgets can produce __messages__ based on user interaction.
- ///
- /// [`Embedded`]: trait.Embedded.html
- fn view(&mut self) -> Element<'_, Self::Message>;
-
- /// Returns the event [`Subscription`] for the current state of the embedded
- /// application.
- ///
- /// A [`Subscription`] will be kept alive as long as you keep returning it,
- /// and the __messages__ produced will be handled by
- /// [`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 [`Embedded`] application.
- ///
- /// [`Embedded`]: trait.Embedded.html
- fn run(flags: Self::Flags, container_id: Option<String>)
- where
- Self: 'static + Sized,
- {
- use futures::stream::StreamExt;
- use wasm_bindgen::JsCast;
- use web_sys::HtmlElement;
-
- let window = web_sys::window().unwrap();
- let document = window.document().unwrap();
- let container: HtmlElement = container_id
- .map(|id| document.get_element_by_id(&id).unwrap())
- .map(|container| {
- container.dyn_ref::<HtmlElement>().unwrap().to_owned()
- })
- .unwrap_or_else(|| document.body().unwrap());
-
- let (sender, receiver) =
- iced_futures::futures::channel::mpsc::unbounded();
-
- let mut runtime = iced_futures::Runtime::new(
- Self::Executor::new().expect("Create executor"),
- sender.clone(),
- );
-
- let (app, command) = runtime.enter(|| Self::new(flags));
- run_command(command, &mut runtime);
-
- let application = Rc::new(RefCell::new(app));
-
- let instance = EmbeddedInstance {
- application: application.clone(),
- bus: Bus::new(sender),
- };
-
- let vdom = dodrio::Vdom::new(&container, instance);
-
- let event_loop = receiver.for_each(move |message| {
- let (command, subscription) = runtime.enter(|| {
- let command = application.borrow_mut().update(message);
- let subscription = application.borrow().subscription();
-
- (command, subscription)
- });
-
- run_command(command, &mut runtime);
- runtime.track(subscription);
-
- vdom.weak().schedule_render();
-
- futures::future::ready(())
- });
-
- wasm_bindgen_futures::spawn_local(event_loop);
- }
-}
-
-fn run_command<Message: 'static + Send, E: Executor>(
- command: Command<Message>,
- runtime: &mut iced_futures::Runtime<
- Hasher,
- (),
- E,
- iced_futures::futures::channel::mpsc::UnboundedSender<Message>,
- Message,
- >,
-) {
- for action in command.actions() {
- match action {
- command::Action::Future(future) => {
- runtime.spawn(future);
- }
- }
- }
-}
-
-struct EmbeddedInstance<A: Embedded> {
- application: Rc<RefCell<A>>,
- bus: Bus<A::Message>,
-}
-
-impl<'a, A> dodrio::Render<'a> for EmbeddedInstance<A>
-where
- A: Embedded,
-{
- 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(context.bump, &self.bus, &mut css);
-
- div(context.bump)
- .attr("style", "width: 100%; height: 100%")
- .children(vec![css.node(context.bump), node])
- .finish()
- }
-}
diff --git a/web/src/subscription.rs b/web/src/subscription.rs
deleted file mode 100644
index fb54f7e3..00000000
--- a/web/src/subscription.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//! Listen to external events in your application.
-use crate::Hasher;
-
-/// A request to listen to external events.
-///
-/// Besides performing async actions on demand with [`Command`], most
-/// applications also need to listen to external events passively.
-///
-/// A [`Subscription`] is normally provided to some runtime, like a [`Command`],
-/// and it will generate events as long as the user keeps requesting it.
-///
-/// For instance, you can use a [`Subscription`] to listen to a WebSocket
-/// connection, keyboard presses, mouse events, time ticks, etc.
-///
-/// [`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
deleted file mode 100644
index 4cb0a9cc..00000000
--- a/web/src/widget.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-//! Use the built-in widgets or create your own.
-//!
-//! # Custom widgets
-//! If you want to implement a custom widget, you simply need to implement the
-//! [`Widget`] trait. You can use the API of the built-in widgets as a guide or
-//! source of inspiration.
-//!
-//! # Re-exports
-//! For convenience, the contents of this module are available at the root
-//! module. Therefore, you can directly type:
-//!
-//! ```
-//! use iced_web::{button, Button, Widget};
-//! ```
-use crate::{Bus, Css};
-use dodrio::bumpalo;
-
-pub mod button;
-pub mod checkbox;
-pub mod container;
-pub mod image;
-pub mod progress_bar;
-pub mod radio;
-pub mod scrollable;
-pub mod slider;
-pub mod text_input;
-pub mod toggler;
-
-mod column;
-mod row;
-mod space;
-mod text;
-
-#[doc(no_inline)]
-pub use button::Button;
-#[doc(no_inline)]
-pub use scrollable::Scrollable;
-#[doc(no_inline)]
-pub use slider::Slider;
-#[doc(no_inline)]
-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;
-pub use container::Container;
-pub use image::Image;
-pub use progress_bar::ProgressBar;
-pub use radio::Radio;
-pub use row::Row;
-pub use space::Space;
-
-/// A component that displays information and allows interaction.
-///
-/// If you want to build your own widgets, you will need to implement this
-/// trait.
-pub trait Widget<Message> {
- /// Produces a VDOM node for the [`Widget`].
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- _bus: &Bus<Message>,
- style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b>;
-}
diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs
deleted file mode 100644
index 88137607..00000000
--- a/web/src/widget/button.rs
+++ /dev/null
@@ -1,192 +0,0 @@
-//! Allow your users to perform actions by pressing a button.
-//!
-//! A [`Button`] has some local [`State`].
-use crate::{css, Background, Bus, Css, Element, Length, Padding, Widget};
-
-pub use iced_style::button::{Style, StyleSheet};
-
-use dodrio::bumpalo;
-
-/// A generic widget that produces a message when pressed.
-///
-/// ```
-/// # use iced_web::{button, Button, Text};
-/// #
-/// enum Message {
-/// ButtonPressed,
-/// }
-///
-/// let mut state = button::State::new();
-/// 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: Padding,
- style: Box<dyn StyleSheet + 'a>,
-}
-
-impl<'a, Message> Button<'a, Message> {
- /// Creates a new [`Button`] with some local [`State`] and the given
- /// content.
- pub fn new<E>(_state: &'a mut State, content: E) -> Self
- where
- E: Into<Element<'a, Message>>,
- {
- Button {
- content: content.into(),
- on_press: None,
- width: Length::Shrink,
- height: Length::Shrink,
- min_width: 0,
- min_height: 0,
- padding: Padding::new(5),
- style: Default::default(),
- }
- }
-
- /// Sets the width of the [`Button`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Button`].
- pub fn height(mut self, height: Length) -> Self {
- self.height = height;
- self
- }
-
- /// Sets the minimum width of the [`Button`].
- pub fn min_width(mut self, min_width: u32) -> Self {
- self.min_width = min_width;
- self
- }
-
- /// Sets the minimum height of the [`Button`].
- pub fn min_height(mut self, min_height: u32) -> Self {
- self.min_height = min_height;
- self
- }
-
- /// Sets the [`Padding`] of the [`Button`].
- pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
- self.padding = padding.into();
- self
- }
-
- /// Sets the style of the [`Button`].
- pub fn style(mut self, style: impl Into<Box<dyn StyleSheet + 'a>>) -> Self {
- self.style = style.into();
- self
- }
-
- /// 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
- }
-}
-
-/// The local state of a [`Button`].
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub struct State;
-
-impl State {
- /// Creates a new [`State`].
- pub fn new() -> State {
- State::default()
- }
-}
-
-impl<'a, Message> Widget<Message> for Button<'a, Message>
-where
- Message: 'static + Clone,
-{
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- bus: &Bus<Message>,
- style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
-
- // TODO: State-based styling
- let style = self.style.active();
-
- let background = match style.background {
- None => String::from("none"),
- Some(background) => match background {
- Background::Color(color) => css::color(color),
- },
- };
-
- let mut node = button(bump)
- .attr(
- "style",
- bumpalo::format!(
- in bump,
- "background: {}; border-radius: {}px; width:{}; \
- min-width: {}; color: {}; padding: {}",
- background,
- style.border_radius,
- css::length(self.width),
- css::min_length(self.min_width),
- css::color(style.text_color),
- css::padding(self.padding)
- )
- .into_bump_str(),
- )
- .children(vec![self.content.node(bump, bus, style_sheet)]);
-
- if let Some(on_press) = self.on_press.clone() {
- let event_bus = bus.clone();
-
- node = node.on("click", move |_root, _vdom, _event| {
- event_bus.publish(on_press.clone());
- });
- } else {
- node = node.attr("disabled", "");
- }
-
- node.finish()
- }
-}
-
-impl<'a, Message> From<Button<'a, Message>> for Element<'a, Message>
-where
- Message: 'static + Clone,
-{
- fn from(button: Button<'a, Message>) -> Element<'a, Message> {
- Element::new(button)
- }
-}
diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs
deleted file mode 100644
index 844bf862..00000000
--- a/web/src/widget/checkbox.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-//! Show toggle controls using checkboxes.
-use crate::{css, Bus, Css, Element, Length, Widget};
-
-pub use iced_style::checkbox::{Style, StyleSheet};
-
-use dodrio::bumpalo;
-use std::rc::Rc;
-
-/// A box that can be checked.
-///
-/// # Example
-///
-/// ```
-/// # use iced_web::Checkbox;
-///
-/// pub enum Message {
-/// CheckboxToggled(bool),
-/// }
-///
-/// let is_checked = true;
-///
-/// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled);
-/// ```
-///
-/// ![Checkbox drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/checkbox.png?raw=true)
-#[allow(missing_debug_implementations)]
-pub struct Checkbox<'a, Message> {
- is_checked: bool,
- on_toggle: Rc<dyn Fn(bool) -> Message>,
- label: String,
- id: Option<String>,
- width: Length,
- #[allow(dead_code)]
- style_sheet: Box<dyn StyleSheet + 'a>,
-}
-
-impl<'a, Message> Checkbox<'a, Message> {
- /// Creates a new [`Checkbox`].
- ///
- /// It expects:
- /// * a boolean describing whether the [`Checkbox`] is checked or not
- /// * the label of the [`Checkbox`]
- /// * 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`.
- 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: label.into(),
- id: None,
- width: Length::Shrink,
- style_sheet: Default::default(),
- }
- }
-
- /// Sets the width of the [`Checkbox`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the style of the [`Checkbox`].
- pub fn style(
- mut self,
- style_sheet: impl Into<Box<dyn StyleSheet + 'a>>,
- ) -> Self {
- self.style_sheet = style_sheet.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<'a, Message> Widget<Message> for Checkbox<'a, 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 checkbox_label =
- String::from_str_in(&self.label, bump).into_bump_str();
-
- let event_bus = bus.clone();
- let on_toggle = self.on_toggle.clone();
- let is_checked = self.is_checked;
-
- let row_class = style_sheet.insert(bump, css::Rule::Row);
-
- let spacing_class = style_sheet.insert(bump, css::Rule::Spacing(5));
-
- 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)
- .into_bump_str(),
- )
- .attr(
- "style",
- bumpalo::format!(in bump, "width: {}; align-items: center", css::length(self.width))
- .into_bump_str(),
- )
- .children(vec![
- // TODO: Checkbox styling
- input
- .attr("type", "checkbox")
- .bool_attr("checked", self.is_checked)
- .on("click", move |_root, vdom, _event| {
- let msg = on_toggle(!is_checked);
- event_bus.publish(msg);
-
- vdom.schedule_render();
- })
- .finish(),
- text(checkbox_label),
- ])
- .finish()
- }
-}
-
-impl<'a, Message> From<Checkbox<'a, Message>> for Element<'a, Message>
-where
- Message: 'static,
-{
- fn from(checkbox: Checkbox<'a, Message>) -> Element<'a, Message> {
- Element::new(checkbox)
- }
-}
diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs
deleted file mode 100644
index 30a57c41..00000000
--- a/web/src/widget/column.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-use crate::css;
-use crate::{Alignment, Bus, Css, Element, Length, Padding, Widget};
-
-use dodrio::bumpalo;
-use std::u32;
-
-/// A container that distributes its contents vertically.
-///
-/// A [`Column`] will try to fill the horizontal space of its container.
-#[allow(missing_debug_implementations)]
-pub struct Column<'a, Message> {
- spacing: u16,
- padding: Padding,
- width: Length,
- height: Length,
- max_width: u32,
- max_height: u32,
- align_items: Alignment,
- children: Vec<Element<'a, Message>>,
-}
-
-impl<'a, Message> Column<'a, Message> {
- /// Creates an empty [`Column`].
- pub fn new() -> Self {
- Self::with_children(Vec::new())
- }
-
- /// Creates a [`Column`] with the given elements.
- pub fn with_children(children: Vec<Element<'a, Message>>) -> Self {
- Column {
- spacing: 0,
- padding: Padding::ZERO,
- width: Length::Fill,
- height: Length::Shrink,
- max_width: u32::MAX,
- max_height: u32::MAX,
- align_items: Alignment::Start,
- children,
- }
- }
-
- /// Sets the vertical spacing _between_ elements.
- ///
- /// Custom margins per element do not exist in Iced. You should use this
- /// method instead! While less flexible, it helps you keep spacing between
- /// elements consistent.
- pub fn spacing(mut self, units: u16) -> Self {
- self.spacing = units;
- self
- }
-
- /// Sets the [`Padding`] of the [`Column`].
- pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
- self.padding = padding.into();
- self
- }
-
- /// Sets the width of the [`Column`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Column`].
- pub fn height(mut self, height: Length) -> Self {
- self.height = height;
- self
- }
-
- /// Sets the maximum width of the [`Column`].
- 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.
- 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`] .
- pub fn align_items(mut self, align: Alignment) -> Self {
- self.align_items = align;
- self
- }
-
- /// Adds an element to the [`Column`].
- pub fn push<E>(mut self, child: E) -> Self
- where
- E: Into<Element<'a, Message>>,
- {
- self.children.push(child.into());
- self
- }
-}
-
-impl<'a, Message> Widget<Message> for Column<'a, Message> {
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- publish: &Bus<Message>,
- style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
-
- let children: Vec<_> = self
- .children
- .iter()
- .map(|element| element.widget.node(bump, publish, style_sheet))
- .collect();
-
- let column_class = style_sheet.insert(bump, css::Rule::Column);
-
- let spacing_class =
- style_sheet.insert(bump, css::Rule::Spacing(self.spacing));
-
- // TODO: Complete styling
- div(bump)
- .attr(
- "class",
- bumpalo::format!(in bump, "{} {}", column_class, spacing_class)
- .into_bump_str(),
- )
- .attr("style", bumpalo::format!(
- in bump,
- "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::alignment(self.align_items)
- ).into_bump_str()
- )
- .children(children)
- .finish()
- }
-}
-
-impl<'a, Message> From<Column<'a, Message>> for Element<'a, Message>
-where
- Message: 'static,
-{
- fn from(column: Column<'a, Message>) -> Element<'a, Message> {
- Element::new(column)
- }
-}
diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs
deleted file mode 100644
index 8e345b9a..00000000
--- a/web/src/widget/container.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-//! Decorate content and apply alignment.
-use crate::alignment::{self, Alignment};
-use crate::bumpalo;
-use crate::css;
-use crate::{Bus, Css, Element, Length, Padding, Widget};
-
-pub use iced_style::container::{Style, StyleSheet};
-
-/// An element decorating some content.
-///
-/// It is normally used for alignment purposes.
-#[allow(missing_debug_implementations)]
-pub struct Container<'a, Message> {
- padding: Padding,
- width: Length,
- height: Length,
- max_width: u32,
- #[allow(dead_code)]
- max_height: u32,
- horizontal_alignment: alignment::Horizontal,
- vertical_alignment: alignment::Vertical,
- style_sheet: Box<dyn StyleSheet + 'a>,
- content: Element<'a, Message>,
-}
-
-impl<'a, Message> Container<'a, Message> {
- /// Creates an empty [`Container`].
- pub fn new<T>(content: T) -> Self
- where
- T: Into<Element<'a, Message>>,
- {
- use std::u32;
-
- Container {
- padding: Padding::ZERO,
- width: Length::Shrink,
- height: Length::Shrink,
- max_width: u32::MAX,
- max_height: u32::MAX,
- horizontal_alignment: alignment::Horizontal::Left,
- vertical_alignment: alignment::Vertical::Top,
- style_sheet: Default::default(),
- content: content.into(),
- }
- }
-
- /// Sets the [`Padding`] of the [`Container`].
- pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
- self.padding = padding.into();
- self
- }
-
- /// Sets the width of the [`Container`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Container`].
- pub fn height(mut self, height: Length) -> Self {
- self.height = height;
- self
- }
-
- /// Sets the maximum width of the [`Container`].
- 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.
- 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`].
- pub fn center_x(mut self) -> Self {
- self.horizontal_alignment = alignment::Horizontal::Center;
-
- self
- }
-
- /// Centers the contents in the vertical axis of the [`Container`].
- pub fn center_y(mut self) -> Self {
- self.vertical_alignment = alignment::Vertical::Center;
-
- self
- }
-
- /// Sets the style of the [`Container`].
- pub fn style(mut self, style: impl Into<Box<dyn StyleSheet + 'a>>) -> Self {
- self.style_sheet = style.into();
- self
- }
-}
-
-impl<'a, Message> Widget<Message> for Container<'a, 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::*;
-
- let column_class = style_sheet.insert(bump, css::Rule::Column);
-
- let style = self.style_sheet.style();
-
- let node = div(bump)
- .attr(
- "class",
- bumpalo::format!(in bump, "{}", column_class).into_bump_str(),
- )
- .attr(
- "style",
- bumpalo::format!(
- in bump,
- "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::alignment(Alignment::from(self.horizontal_alignment)),
- css::alignment(Alignment::from(self.vertical_alignment)),
- style.background.map(css::background).unwrap_or(String::from("initial")),
- style.text_color.map(css::color).unwrap_or(String::from("inherit")),
- style.border_width,
- css::color(style.border_color),
- style.border_radius
- )
- .into_bump_str(),
- )
- .children(vec![self.content.node(bump, bus, style_sheet)]);
-
- // TODO: Complete styling
-
- node.finish()
- }
-}
-
-impl<'a, Message> From<Container<'a, Message>> for Element<'a, Message>
-where
- Message: 'static,
-{
- fn from(container: Container<'a, Message>) -> Element<'a, Message> {
- Element::new(container)
- }
-}
diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs
deleted file mode 100644
index 28435f4f..00000000
--- a/web/src/widget/image.rs
+++ /dev/null
@@ -1,186 +0,0 @@
-//! Display images in your user interface.
-use crate::{Bus, Css, Element, Hasher, Length, Widget};
-
-use dodrio::bumpalo;
-use std::{
- hash::{Hash, Hasher as _},
- path::PathBuf,
- sync::Arc,
-};
-
-/// A frame that displays an image while keeping aspect ratio.
-///
-/// # Example
-///
-/// ```
-/// # use iced_web::Image;
-///
-/// let image = Image::new("resources/ferris.png");
-/// ```
-#[derive(Debug)]
-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,
-
- /// The height of the image
- pub height: Length,
-}
-
-impl Image {
- /// Creates a new [`Image`] with the given path.
- 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.
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Image`] boundaries.
- 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 {
- 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 src = match self.handle.data.as_ref() {
- Data::Path(path) => {
- String::from_str_in(path.to_str().unwrap_or(""), bump)
- }
- Data::Bytes(bytes) => {
- // The web is able to infer the kind of image, so we don't have to add a dependency on image-rs to guess the mime type.
- bumpalo::format!(in bump, "data:;base64,{}", base64::encode(bytes))
- },
- }
- .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 => {}
- Length::Fill | Length::FillPortion(_) => {
- image = image.attr("width", "100%");
- }
- Length::Units(px) => {
- image = image.attr(
- "width",
- bumpalo::format!(in bump, "{}px", px).into_bump_str(),
- );
- }
- }
-
- // TODO: Complete styling
-
- image.finish()
- }
-}
-
-impl<'a, Message> From<Image> for Element<'a, Message> {
- fn from(image: Image) -> Element<'a, Message> {
- Element::new(image)
- }
-}
-
-/// An [`Image`] handle.
-#[derive(Debug, Clone)]
-pub struct Handle {
- id: u64,
- data: Arc<Data>,
-}
-
-impl Handle {
- /// Creates an image [`Handle`] pointing to the image of the given path.
- pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle {
- Self::from_data(Data::Path(path.into()))
- }
-
- /// Creates an image [`Handle`] containing the image data directly.
- ///
- /// This is useful if you already have your image loaded in-memory, maybe
- /// because you downloaded or generated it procedurally.
- pub fn from_memory(bytes: Vec<u8>) -> Handle {
- Self::from_data(Data::Bytes(bytes))
- }
-
- fn from_data(data: Data) -> Handle {
- let mut hasher = Hasher::default();
- data.hash(&mut hasher);
-
- Handle {
- id: hasher.finish(),
- data: Arc::new(data),
- }
- }
-
- /// Returns the unique identifier of the [`Handle`].
- pub fn id(&self) -> u64 {
- self.id
- }
-
- /// Returns a reference to the image [`Data`].
- pub fn data(&self) -> &Data {
- &self.data
- }
-}
-
-impl From<String> for Handle {
- fn from(path: String) -> Handle {
- Handle::from_path(path)
- }
-}
-
-impl From<&str> for Handle {
- fn from(path: &str) -> Handle {
- Handle::from_path(path)
- }
-}
-
-/// The data of an [`Image`].
-#[derive(Clone, Hash)]
-pub enum Data {
- /// A remote image
- Path(PathBuf),
-
- /// In-memory data
- Bytes(Vec<u8>),
-}
-
-impl std::fmt::Debug for Data {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Data::Path(path) => write!(f, "Path({:?})", path),
- Data::Bytes(_) => write!(f, "Bytes(...)"),
- }
- }
-}
diff --git a/web/src/widget/progress_bar.rs b/web/src/widget/progress_bar.rs
deleted file mode 100644
index 01f412f8..00000000
--- a/web/src/widget/progress_bar.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-//! Provide progress feedback to your users.
-use crate::{bumpalo, css, Bus, Css, Element, Length, Widget};
-
-pub use iced_style::progress_bar::{Style, StyleSheet};
-
-use std::ops::RangeInclusive;
-
-/// A bar that displays progress.
-///
-/// # Example
-/// ```
-/// use iced_web::ProgressBar;
-///
-/// let value = 50.0;
-///
-/// ProgressBar::new(0.0..=100.0, value);
-/// ```
-///
-/// ![Progress bar](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
-#[allow(missing_debug_implementations)]
-pub struct ProgressBar<'a> {
- range: RangeInclusive<f32>,
- value: f32,
- width: Length,
- height: Option<Length>,
- style: Box<dyn StyleSheet + 'a>,
-}
-
-impl<'a> ProgressBar<'a> {
- /// Creates a new [`ProgressBar`].
- ///
- /// It expects:
- /// * an inclusive range of possible values
- /// * the current value of the [`ProgressBar`]
- pub fn new(range: RangeInclusive<f32>, value: f32) -> Self {
- ProgressBar {
- value: value.max(*range.start()).min(*range.end()),
- range,
- width: Length::Fill,
- height: None,
- style: Default::default(),
- }
- }
-
- /// Sets the width of the [`ProgressBar`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`ProgressBar`].
- pub fn height(mut self, height: Length) -> Self {
- self.height = Some(height);
- self
- }
-
- /// Sets the style of the [`ProgressBar`].
- pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self {
- self.style = style.into();
- self
- }
-}
-
-impl<'a, Message> Widget<Message> for ProgressBar<'a> {
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- _bus: &Bus<Message>,
- _style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
-
- let (range_start, range_end) = self.range.clone().into_inner();
- let amount_filled =
- (self.value - range_start) / (range_end - range_start).max(1.0);
-
- let style = self.style.style();
-
- let bar = div(bump)
- .attr(
- "style",
- bumpalo::format!(
- in bump,
- "width: {}%; height: 100%; background: {}",
- amount_filled * 100.0,
- css::background(style.bar)
- )
- .into_bump_str(),
- )
- .finish();
-
- let node = div(bump).attr(
- "style",
- bumpalo::format!(
- in bump,
- "width: {}; height: {}; background: {}; border-radius: {}px; overflow: hidden;",
- css::length(self.width),
- css::length(self.height.unwrap_or(Length::Units(30))),
- css::background(style.background),
- style.border_radius
- )
- .into_bump_str(),
- ).children(vec![bar]);
-
- node.finish()
- }
-}
-
-impl<'a, Message> From<ProgressBar<'a>> for Element<'a, Message>
-where
- Message: 'static,
-{
- fn from(container: ProgressBar<'a>) -> Element<'a, Message> {
- Element::new(container)
- }
-}
diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs
deleted file mode 100644
index 03b2922b..00000000
--- a/web/src/widget/radio.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-//! Create choices using radio buttons.
-use crate::{Bus, Css, Element, Widget};
-
-pub use iced_style::radio::{Style, StyleSheet};
-
-use dodrio::bumpalo;
-
-/// A circular button representing a choice.
-///
-/// # Example
-/// ```
-/// # use iced_web::Radio;
-///
-/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-/// pub enum Choice {
-/// A,
-/// B,
-/// }
-///
-/// #[derive(Debug, Clone, Copy)]
-/// pub enum Message {
-/// RadioSelected(Choice),
-/// }
-///
-/// let selected_choice = Some(Choice::A);
-///
-/// Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected);
-///
-/// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected);
-/// ```
-///
-/// ![Radio buttons drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/radio.png?raw=true)
-#[allow(missing_debug_implementations)]
-pub struct Radio<'a, Message> {
- is_selected: bool,
- on_click: Message,
- label: String,
- id: Option<String>,
- name: Option<String>,
- #[allow(dead_code)]
- style_sheet: Box<dyn StyleSheet + 'a>,
-}
-
-impl<'a, Message> Radio<'a, Message> {
- /// Creates a new [`Radio`] button.
- ///
- /// It expects:
- /// * the value related to the [`Radio`] button
- /// * the label of the [`Radio`] button
- /// * 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`.
- 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,
- {
- Radio {
- is_selected: Some(value) == selected,
- on_click: f(value),
- label: label.into(),
- id: None,
- name: None,
- style_sheet: Default::default(),
- }
- }
-
- /// Sets the style of the [`Radio`] button.
- pub fn style(
- mut self,
- style_sheet: impl Into<Box<dyn StyleSheet + 'a>>,
- ) -> Self {
- self.style_sheet = style_sheet.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<'a, Message> Widget<Message> for Radio<'a, Message>
-where
- Message: 'static + Clone,
-{
- 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 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
- .attr("style", "display: block; font-size: 20px")
- .children(vec![
- input
- .attr("type", "radio")
- .attr("style", "margin-right: 10px")
- .bool_attr("checked", self.is_selected)
- .on("click", move |_root, _vdom, _event| {
- event_bus.publish(on_click.clone());
- })
- .finish(),
- text(radio_label),
- ])
- .finish()
- }
-}
-
-impl<'a, Message> From<Radio<'a, Message>> for Element<'a, Message>
-where
- Message: 'static + Clone,
-{
- fn from(radio: Radio<'a, Message>) -> Element<'a, Message> {
- Element::new(radio)
- }
-}
diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs
deleted file mode 100644
index 13eab27d..00000000
--- a/web/src/widget/row.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-use crate::css;
-use crate::{Alignment, Bus, Css, Element, Length, Padding, Widget};
-
-use dodrio::bumpalo;
-use std::u32;
-
-/// A container that distributes its contents horizontally.
-///
-/// A [`Row`] will try to fill the horizontal space of its container.
-#[allow(missing_debug_implementations)]
-pub struct Row<'a, Message> {
- spacing: u16,
- padding: Padding,
- width: Length,
- height: Length,
- max_width: u32,
- max_height: u32,
- align_items: Alignment,
- children: Vec<Element<'a, Message>>,
-}
-
-impl<'a, Message> Row<'a, Message> {
- /// Creates an empty [`Row`].
- pub fn new() -> Self {
- Self::with_children(Vec::new())
- }
-
- /// Creates a [`Row`] with the given elements.
- pub fn with_children(children: Vec<Element<'a, Message>>) -> Self {
- Row {
- spacing: 0,
- padding: Padding::ZERO,
- width: Length::Fill,
- height: Length::Shrink,
- max_width: u32::MAX,
- max_height: u32::MAX,
- align_items: Alignment::Start,
- children,
- }
- }
-
- /// Sets the horizontal spacing _between_ elements.
- ///
- /// Custom margins per element do not exist in Iced. You should use this
- /// method instead! While less flexible, it helps you keep spacing between
- /// elements consistent.
- pub fn spacing(mut self, units: u16) -> Self {
- self.spacing = units;
- self
- }
-
- /// Sets the [`Padding`] of the [`Row`].
- pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
- self.padding = padding.into();
- self
- }
-
- /// Sets the width of the [`Row`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Row`].
- pub fn height(mut self, height: Length) -> Self {
- self.height = height;
- self
- }
-
- /// Sets the maximum width of the [`Row`].
- pub fn max_width(mut self, max_width: u32) -> Self {
- self.max_width = max_width;
- self
- }
-
- /// Sets the maximum height of the [`Row`].
- 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`] .
- pub fn align_items(mut self, align: Alignment) -> Self {
- self.align_items = align;
- self
- }
-
- /// Adds an [`Element`] to the [`Row`].
- pub fn push<E>(mut self, child: E) -> Self
- where
- E: Into<Element<'a, Message>>,
- {
- self.children.push(child.into());
- self
- }
-}
-
-impl<'a, Message> Widget<Message> for Row<'a, Message> {
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- publish: &Bus<Message>,
- style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
-
- let children: Vec<_> = self
- .children
- .iter()
- .map(|element| element.widget.node(bump, publish, style_sheet))
- .collect();
-
- let row_class = style_sheet.insert(bump, css::Rule::Row);
-
- let spacing_class =
- style_sheet.insert(bump, css::Rule::Spacing(self.spacing));
-
- // TODO: Complete styling
- div(bump)
- .attr(
- "class",
- bumpalo::format!(in bump, "{} {}", row_class, spacing_class)
- .into_bump_str(),
- )
- .attr("style", bumpalo::format!(
- in bump,
- "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::alignment(self.align_items)
- ).into_bump_str()
- )
- .children(children)
- .finish()
- }
-}
-
-impl<'a, Message> From<Row<'a, Message>> for Element<'a, Message>
-where
- Message: 'static,
-{
- fn from(column: Row<'a, Message>) -> Element<'a, Message> {
- Element::new(column)
- }
-}
diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs
deleted file mode 100644
index 22cb61be..00000000
--- a/web/src/widget/scrollable.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-//! Navigate an endless amount of content with a scrollbar.
-use crate::bumpalo;
-use crate::css;
-use crate::{Alignment, Bus, Column, Css, Element, Length, Padding, Widget};
-
-pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet};
-
-/// A widget that can vertically display an infinite amount of content with a
-/// scrollbar.
-#[allow(missing_debug_implementations)]
-pub struct Scrollable<'a, Message> {
- width: Length,
- height: Length,
- max_height: u32,
- content: Column<'a, Message>,
- #[allow(dead_code)]
- style_sheet: Box<dyn StyleSheet + 'a>,
-}
-
-impl<'a, Message> Scrollable<'a, Message> {
- /// Creates a new [`Scrollable`] with the given [`State`].
- pub fn new(_state: &'a mut State) -> Self {
- use std::u32;
-
- Scrollable {
- width: Length::Fill,
- height: Length::Shrink,
- max_height: u32::MAX,
- content: Column::new(),
- style_sheet: Default::default(),
- }
- }
-
- /// Sets the vertical spacing _between_ elements.
- ///
- /// Custom margins per element do not exist in Iced. You should use this
- /// method instead! While less flexible, it helps you keep spacing between
- /// elements consistent.
- pub fn spacing(mut self, units: u16) -> Self {
- self.content = self.content.spacing(units);
- self
- }
-
- /// Sets the [`Padding`] of the [`Scrollable`].
- pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
- self.content = self.content.padding(padding);
- self
- }
-
- /// Sets the width of the [`Scrollable`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Scrollable`].
- pub fn height(mut self, height: Length) -> Self {
- self.height = height;
- self
- }
-
- /// Sets the maximum width of the [`Scrollable`].
- 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.
- 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`] .
- pub fn align_items(mut self, align_items: Alignment) -> Self {
- self.content = self.content.align_items(align_items);
- self
- }
-
- /// Sets the style of the [`Scrollable`] .
- pub fn style(
- mut self,
- style_sheet: impl Into<Box<dyn StyleSheet + 'a>>,
- ) -> Self {
- self.style_sheet = style_sheet.into();
- self
- }
-
- /// Adds an element to the [`Scrollable`].
- pub fn push<E>(mut self, child: E) -> Self
- where
- E: Into<Element<'a, Message>>,
- {
- self.content = self.content.push(child);
- self
- }
-}
-
-impl<'a, Message> Widget<Message> for Scrollable<'a, 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::*;
-
- let width = css::length(self.width);
- let height = css::length(self.height);
-
- // TODO: Scrollbar styling
-
- let node = div(bump)
- .attr(
- "style",
- bumpalo::format!(
- in bump,
- "width: {}; height: {}; max-height: {}px; overflow: auto",
- width,
- height,
- self.max_height
- )
- .into_bump_str(),
- )
- .children(vec![self.content.node(bump, bus, style_sheet)]);
-
- node.finish()
- }
-}
-
-impl<'a, Message> From<Scrollable<'a, Message>> for Element<'a, Message>
-where
- Message: 'static,
-{
- fn from(scrollable: Scrollable<'a, Message>) -> Element<'a, Message> {
- Element::new(scrollable)
- }
-}
-
-/// The local state of a [`Scrollable`].
-#[derive(Debug, Clone, Copy, Default)]
-pub struct State;
-
-impl State {
- /// Creates a new [`State`] with the scrollbar located at the top.
- pub fn new() -> Self {
- State::default()
- }
-}
diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs
deleted file mode 100644
index 8cbf5bd0..00000000
--- a/web/src/widget/slider.rs
+++ /dev/null
@@ -1,183 +0,0 @@
-//! Display an interactive selector of a single value from a range of values.
-//!
-//! A [`Slider`] has some local [`State`].
-use crate::{Bus, Css, Element, Length, Widget};
-
-pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet};
-
-use dodrio::bumpalo;
-use std::{ops::RangeInclusive, rc::Rc};
-
-/// An horizontal bar and a handle that selects a single value from a range of
-/// values.
-///
-/// 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.
-///
-/// # Example
-/// ```
-/// # use iced_web::{slider, Slider};
-/// #
-/// pub enum Message {
-/// SliderChanged(f32),
-/// }
-///
-/// let state = &mut slider::State::new();
-/// let value = 50.0;
-///
-/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged);
-/// ```
-///
-/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)
-#[allow(missing_debug_implementations)]
-pub struct Slider<'a, T, Message> {
- _state: &'a mut State,
- range: RangeInclusive<T>,
- step: T,
- value: T,
- on_change: Rc<Box<dyn Fn(T) -> Message>>,
- #[allow(dead_code)]
- width: Length,
- #[allow(dead_code)]
- style_sheet: Box<dyn StyleSheet + 'a>,
-}
-
-impl<'a, T, Message> Slider<'a, T, Message>
-where
- T: Copy + From<u8> + std::cmp::PartialOrd,
-{
- /// Creates a new [`Slider`].
- ///
- /// It expects:
- /// * the local [`State`] of the [`Slider`]
- /// * an inclusive range of possible values
- /// * the current value of the [`Slider`]
- /// * a function that will be called when the [`Slider`] is dragged.
- /// It receives the new value of the [`Slider`] and must produce a
- /// `Message`.
- pub fn new<F>(
- state: &'a mut State,
- range: RangeInclusive<T>,
- value: T,
- on_change: F,
- ) -> Self
- where
- 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,
- range,
- step: T::from(1),
- on_change: Rc::new(Box::new(on_change)),
- width: Length::Fill,
- style_sheet: Default::default(),
- }
- }
-
- /// Sets the width of the [`Slider`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the style of the [`Slider`].
- pub fn style(
- mut self,
- style_sheet: impl Into<Box<dyn StyleSheet + 'a>>,
- ) -> Self {
- self.style_sheet = style_sheet.into();
- self
- }
-
- /// Sets the step size of the [`Slider`].
- pub fn step(mut self, step: T) -> Self {
- self.step = step;
- self
- }
-}
-
-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>(
- &self,
- bump: &'b bumpalo::Bump,
- bus: &Bus<Message>,
- _style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
- use wasm_bindgen::JsCast;
-
- let (start, end) = self.range.clone().into_inner();
-
- 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: Styling
- input(bump)
- .attr("type", "range")
- .attr("step", step.into_bump_str())
- .attr("min", min.into_bump_str())
- .attr("max", max.into_bump_str())
- .attr("value", value.into_bump_str())
- .attr("style", "width: 100%")
- .on("input", move |_root, _vdom, event| {
- let slider = match event.target().and_then(|t| {
- t.dyn_into::<web_sys::HtmlInputElement>().ok()
- }) {
- None => return,
- Some(slider) => slider,
- };
-
- 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, 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, T, Message>) -> Element<'a, Message> {
- Element::new(slider)
- }
-}
-
-/// The local state of a [`Slider`].
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub struct State;
-
-impl State {
- /// Creates a new [`State`].
- pub fn new() -> Self {
- Self
- }
-}
diff --git a/web/src/widget/space.rs b/web/src/widget/space.rs
deleted file mode 100644
index a8571fdb..00000000
--- a/web/src/widget/space.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-use crate::{css, Bus, Css, Element, Length, Widget};
-use dodrio::bumpalo;
-
-/// An amount of empty space.
-///
-/// It can be useful if you want to fill some space with nothing.
-#[derive(Debug)]
-pub struct Space {
- width: Length,
- height: Length,
-}
-
-impl Space {
- /// Creates an amount of empty [`Space`] with the given width and height.
- pub fn new(width: Length, height: Length) -> Self {
- Space { width, height }
- }
-
- /// Creates an amount of horizontal [`Space`].
- pub fn with_width(width: Length) -> Self {
- Space {
- width,
- height: Length::Shrink,
- }
- }
-
- /// Creates an amount of vertical [`Space`].
- pub fn with_height(height: Length) -> Self {
- Space {
- width: Length::Shrink,
- height,
- }
- }
-}
-
-impl<'a, Message> Widget<Message> for Space {
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- _publish: &Bus<Message>,
- _css: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
-
- let width = css::length(self.width);
- let height = css::length(self.height);
-
- let style = bumpalo::format!(
- in bump,
- "width: {}; height: {};",
- width,
- height
- );
-
- div(bump).attr("style", style.into_bump_str()).finish()
- }
-}
-
-impl<'a, Message> From<Space> for Element<'a, Message> {
- fn from(space: Space) -> Element<'a, Message> {
- Element::new(space)
- }
-}
diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs
deleted file mode 100644
index 53d57bfd..00000000
--- a/web/src/widget/text.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-use crate::alignment;
-use crate::css;
-use crate::{Bus, Color, Css, Element, Font, Length, Widget};
-use dodrio::bumpalo;
-
-/// A paragraph of text.
-///
-/// # Example
-///
-/// ```
-/// # use iced_web::Text;
-///
-/// Text::new("I <3 iced!")
-/// .size(40);
-/// ```
-#[derive(Debug, Clone)]
-pub struct Text {
- content: String,
- size: Option<u16>,
- color: Option<Color>,
- font: Font,
- width: Length,
- height: Length,
- horizontal_alignment: alignment::Horizontal,
- vertical_alignment: alignment::Vertical,
-}
-
-impl Text {
- /// Create a new fragment of [`Text`] with the given contents.
- pub fn new<T: Into<String>>(label: T) -> Self {
- Text {
- content: label.into(),
- size: None,
- color: None,
- font: Font::Default,
- width: Length::Shrink,
- height: Length::Shrink,
- horizontal_alignment: alignment::Horizontal::Left,
- vertical_alignment: alignment::Vertical::Top,
- }
- }
-
- /// Sets the size of the [`Text`].
- pub fn size(mut self, size: u16) -> Self {
- self.size = Some(size);
- self
- }
-
- /// Sets the [`Color`] of the [`Text`].
- pub fn color<C: Into<Color>>(mut self, color: C) -> Self {
- self.color = Some(color.into());
- self
- }
-
- /// Sets the [`Font`] of the [`Text`].
- pub fn font(mut self, font: Font) -> Self {
- self.font = font;
- self
- }
-
- /// Sets the width of the [`Text`] boundaries.
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Text`] boundaries.
- pub fn height(mut self, height: Length) -> Self {
- self.height = height;
- self
- }
-
- /// Sets the [`HorizontalAlignment`] of the [`Text`].
- pub fn horizontal_alignment(
- mut self,
- alignment: alignment::Horizontal,
- ) -> Self {
- self.horizontal_alignment = alignment;
- self
- }
-
- /// Sets the [`VerticalAlignment`] of the [`Text`].
- pub fn vertical_alignment(
- mut self,
- alignment: alignment::Vertical,
- ) -> Self {
- self.vertical_alignment = alignment;
- self
- }
-}
-
-impl<'a, Message> Widget<Message> for Text {
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- _publish: &Bus<Message>,
- _style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
-
- let content = {
- use dodrio::bumpalo::collections::String;
-
- String::from_str_in(&self.content, bump)
- };
-
- let color = self
- .color
- .map(css::color)
- .unwrap_or(String::from("inherit"));
-
- let width = css::length(self.width);
- let height = css::length(self.height);
-
- let text_align = match self.horizontal_alignment {
- alignment::Horizontal::Left => "left",
- alignment::Horizontal::Center => "center",
- alignment::Horizontal::Right => "right",
- };
-
- let style = bumpalo::format!(
- in bump,
- "width: {}; height: {}; font-size: {}px; color: {}; \
- text-align: {}; font-family: {}",
- width,
- height,
- self.size.unwrap_or(20),
- color,
- text_align,
- match self.font {
- Font::Default => "inherit",
- Font::External { name, .. } => name,
- }
- );
-
- // TODO: Complete styling
- p(bump)
- .attr("style", style.into_bump_str())
- .children(vec![text(content.into_bump_str())])
- .finish()
- }
-}
-
-impl<'a, Message> From<Text> for Element<'a, Message> {
- fn from(text: Text) -> Element<'a, Message> {
- Element::new(text)
- }
-}
diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs
deleted file mode 100644
index c5874485..00000000
--- a/web/src/widget/text_input.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-//! Display fields that can be filled with text.
-//!
-//! A [`TextInput`] has some local [`State`].
-use crate::{bumpalo, css, Bus, Css, Element, Length, Padding, Widget};
-
-pub use iced_style::text_input::{Style, StyleSheet};
-
-use std::{rc::Rc, u32};
-
-/// A field that can be filled with text.
-///
-/// # Example
-/// ```
-/// # use iced_web::{text_input, TextInput};
-/// #
-/// enum Message {
-/// TextInputChanged(String),
-/// }
-///
-/// let mut state = text_input::State::new();
-/// let value = "Some text";
-///
-/// let input = TextInput::new(
-/// &mut state,
-/// "This is the placeholder...",
-/// value,
-/// Message::TextInputChanged,
-/// );
-/// ```
-#[allow(missing_debug_implementations)]
-pub struct TextInput<'a, Message> {
- _state: &'a mut State,
- placeholder: String,
- value: String,
- is_secure: bool,
- width: Length,
- max_width: u32,
- padding: Padding,
- size: Option<u16>,
- on_change: Rc<Box<dyn Fn(String) -> Message>>,
- on_submit: Option<Message>,
- style_sheet: Box<dyn StyleSheet + 'a>,
-}
-
-impl<'a, Message> TextInput<'a, Message> {
- /// Creates a new [`TextInput`].
- ///
- /// It expects:
- /// - some [`State`]
- /// - a placeholder
- /// - the current value
- /// - a function that produces a message when the [`TextInput`] changes
- pub fn new<F>(
- state: &'a mut State,
- placeholder: &str,
- value: &str,
- on_change: F,
- ) -> Self
- where
- F: 'static + Fn(String) -> Message,
- {
- Self {
- _state: state,
- placeholder: String::from(placeholder),
- value: String::from(value),
- is_secure: false,
- width: Length::Fill,
- max_width: u32::MAX,
- padding: Padding::ZERO,
- size: None,
- on_change: Rc::new(Box::new(on_change)),
- on_submit: None,
- style_sheet: Default::default(),
- }
- }
-
- /// Converts the [`TextInput`] into a secure password input.
- pub fn password(mut self) -> Self {
- self.is_secure = true;
- self
- }
-
- /// Sets the width of the [`TextInput`].
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the maximum width of the [`TextInput`].
- pub fn max_width(mut self, max_width: u32) -> Self {
- self.max_width = max_width;
- self
- }
-
- /// Sets the [`Padding`] of the [`TextInput`].
- pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
- self.padding = padding.into();
- self
- }
-
- /// Sets the text size of the [`TextInput`].
- pub fn size(mut self, size: u16) -> Self {
- self.size = Some(size);
- self
- }
-
- /// Sets the message that should be produced when the [`TextInput`] is
- /// focused and the enter key is pressed.
- pub fn on_submit(mut self, message: Message) -> Self {
- self.on_submit = Some(message);
- self
- }
-
- /// Sets the style of the [`TextInput`].
- pub fn style(
- mut self,
- style_sheet: impl Into<Box<dyn StyleSheet + 'a>>,
- ) -> Self {
- self.style_sheet = style_sheet.into();
- self
- }
-}
-
-impl<'a, Message> Widget<Message> for TextInput<'a, Message>
-where
- Message: 'static + Clone,
-{
- fn node<'b>(
- &self,
- bump: &'b bumpalo::Bump,
- bus: &Bus<Message>,
- _style_sheet: &mut Css<'b>,
- ) -> dodrio::Node<'b> {
- use dodrio::builder::*;
- use wasm_bindgen::JsCast;
-
- 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();
- let input_event_bus = bus.clone();
- let submit_event_bus = bus.clone();
- let style = self.style_sheet.active();
-
- input(bump)
- .attr(
- "style",
- bumpalo::format!(
- in bump,
- "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,
- css::color(style.border_color),
- style.border_radius,
- css::color(self.style_sheet.value_color())
- )
- .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()
- }) {
- None => return,
- Some(text_input) => text_input,
- };
-
- input_event_bus.publish(on_change(text_input.value()));
- })
- .on("keypress", move |_root, _vdom, event| {
- if let Some(on_submit) = on_submit.clone() {
- let event =
- event.unchecked_into::<web_sys::KeyboardEvent>();
-
- match event.key_code() {
- 13 => {
- submit_event_bus.publish(on_submit);
- }
- _ => {}
- }
- }
- })
- .finish()
- }
-}
-
-impl<'a, Message> From<TextInput<'a, Message>> for Element<'a, Message>
-where
- Message: 'static + Clone,
-{
- fn from(text_input: TextInput<'a, Message>) -> Element<'a, Message> {
- Element::new(text_input)
- }
-}
-
-/// The state of a [`TextInput`].
-#[derive(Debug, Clone, Copy, Default)]
-pub struct State;
-
-impl State {
- /// Creates a new [`State`], representing an unfocused [`TextInput`].
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Creates a new [`State`], representing a focused [`TextInput`].
- pub fn focused() -> Self {
- // 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
deleted file mode 100644
index 0a198079..00000000
--- a/web/src/widget/toggler.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-//! 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)
- }
-}