Merge pull request #180 from hecrj/feature/web-styling
Custom styling for `iced_web`
This commit is contained in:
commit
97c308076f
11
Cargo.toml
11
Cargo.toml
@ -12,12 +12,16 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
|
|||||||
categories = ["gui"]
|
categories = ["gui"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# Enables the Image widget
|
# Enables the `Image` widget
|
||||||
image = ["iced_wgpu/image"]
|
image = ["iced_wgpu/image"]
|
||||||
# Enables the Svg widget
|
# Enables the `Svg` widget
|
||||||
svg = ["iced_wgpu/svg"]
|
svg = ["iced_wgpu/svg"]
|
||||||
# Enables a debug view in native platforms (press F12)
|
# Enables a debug view in native platforms (press F12)
|
||||||
debug = ["iced_winit/debug"]
|
debug = ["iced_winit/debug"]
|
||||||
|
# Enables `tokio` as the `executor::Default` on native platforms
|
||||||
|
tokio = ["iced_futures/tokio"]
|
||||||
|
# Enables `async-std` as the `executor::Default` on native platforms
|
||||||
|
async-std = ["iced_futures/async-std"]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
@ -45,6 +49,9 @@ members = [
|
|||||||
"examples/tour",
|
"examples/tour",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
iced_futures = { version = "0.1.0-alpha", path = "futures" }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
iced_winit = { version = "0.1.0-alpha", path = "winit" }
|
iced_winit = { version = "0.1.0-alpha", path = "winit" }
|
||||||
iced_wgpu = { version = "0.1.0", path = "wgpu" }
|
iced_wgpu = { version = "0.1.0", path = "wgpu" }
|
||||||
|
@ -6,9 +6,13 @@ edition = "2018"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["image"] }
|
iced = { path = "../..", features = ["image", "debug", "tokio"] }
|
||||||
iced_futures = { path = "../../futures", features = ["async-std"] }
|
|
||||||
surf = "1.0"
|
|
||||||
rand = "0.7"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
rand = { version = "0.7", features = ["wasm-bindgen"] }
|
||||||
|
|
||||||
|
[dependencies.reqwest]
|
||||||
|
version = "0.10"
|
||||||
|
git = "https://github.com/hecrj/reqwest.git"
|
||||||
|
branch = "feature/wasm-deserialize-json"
|
||||||
|
features = ["json"]
|
||||||
|
@ -27,7 +27,7 @@ enum Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Pokedex {
|
impl Application for Pokedex {
|
||||||
type Executor = iced_futures::executor::AsyncStd;
|
type Executor = iced::executor::Default;
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
|
|
||||||
fn new() -> (Pokedex, Command<Message>) {
|
fn new() -> (Pokedex, Command<Message>) {
|
||||||
@ -79,6 +79,7 @@ impl Application for Pokedex {
|
|||||||
fn view(&mut self) -> Element<Message> {
|
fn view(&mut self) -> Element<Message> {
|
||||||
let content = match self {
|
let content = match self {
|
||||||
Pokedex::Loading => Column::new()
|
Pokedex::Loading => Column::new()
|
||||||
|
.width(Length::Shrink)
|
||||||
.push(Text::new("Searching for Pokémon...").size(40)),
|
.push(Text::new("Searching for Pokémon...").size(40)),
|
||||||
Pokedex::Loaded { pokemon, search } => Column::new()
|
Pokedex::Loaded { pokemon, search } => Column::new()
|
||||||
.max_width(500)
|
.max_width(500)
|
||||||
@ -166,19 +167,21 @@ impl Pokemon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let id = {
|
let id = {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::rngs::OsRng::default();
|
||||||
|
|
||||||
rng.gen_range(0, Pokemon::TOTAL)
|
rng.gen_range(0, Pokemon::TOTAL)
|
||||||
};
|
};
|
||||||
|
|
||||||
let url = format!("https://pokeapi.co/api/v2/pokemon-species/{}", id);
|
let fetch_entry = async {
|
||||||
let sprite = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id);
|
let url =
|
||||||
|
format!("https://pokeapi.co/api/v2/pokemon-species/{}", id);
|
||||||
|
|
||||||
let (entry, sprite): (Entry, _) = futures::future::try_join(
|
reqwest::get(&url).await?.json().await
|
||||||
surf::get(&url).recv_json(),
|
};
|
||||||
surf::get(&sprite).recv_bytes(),
|
|
||||||
)
|
let (entry, image): (Entry, _) =
|
||||||
.await?;
|
futures::future::try_join(fetch_entry, Self::fetch_image(id))
|
||||||
|
.await?;
|
||||||
|
|
||||||
let description = entry
|
let description = entry
|
||||||
.flavor_text_entries
|
.flavor_text_entries
|
||||||
@ -195,9 +198,23 @@ impl Pokemon {
|
|||||||
.chars()
|
.chars()
|
||||||
.map(|c| if c.is_control() { ' ' } else { c })
|
.map(|c| if c.is_control() { ' ' } else { c })
|
||||||
.collect(),
|
.collect(),
|
||||||
image: image::Handle::from_memory(sprite),
|
image,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn fetch_image(id: u16) -> Result<image::Handle, reqwest::Error> {
|
||||||
|
let url = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id);
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
let bytes = reqwest::get(&url).await?.bytes().await?;
|
||||||
|
|
||||||
|
Ok(image::Handle::from_memory(bytes.as_ref().to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
Ok(image::Handle::from_path(url))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -206,9 +223,9 @@ enum Error {
|
|||||||
LanguageError,
|
LanguageError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<surf::Exception> for Error {
|
impl From<reqwest::Error> for Error {
|
||||||
fn from(exception: surf::Exception) -> Error {
|
fn from(error: reqwest::Error) -> Error {
|
||||||
dbg!(&exception);
|
dbg!(&error);
|
||||||
|
|
||||||
Error::APIError
|
Error::APIError
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,7 @@ impl Sandbox for Styling {
|
|||||||
"Toggle me!",
|
"Toggle me!",
|
||||||
Message::CheckboxToggled,
|
Message::CheckboxToggled,
|
||||||
)
|
)
|
||||||
|
.width(Length::Fill)
|
||||||
.style(self.theme);
|
.style(self.theme);
|
||||||
|
|
||||||
let content = Column::new()
|
let content = Column::new()
|
||||||
|
@ -6,13 +6,18 @@ edition = "2018"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../.." }
|
iced = { path = "../..", features = ["async-std"] }
|
||||||
iced_futures = { path = "../../futures", features = ["async-std"] }
|
|
||||||
async-std = "1.0"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
async-std = "1.0"
|
||||||
directories = "2.0"
|
directories = "2.0"
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
web-sys = { version = "0.3", features = ["Window", "Storage"] }
|
||||||
|
wasm-timer = "0.2"
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
assets = [
|
assets = [
|
||||||
["target/release/todos", "usr/bin/iced-todos", "755"],
|
["target/release/todos", "usr/bin/iced-todos", "755"],
|
||||||
|
@ -38,7 +38,7 @@ enum Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Todos {
|
impl Application for Todos {
|
||||||
type Executor = iced_futures::executor::AsyncStd;
|
type Executor = iced::executor::Default;
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
|
|
||||||
fn new() -> (Todos, Command<Message>) {
|
fn new() -> (Todos, Command<Message>) {
|
||||||
@ -377,6 +377,7 @@ impl Controls {
|
|||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
|
.width(Length::Shrink)
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(filter_button(
|
.push(filter_button(
|
||||||
all_button,
|
all_button,
|
||||||
@ -493,6 +494,7 @@ enum SaveError {
|
|||||||
FormatError,
|
FormatError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
impl SavedState {
|
impl SavedState {
|
||||||
fn path() -> std::path::PathBuf {
|
fn path() -> std::path::PathBuf {
|
||||||
let mut path = if let Some(project_dirs) =
|
let mut path = if let Some(project_dirs) =
|
||||||
@ -555,6 +557,41 @@ impl SavedState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
impl SavedState {
|
||||||
|
fn storage() -> Option<web_sys::Storage> {
|
||||||
|
let window = web_sys::window()?;
|
||||||
|
|
||||||
|
window.local_storage().ok()?
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load() -> Result<SavedState, LoadError> {
|
||||||
|
let storage = Self::storage().ok_or(LoadError::FileError)?;
|
||||||
|
|
||||||
|
let contents = storage
|
||||||
|
.get_item("state")
|
||||||
|
.map_err(|_| LoadError::FileError)?
|
||||||
|
.ok_or(LoadError::FileError)?;
|
||||||
|
|
||||||
|
serde_json::from_str(&contents).map_err(|_| LoadError::FormatError)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save(self) -> Result<(), SaveError> {
|
||||||
|
let storage = Self::storage().ok_or(SaveError::FileError)?;
|
||||||
|
|
||||||
|
let json = serde_json::to_string_pretty(&self)
|
||||||
|
.map_err(|_| SaveError::FormatError)?;
|
||||||
|
|
||||||
|
storage
|
||||||
|
.set_item("state", &json)
|
||||||
|
.map_err(|_| SaveError::WriteError)?;
|
||||||
|
|
||||||
|
let _ = wasm_timer::Delay::new(std::time::Duration::from_secs(2)).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod style {
|
mod style {
|
||||||
use iced::{button, Background, Color, Vector};
|
use iced::{button, Background, Color, Vector};
|
||||||
|
|
||||||
|
@ -8,6 +8,3 @@ publish = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["image", "debug"] }
|
iced = { path = "../..", features = ["image", "debug"] }
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
wasm-bindgen = "0.2.51"
|
|
||||||
|
@ -779,16 +779,3 @@ mod style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should be gracefully handled by Iced in the future. Probably using our
|
|
||||||
// own proc macro, or maybe the whole process is streamlined by `wasm-pack` at
|
|
||||||
// some point.
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
mod wasm {
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
|
||||||
pub fn run() {
|
|
||||||
super::main()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -19,12 +19,12 @@ log = "0.4"
|
|||||||
[dependencies.futures]
|
[dependencies.futures]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
[dependencies.tokio]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
optional = true
|
optional = true
|
||||||
features = ["rt-core"]
|
features = ["rt-core", "rt-threaded"]
|
||||||
|
|
||||||
[dependencies.async-std]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.async-std]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
@ -1,100 +1,11 @@
|
|||||||
use futures::future::{BoxFuture, Future, FutureExt};
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod native;
|
||||||
|
|
||||||
/// A collection of async operations.
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
///
|
pub use native::Command;
|
||||||
/// You should be able to turn a future easily into a [`Command`], either by
|
|
||||||
/// using the `From` trait or [`Command::perform`].
|
|
||||||
///
|
|
||||||
/// [`Command`]: struct.Command.html
|
|
||||||
pub struct Command<T> {
|
|
||||||
futures: Vec<BoxFuture<'static, T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Command<T> {
|
#[cfg(target_arch = "wasm32")]
|
||||||
/// Creates an empty [`Command`].
|
mod web;
|
||||||
///
|
|
||||||
/// In other words, a [`Command`] that does nothing.
|
|
||||||
///
|
|
||||||
/// [`Command`]: struct.Command.html
|
|
||||||
pub fn none() -> Self {
|
|
||||||
Self {
|
|
||||||
futures: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a [`Command`] that performs the action of the given future.
|
#[cfg(target_arch = "wasm32")]
|
||||||
///
|
pub use web::Command;
|
||||||
/// [`Command`]: struct.Command.html
|
|
||||||
pub fn perform<A>(
|
|
||||||
future: impl Future<Output = T> + 'static + Send,
|
|
||||||
f: impl Fn(T) -> A + 'static + Send,
|
|
||||||
) -> Command<A> {
|
|
||||||
Command {
|
|
||||||
futures: vec![future.map(f).boxed()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies a transformation to the result of a [`Command`].
|
|
||||||
///
|
|
||||||
/// [`Command`]: struct.Command.html
|
|
||||||
pub fn map<A>(
|
|
||||||
mut self,
|
|
||||||
f: impl Fn(T) -> A + 'static + Send + Sync,
|
|
||||||
) -> Command<A>
|
|
||||||
where
|
|
||||||
T: 'static,
|
|
||||||
{
|
|
||||||
let f = std::sync::Arc::new(f);
|
|
||||||
|
|
||||||
Command {
|
|
||||||
futures: self
|
|
||||||
.futures
|
|
||||||
.drain(..)
|
|
||||||
.map(|future| {
|
|
||||||
let f = f.clone();
|
|
||||||
|
|
||||||
future.map(move |result| f(result)).boxed()
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a [`Command`] that performs the actions of all the given
|
|
||||||
/// commands.
|
|
||||||
///
|
|
||||||
/// Once this command is run, all the commands will be exectued at once.
|
|
||||||
///
|
|
||||||
/// [`Command`]: struct.Command.html
|
|
||||||
pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
|
|
||||||
Self {
|
|
||||||
futures: commands
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|command| command.futures)
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a [`Command`] into its underlying list of futures.
|
|
||||||
///
|
|
||||||
/// [`Command`]: struct.Command.html
|
|
||||||
pub fn futures(self) -> Vec<BoxFuture<'static, T>> {
|
|
||||||
self.futures
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, A> From<A> for Command<T>
|
|
||||||
where
|
|
||||||
A: Future<Output = T> + 'static + Send,
|
|
||||||
{
|
|
||||||
fn from(future: A) -> Self {
|
|
||||||
Self {
|
|
||||||
futures: vec![future.boxed()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::fmt::Debug for Command<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("Command").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
100
futures/src/command/native.rs
Normal file
100
futures/src/command/native.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use futures::future::{BoxFuture, Future, FutureExt};
|
||||||
|
|
||||||
|
/// A collection of async operations.
|
||||||
|
///
|
||||||
|
/// You should be able to turn a future easily into a [`Command`], either by
|
||||||
|
/// using the `From` trait or [`Command::perform`].
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub struct Command<T> {
|
||||||
|
futures: Vec<BoxFuture<'static, T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Command<T> {
|
||||||
|
/// Creates an empty [`Command`].
|
||||||
|
///
|
||||||
|
/// In other words, a [`Command`] that does nothing.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self {
|
||||||
|
futures: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Command`] that performs the action of the given future.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn perform<A>(
|
||||||
|
future: impl Future<Output = T> + 'static + Send,
|
||||||
|
f: impl Fn(T) -> A + 'static + Send,
|
||||||
|
) -> Command<A> {
|
||||||
|
Command {
|
||||||
|
futures: vec![future.map(f).boxed()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies a transformation to the result of a [`Command`].
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn map<A>(
|
||||||
|
mut self,
|
||||||
|
f: impl Fn(T) -> A + 'static + Send + Sync,
|
||||||
|
) -> Command<A>
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
let f = std::sync::Arc::new(f);
|
||||||
|
|
||||||
|
Command {
|
||||||
|
futures: self
|
||||||
|
.futures
|
||||||
|
.drain(..)
|
||||||
|
.map(|future| {
|
||||||
|
let f = f.clone();
|
||||||
|
|
||||||
|
future.map(move |result| f(result)).boxed()
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
|
||||||
|
Self {
|
||||||
|
futures: commands
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|command| command.futures)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a [`Command`] into its underlying list of futures.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn futures(self) -> Vec<BoxFuture<'static, T>> {
|
||||||
|
self.futures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, A> From<A> for Command<T>
|
||||||
|
where
|
||||||
|
A: Future<Output = T> + 'static + Send,
|
||||||
|
{
|
||||||
|
fn from(future: A) -> Self {
|
||||||
|
Self {
|
||||||
|
futures: vec![future.boxed()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Command<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Command").finish()
|
||||||
|
}
|
||||||
|
}
|
101
futures/src/command/web.rs
Normal file
101
futures/src/command/web.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use futures::future::{Future, FutureExt};
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
/// A collection of async operations.
|
||||||
|
///
|
||||||
|
/// You should be able to turn a future easily into a [`Command`], either by
|
||||||
|
/// using the `From` trait or [`Command::perform`].
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub struct Command<T> {
|
||||||
|
futures: Vec<Pin<Box<dyn Future<Output = T> + 'static>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Command<T> {
|
||||||
|
/// Creates an empty [`Command`].
|
||||||
|
///
|
||||||
|
/// In other words, a [`Command`] that does nothing.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self {
|
||||||
|
futures: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Command`] that performs the action of the given future.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn perform<A>(
|
||||||
|
future: impl Future<Output = T> + 'static,
|
||||||
|
f: impl Fn(T) -> A + 'static,
|
||||||
|
) -> Command<A> {
|
||||||
|
Command {
|
||||||
|
futures: vec![future.map(f).boxed_local()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies a transformation to the result of a [`Command`].
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn map<A>(
|
||||||
|
mut self,
|
||||||
|
f: impl Fn(T) -> A + 'static + Send + Sync + Unpin,
|
||||||
|
) -> Command<A>
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
let f = std::sync::Arc::new(f);
|
||||||
|
|
||||||
|
Command {
|
||||||
|
futures: self
|
||||||
|
.futures
|
||||||
|
.drain(..)
|
||||||
|
.map(|future| {
|
||||||
|
let f = f.clone();
|
||||||
|
|
||||||
|
future.map(move |result| f(result)).boxed_local()
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
|
||||||
|
Self {
|
||||||
|
futures: commands
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|command| command.futures)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a [`Command`] into its underlying list of futures.
|
||||||
|
///
|
||||||
|
/// [`Command`]: struct.Command.html
|
||||||
|
pub fn futures(self) -> Vec<Pin<Box<dyn Future<Output = T> + 'static>>> {
|
||||||
|
self.futures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, A> From<A> for Command<T>
|
||||||
|
where
|
||||||
|
A: Future<Output = T> + 'static,
|
||||||
|
{
|
||||||
|
fn from(future: A) -> Self {
|
||||||
|
Self {
|
||||||
|
futures: vec![future.boxed_local()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Command<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Command").finish()
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
//! Choose your preferred executor to power a runtime.
|
//! Choose your preferred executor to power a runtime.
|
||||||
mod null;
|
mod null;
|
||||||
|
|
||||||
#[cfg(feature = "thread-pool")]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "thread-pool"))]
|
||||||
mod thread_pool;
|
mod thread_pool;
|
||||||
|
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
|
||||||
mod tokio;
|
mod tokio;
|
||||||
|
|
||||||
#[cfg(feature = "async-std")]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
|
||||||
mod async_std;
|
mod async_std;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -15,13 +15,13 @@ mod wasm_bindgen;
|
|||||||
|
|
||||||
pub use null::Null;
|
pub use null::Null;
|
||||||
|
|
||||||
#[cfg(feature = "thread-pool")]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "thread-pool"))]
|
||||||
pub use thread_pool::ThreadPool;
|
pub use thread_pool::ThreadPool;
|
||||||
|
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
|
||||||
pub use self::tokio::Tokio;
|
pub use self::tokio::Tokio;
|
||||||
|
|
||||||
#[cfg(feature = "async-std")]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
|
||||||
pub use self::async_std::AsyncStd;
|
pub use self::async_std::AsyncStd;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -41,8 +41,15 @@ pub trait Executor: Sized {
|
|||||||
/// Spawns a future in the [`Executor`].
|
/// Spawns a future in the [`Executor`].
|
||||||
///
|
///
|
||||||
/// [`Executor`]: trait.Executor.html
|
/// [`Executor`]: trait.Executor.html
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn spawn(&self, future: impl Future<Output = ()> + Send + 'static);
|
fn spawn(&self, future: impl Future<Output = ()> + Send + 'static);
|
||||||
|
|
||||||
|
/// Spawns a local future in the [`Executor`].
|
||||||
|
///
|
||||||
|
/// [`Executor`]: trait.Executor.html
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn spawn(&self, future: impl Future<Output = ()> + 'static);
|
||||||
|
|
||||||
/// Runs the given closure inside the [`Executor`].
|
/// Runs the given closure inside the [`Executor`].
|
||||||
///
|
///
|
||||||
/// Some executors, like `tokio`, require some global state to be in place
|
/// Some executors, like `tokio`, require some global state to be in place
|
||||||
|
@ -11,5 +11,9 @@ impl Executor for Null {
|
|||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn spawn(&self, _future: impl Future<Output = ()> + Send + 'static) {}
|
fn spawn(&self, _future: impl Future<Output = ()> + Send + 'static) {}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn spawn(&self, _future: impl Future<Output = ()> + 'static) {}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,7 @@ impl Executor for WasmBindgen {
|
|||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn(
|
fn spawn(&self, future: impl futures::Future<Output = ()> + 'static) {
|
||||||
&self,
|
|
||||||
future: impl futures::Future<Output = ()> + Send + 'static,
|
|
||||||
) {
|
|
||||||
wasm_bindgen_futures::spawn_local(future);
|
wasm_bindgen_futures::spawn_local(future);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,11 +73,13 @@ where
|
|||||||
for future in futures {
|
for future in futures {
|
||||||
let mut sender = self.sender.clone();
|
let mut sender = self.sender.clone();
|
||||||
|
|
||||||
self.executor.spawn(future.then(|message| async move {
|
let future = future.then(|message| async move {
|
||||||
let _ = sender.send(message).await;
|
let _ = sender.send(message).await;
|
||||||
|
|
||||||
()
|
()
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
self.executor.spawn(future);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
//! Display images in your user interface.
|
//! Display images in your user interface.
|
||||||
|
|
||||||
use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget};
|
use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -9,6 +9,8 @@ use crate::{
|
|||||||
layout, Clipboard, Element, Event, Font, Hasher, Layout, Length, Point,
|
layout, Clipboard, Element, Event, Font, Hasher, Layout, Length, Point,
|
||||||
Rectangle, Size, Widget,
|
Rectangle, Size, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::u32;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
/// A field that can be filled with text.
|
/// A field that can be filled with text.
|
||||||
@ -43,7 +45,7 @@ pub struct TextInput<'a, Message, Renderer: self::Renderer> {
|
|||||||
is_secure: bool,
|
is_secure: bool,
|
||||||
font: Font,
|
font: Font,
|
||||||
width: Length,
|
width: Length,
|
||||||
max_width: Length,
|
max_width: u32,
|
||||||
padding: u16,
|
padding: u16,
|
||||||
size: Option<u16>,
|
size: Option<u16>,
|
||||||
on_change: Box<dyn Fn(String) -> Message>,
|
on_change: Box<dyn Fn(String) -> Message>,
|
||||||
@ -78,7 +80,7 @@ impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> {
|
|||||||
is_secure: false,
|
is_secure: false,
|
||||||
font: Font::Default,
|
font: Font::Default,
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
max_width: Length::Shrink,
|
max_width: u32::MAX,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
size: None,
|
size: None,
|
||||||
on_change: Box::new(on_change),
|
on_change: Box::new(on_change),
|
||||||
@ -114,7 +116,7 @@ impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> {
|
|||||||
/// Sets the maximum width of the [`TextInput`].
|
/// Sets the maximum width of the [`TextInput`].
|
||||||
///
|
///
|
||||||
/// [`TextInput`]: struct.TextInput.html
|
/// [`TextInput`]: struct.TextInput.html
|
||||||
pub fn max_width(mut self, max_width: Length) -> Self {
|
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||||
self.max_width = max_width;
|
self.max_width = max_width;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -178,6 +180,7 @@ where
|
|||||||
let limits = limits
|
let limits = limits
|
||||||
.pad(padding)
|
.pad(padding)
|
||||||
.width(self.width)
|
.width(self.width)
|
||||||
|
.max_width(self.max_width)
|
||||||
.height(Length::Units(text_size));
|
.height(Length::Units(text_size));
|
||||||
|
|
||||||
let mut text = layout::Node::new(limits.resolve(Size::ZERO));
|
let mut text = layout::Node::new(limits.resolve(Size::ZERO));
|
||||||
|
@ -5,36 +5,49 @@ pub use platform::Default;
|
|||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
mod platform {
|
mod platform {
|
||||||
use iced_winit::{executor::ThreadPool, futures, Executor};
|
use iced_futures::{executor, futures};
|
||||||
|
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
type Executor = executor::Tokio;
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "tokio"), feature = "async-std"))]
|
||||||
|
type Executor = executor::AsyncStd;
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
|
||||||
|
type Executor = executor::ThreadPool;
|
||||||
|
|
||||||
/// A default cross-platform executor.
|
/// A default cross-platform executor.
|
||||||
///
|
///
|
||||||
/// - On native platforms, it will use a `iced_futures::executor::ThreadPool`.
|
/// - On native platforms, it will use:
|
||||||
|
/// - `iced_futures::executor::Tokio` when the `tokio` feature is enabled.
|
||||||
|
/// - `iced_futures::executor::AsyncStd` when the `async-std` feature is
|
||||||
|
/// enabled.
|
||||||
|
/// - `iced_futures::executor::ThreadPool` otherwise.
|
||||||
/// - On the Web, it will use `iced_futures::executor::WasmBindgen`.
|
/// - On the Web, it will use `iced_futures::executor::WasmBindgen`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Default(ThreadPool);
|
pub struct Default(Executor);
|
||||||
|
|
||||||
impl Executor for Default {
|
impl super::Executor for Default {
|
||||||
fn new() -> Result<Self, futures::io::Error> {
|
fn new() -> Result<Self, futures::io::Error> {
|
||||||
Ok(Default(ThreadPool::new()?))
|
Ok(Default(Executor::new()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn(
|
fn spawn(
|
||||||
&self,
|
&self,
|
||||||
future: impl futures::Future<Output = ()> + Send + 'static,
|
future: impl futures::Future<Output = ()> + Send + 'static,
|
||||||
) {
|
) {
|
||||||
self.0.spawn(future);
|
let _ = self.0.spawn(future);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
mod platform {
|
mod platform {
|
||||||
use iced_web::{executor::WasmBindgen, futures, Executor};
|
use iced_futures::{executor::WasmBindgen, futures, Executor};
|
||||||
|
|
||||||
/// A default cross-platform executor.
|
/// A default cross-platform executor.
|
||||||
///
|
///
|
||||||
/// - On native platforms, it will use a `iced_futures::executor::ThreadPool`.
|
/// - On native platforms, it will use `iced_futures::executor::ThreadPool`.
|
||||||
/// - On the Web, it will use `iced_futures::executor::WasmBindgen`.
|
/// - On the Web, it will use `iced_futures::executor::WasmBindgen`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Default(WasmBindgen);
|
pub struct Default(WasmBindgen);
|
||||||
@ -44,10 +57,7 @@ mod platform {
|
|||||||
Ok(Default(WasmBindgen::new()?))
|
Ok(Default(WasmBindgen::new()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn(
|
fn spawn(&self, future: impl futures::Future<Output = ()> + 'static) {
|
||||||
&self,
|
|
||||||
future: impl futures::Future<Output = ()> + Send + 'static,
|
|
||||||
) {
|
|
||||||
self.0.spawn(future);
|
self.0.spawn(future);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,11 @@ categories = ["web-programming"]
|
|||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
iced_style = { version = "0.1.0-alpha", path = "../style" }
|
||||||
dodrio = "0.1.0"
|
dodrio = "0.1.0"
|
||||||
wasm-bindgen = "0.2.51"
|
wasm-bindgen = "0.2.51"
|
||||||
wasm-bindgen-futures = "0.4"
|
wasm-bindgen-futures = "0.4"
|
||||||
|
url = "2.0"
|
||||||
|
|
||||||
[dependencies.iced_core]
|
[dependencies.iced_core]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -37,4 +39,5 @@ features = [
|
|||||||
"Event",
|
"Event",
|
||||||
"EventTarget",
|
"EventTarget",
|
||||||
"InputEvent",
|
"InputEvent",
|
||||||
|
"KeyboardEvent",
|
||||||
]
|
]
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
//! Style your widgets.
|
//! Style your widgets.
|
||||||
use crate::{bumpalo, Align, Color, Length};
|
use crate::{bumpalo, Align, Background, Color, Length};
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// The style of a VDOM node.
|
/// A CSS rule of a VDOM node.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Style {
|
pub enum Rule {
|
||||||
/// Container with vertical distribution
|
/// Container with vertical distribution
|
||||||
Column,
|
Column,
|
||||||
|
|
||||||
@ -19,16 +19,16 @@ pub enum Style {
|
|||||||
Spacing(u16),
|
Spacing(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Rule {
|
||||||
/// Returns the class name of the [`Style`].
|
/// Returns the class name of the [`Style`].
|
||||||
///
|
///
|
||||||
/// [`Style`]: enum.Style.html
|
/// [`Style`]: enum.Style.html
|
||||||
pub fn class<'a>(&self) -> String {
|
pub fn class<'a>(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Style::Column => String::from("c"),
|
Rule::Column => String::from("c"),
|
||||||
Style::Row => String::from("r"),
|
Rule::Row => String::from("r"),
|
||||||
Style::Padding(padding) => format!("p-{}", padding),
|
Rule::Padding(padding) => format!("p-{}", padding),
|
||||||
Style::Spacing(spacing) => format!("s-{}", spacing),
|
Rule::Spacing(spacing) => format!("s-{}", spacing),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,24 +39,24 @@ impl Style {
|
|||||||
let class = self.class();
|
let class = self.class();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Style::Column => {
|
Rule::Column => {
|
||||||
let body = "{ display: flex; flex-direction: column; }";
|
let body = "{ display: flex; flex-direction: column; }";
|
||||||
|
|
||||||
bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
|
bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
|
||||||
}
|
}
|
||||||
Style::Row => {
|
Rule::Row => {
|
||||||
let body = "{ display: flex; flex-direction: row; }";
|
let body = "{ display: flex; flex-direction: row; }";
|
||||||
|
|
||||||
bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
|
bumpalo::format!(in bump, ".{} {}", class, body).into_bump_str()
|
||||||
}
|
}
|
||||||
Style::Padding(padding) => bumpalo::format!(
|
Rule::Padding(padding) => bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
".{} {{ box-sizing: border-box; padding: {}px }}",
|
".{} {{ box-sizing: border-box; padding: {}px }}",
|
||||||
class,
|
class,
|
||||||
padding
|
padding
|
||||||
)
|
)
|
||||||
.into_bump_str(),
|
.into_bump_str(),
|
||||||
Style::Spacing(spacing) => bumpalo::format!(
|
Rule::Spacing(spacing) => bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
".c.{} > * {{ margin-bottom: {}px }} \
|
".c.{} > * {{ margin-bottom: {}px }} \
|
||||||
.r.{} > * {{ margin-right: {}px }} \
|
.r.{} > * {{ margin-right: {}px }} \
|
||||||
@ -74,34 +74,34 @@ impl Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sheet of styles.
|
/// A cascading style sheet.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Sheet<'a> {
|
pub struct Css<'a> {
|
||||||
styles: BTreeMap<String, &'a str>,
|
rules: BTreeMap<String, &'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sheet<'a> {
|
impl<'a> Css<'a> {
|
||||||
/// Creates an empty style [`Sheet`].
|
/// Creates an empty style [`Sheet`].
|
||||||
///
|
///
|
||||||
/// [`Sheet`]: struct.Sheet.html
|
/// [`Sheet`]: struct.Sheet.html
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Css {
|
||||||
styles: BTreeMap::new(),
|
rules: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts the [`Style`] in the [`Sheet`], if it was not previously
|
/// Inserts the [`rule`] in the [`Sheet`], if it was not previously
|
||||||
/// inserted.
|
/// inserted.
|
||||||
///
|
///
|
||||||
/// It returns the class name of the provided [`Style`].
|
/// It returns the class name of the provided [`Rule`].
|
||||||
///
|
///
|
||||||
/// [`Sheet`]: struct.Sheet.html
|
/// [`Sheet`]: struct.Sheet.html
|
||||||
/// [`Style`]: enum.Style.html
|
/// [`Rule`]: enum.Rule.html
|
||||||
pub fn insert(&mut self, bump: &'a bumpalo::Bump, style: Style) -> String {
|
pub fn insert(&mut self, bump: &'a bumpalo::Bump, rule: Rule) -> String {
|
||||||
let class = style.class();
|
let class = rule.class();
|
||||||
|
|
||||||
if !self.styles.contains_key(&class) {
|
if !self.rules.contains_key(&class) {
|
||||||
let _ = self.styles.insert(class.clone(), style.declaration(bump));
|
let _ = self.rules.insert(class.clone(), rule.declaration(bump));
|
||||||
}
|
}
|
||||||
|
|
||||||
class
|
class
|
||||||
@ -119,12 +119,12 @@ impl<'a> Sheet<'a> {
|
|||||||
declarations.push(text(
|
declarations.push(text(
|
||||||
"body { height: 100%; margin: 0; padding: 0; font-family: sans-serif }",
|
"body { height: 100%; margin: 0; padding: 0; font-family: sans-serif }",
|
||||||
));
|
));
|
||||||
declarations.push(text("p { margin: 0 }"));
|
declarations.push(text("* { margin: 0; padding: 0 }"));
|
||||||
declarations.push(text(
|
declarations.push(text(
|
||||||
"button { border: none; cursor: pointer; outline: none }",
|
"button { border: none; cursor: pointer; outline: none }",
|
||||||
));
|
));
|
||||||
|
|
||||||
for declaration in self.styles.values() {
|
for declaration in self.rules.values() {
|
||||||
declarations.push(text(*declaration));
|
declarations.push(text(*declaration));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +143,26 @@ pub fn length(length: Length) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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`].
|
/// Returns the style value for the given [`Color`].
|
||||||
///
|
///
|
||||||
/// [`Color`]: ../struct.Color.html
|
/// [`Color`]: ../struct.Color.html
|
||||||
@ -150,6 +170,15 @@ pub fn color(Color { r, g, b, a }: Color) -> String {
|
|||||||
format!("rgba({}, {}, {}, {})", 255.0 * r, 255.0 * g, 255.0 * b, a)
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the style value for the given [`Align`].
|
/// Returns the style value for the given [`Align`].
|
||||||
///
|
///
|
||||||
/// [`Align`]: ../enum.Align.html
|
/// [`Align`]: ../enum.Align.html
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{style, Bus, Color, Widget};
|
use crate::{Bus, Color, Css, Widget};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -57,7 +57,7 @@ impl<'a, Message> Element<'a, Message> {
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
self.widget.node(bump, bus, style_sheet)
|
self.widget.node(bump, bus, style_sheet)
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<B>,
|
bus: &Bus<B>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
self.widget
|
self.widget
|
||||||
.node(bump, &bus.map(self.mapper.clone()), style_sheet)
|
.node(bump, &bus.map(self.mapper.clone()), style_sheet)
|
||||||
|
@ -63,11 +63,12 @@ mod bus;
|
|||||||
mod element;
|
mod element;
|
||||||
mod hasher;
|
mod hasher;
|
||||||
|
|
||||||
pub mod style;
|
pub mod css;
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
|
|
||||||
pub use bus::Bus;
|
pub use bus::Bus;
|
||||||
|
pub use css::Css;
|
||||||
pub use dodrio;
|
pub use dodrio;
|
||||||
pub use element::Element;
|
pub use element::Element;
|
||||||
pub use hasher::Hasher;
|
pub use hasher::Hasher;
|
||||||
@ -76,7 +77,6 @@ pub use iced_core::{
|
|||||||
VerticalAlignment,
|
VerticalAlignment,
|
||||||
};
|
};
|
||||||
pub use iced_futures::{executor, futures, Command};
|
pub use iced_futures::{executor, futures, Command};
|
||||||
pub use style::Style;
|
|
||||||
pub use subscription::Subscription;
|
pub use subscription::Subscription;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
@ -241,13 +241,13 @@ where
|
|||||||
|
|
||||||
let mut ui = self.application.borrow_mut();
|
let mut ui = self.application.borrow_mut();
|
||||||
let element = ui.view();
|
let element = ui.view();
|
||||||
let mut style_sheet = style::Sheet::new();
|
let mut css = Css::new();
|
||||||
|
|
||||||
let node = element.widget.node(bump, &self.bus, &mut style_sheet);
|
let node = element.widget.node(bump, &self.bus, &mut css);
|
||||||
|
|
||||||
div(bump)
|
div(bump)
|
||||||
.attr("style", "width: 100%; height: 100%")
|
.attr("style", "width: 100%; height: 100%")
|
||||||
.children(vec![style_sheet.node(bump), node])
|
.children(vec![css.node(bump), node])
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,20 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [`Widget`]: trait.Widget.html
|
//! [`Widget`]: trait.Widget.html
|
||||||
use crate::{style, Bus};
|
use crate::{Bus, Css};
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
|
|
||||||
pub mod button;
|
pub mod button;
|
||||||
|
pub mod checkbox;
|
||||||
|
pub mod container;
|
||||||
|
pub mod image;
|
||||||
|
pub mod progress_bar;
|
||||||
|
pub mod radio;
|
||||||
pub mod scrollable;
|
pub mod scrollable;
|
||||||
pub mod slider;
|
pub mod slider;
|
||||||
pub mod text_input;
|
pub mod text_input;
|
||||||
|
|
||||||
mod checkbox;
|
|
||||||
mod column;
|
mod column;
|
||||||
mod container;
|
|
||||||
mod image;
|
|
||||||
mod radio;
|
|
||||||
mod row;
|
mod row;
|
||||||
mod space;
|
mod space;
|
||||||
mod text;
|
mod text;
|
||||||
@ -46,6 +47,7 @@ pub use checkbox::Checkbox;
|
|||||||
pub use column::Column;
|
pub use column::Column;
|
||||||
pub use container::Container;
|
pub use container::Container;
|
||||||
pub use image::Image;
|
pub use image::Image;
|
||||||
|
pub use progress_bar::ProgressBar;
|
||||||
pub use radio::Radio;
|
pub use radio::Radio;
|
||||||
pub use row::Row;
|
pub use row::Row;
|
||||||
pub use space::Space;
|
pub use space::Space;
|
||||||
@ -64,6 +66,6 @@ pub trait Widget<Message> {
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
_bus: &Bus<Message>,
|
_bus: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b>;
|
) -> dodrio::Node<'b>;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
//!
|
//!
|
||||||
//! [`Button`]: struct.Button.html
|
//! [`Button`]: struct.Button.html
|
||||||
//! [`State`]: struct.State.html
|
//! [`State`]: struct.State.html
|
||||||
use crate::{style, Background, Bus, Element, Length, Style, Widget};
|
use crate::{css, Background, Bus, Css, Element, Length, Widget};
|
||||||
|
|
||||||
|
pub use iced_style::button::{Style, StyleSheet};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
|
|
||||||
@ -26,10 +28,11 @@ pub struct Button<'a, Message> {
|
|||||||
content: Element<'a, Message>,
|
content: Element<'a, Message>,
|
||||||
on_press: Option<Message>,
|
on_press: Option<Message>,
|
||||||
width: Length,
|
width: Length,
|
||||||
|
height: Length,
|
||||||
min_width: u32,
|
min_width: u32,
|
||||||
|
min_height: u32,
|
||||||
padding: u16,
|
padding: u16,
|
||||||
background: Option<Background>,
|
style: Box<dyn StyleSheet>,
|
||||||
border_radius: u16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> Button<'a, Message> {
|
impl<'a, Message> Button<'a, Message> {
|
||||||
@ -46,10 +49,11 @@ impl<'a, Message> Button<'a, Message> {
|
|||||||
content: content.into(),
|
content: content.into(),
|
||||||
on_press: None,
|
on_press: None,
|
||||||
width: Length::Shrink,
|
width: Length::Shrink,
|
||||||
|
height: Length::Shrink,
|
||||||
min_width: 0,
|
min_width: 0,
|
||||||
padding: 0,
|
min_height: 0,
|
||||||
background: None,
|
padding: 5,
|
||||||
border_radius: 0,
|
style: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +65,14 @@ impl<'a, Message> Button<'a, Message> {
|
|||||||
self
|
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`].
|
/// Sets the minimum width of the [`Button`].
|
||||||
///
|
///
|
||||||
/// [`Button`]: struct.Button.html
|
/// [`Button`]: struct.Button.html
|
||||||
@ -69,6 +81,14 @@ impl<'a, Message> Button<'a, Message> {
|
|||||||
self
|
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`].
|
/// Sets the padding of the [`Button`].
|
||||||
///
|
///
|
||||||
/// [`Button`]: struct.Button.html
|
/// [`Button`]: struct.Button.html
|
||||||
@ -77,20 +97,11 @@ impl<'a, Message> Button<'a, Message> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the [`Background`] of the [`Button`].
|
/// Sets the style of the [`Button`].
|
||||||
///
|
///
|
||||||
/// [`Button`]: struct.Button.html
|
/// [`Button`]: struct.Button.html
|
||||||
/// [`Background`]: ../../struct.Background.html
|
pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self {
|
||||||
pub fn background<T: Into<Background>>(mut self, background: T) -> Self {
|
self.style = style.into();
|
||||||
self.background = Some(background.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the border radius of the [`Button`].
|
|
||||||
///
|
|
||||||
/// [`Button`]: struct.Button.html
|
|
||||||
pub fn border_radius(mut self, border_radius: u16) -> Self {
|
|
||||||
self.border_radius = border_radius;
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,18 +137,20 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
let width = style::length(self.width);
|
// TODO: State-based styling
|
||||||
let padding_class =
|
let style = self.style.active();
|
||||||
style_sheet.insert(bump, Style::Padding(self.padding));
|
|
||||||
|
|
||||||
let background = match self.background {
|
let padding_class =
|
||||||
|
style_sheet.insert(bump, css::Rule::Padding(self.padding));
|
||||||
|
|
||||||
|
let background = match style.background {
|
||||||
None => String::from("none"),
|
None => String::from("none"),
|
||||||
Some(background) => match background {
|
Some(background) => match background {
|
||||||
Background::Color(color) => style::color(color),
|
Background::Color(color) => css::color(color),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,11 +163,12 @@ where
|
|||||||
"style",
|
"style",
|
||||||
bumpalo::format!(
|
bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
"background: {}; border-radius: {}px; width:{}; min-width: {}px",
|
"background: {}; border-radius: {}px; width:{}; min-width: {}; color: {}",
|
||||||
background,
|
background,
|
||||||
self.border_radius,
|
style.border_radius,
|
||||||
width,
|
css::length(self.width),
|
||||||
self.min_width
|
css::min_length(self.min_width),
|
||||||
|
css::color(style.text_color)
|
||||||
)
|
)
|
||||||
.into_bump_str(),
|
.into_bump_str(),
|
||||||
)
|
)
|
||||||
@ -168,8 +182,6 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Complete styling
|
|
||||||
|
|
||||||
node.finish()
|
node.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use crate::{style, Bus, Color, Element, Widget};
|
//! Show toggle controls using checkboxes.
|
||||||
|
use crate::{css, Bus, Css, Element, Length, Widget};
|
||||||
|
|
||||||
|
pub use iced_style::checkbox::{Style, StyleSheet};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -25,7 +28,8 @@ pub struct Checkbox<Message> {
|
|||||||
is_checked: bool,
|
is_checked: bool,
|
||||||
on_toggle: Rc<dyn Fn(bool) -> Message>,
|
on_toggle: Rc<dyn Fn(bool) -> Message>,
|
||||||
label: String,
|
label: String,
|
||||||
label_color: Option<Color>,
|
width: Length,
|
||||||
|
style: Box<dyn StyleSheet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> Checkbox<Message> {
|
impl<Message> Checkbox<Message> {
|
||||||
@ -47,15 +51,24 @@ impl<Message> Checkbox<Message> {
|
|||||||
is_checked,
|
is_checked,
|
||||||
on_toggle: Rc::new(f),
|
on_toggle: Rc::new(f),
|
||||||
label: String::from(label),
|
label: String::from(label),
|
||||||
label_color: None,
|
width: Length::Shrink,
|
||||||
|
style: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the color of the label of the [`Checkbox`].
|
/// Sets the width of the [`Checkbox`].
|
||||||
///
|
///
|
||||||
/// [`Checkbox`]: struct.Checkbox.html
|
/// [`Checkbox`]: struct.Checkbox.html
|
||||||
pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self {
|
pub fn width(mut self, width: Length) -> Self {
|
||||||
self.label_color = Some(color.into());
|
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
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,7 +81,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
_style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
@ -78,9 +91,23 @@ where
|
|||||||
let on_toggle = self.on_toggle.clone();
|
let on_toggle = self.on_toggle.clone();
|
||||||
let is_checked = self.is_checked;
|
let is_checked = self.is_checked;
|
||||||
|
|
||||||
// TODO: Complete styling
|
let row_class = style_sheet.insert(bump, css::Rule::Row);
|
||||||
|
|
||||||
|
let spacing_class = style_sheet.insert(bump, css::Rule::Spacing(5));
|
||||||
|
|
||||||
label(bump)
|
label(bump)
|
||||||
|
.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![
|
.children(vec![
|
||||||
|
// TODO: Checkbox styling
|
||||||
input(bump)
|
input(bump)
|
||||||
.attr("type", "checkbox")
|
.attr("type", "checkbox")
|
||||||
.bool_attr("checked", self.is_checked)
|
.bool_attr("checked", self.is_checked)
|
||||||
@ -91,7 +118,8 @@ where
|
|||||||
vdom.schedule_render();
|
vdom.schedule_render();
|
||||||
})
|
})
|
||||||
.finish(),
|
.finish(),
|
||||||
text(checkbox_label.into_bump_str()),
|
span(bump).children(vec![
|
||||||
|
text(checkbox_label.into_bump_str())]).finish(),
|
||||||
])
|
])
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{style, Align, Bus, Element, Length, Style, Widget};
|
use crate::{css, Align, Bus, Css, Element, Length, Widget};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
@ -28,7 +28,7 @@ impl<'a, Message> Column<'a, Message> {
|
|||||||
Column {
|
Column {
|
||||||
spacing: 0,
|
spacing: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
width: Length::Shrink,
|
width: Length::Fill,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
max_width: u32::MAX,
|
max_width: u32::MAX,
|
||||||
max_height: u32::MAX,
|
max_height: u32::MAX,
|
||||||
@ -112,7 +112,7 @@ impl<'a, Message> Widget<Message> for Column<'a, Message> {
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
publish: &Bus<Message>,
|
publish: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
@ -122,18 +122,13 @@ impl<'a, Message> Widget<Message> for Column<'a, Message> {
|
|||||||
.map(|element| element.widget.node(bump, publish, style_sheet))
|
.map(|element| element.widget.node(bump, publish, style_sheet))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let column_class = style_sheet.insert(bump, Style::Column);
|
let column_class = style_sheet.insert(bump, css::Rule::Column);
|
||||||
|
|
||||||
let spacing_class =
|
let spacing_class =
|
||||||
style_sheet.insert(bump, Style::Spacing(self.spacing));
|
style_sheet.insert(bump, css::Rule::Spacing(self.spacing));
|
||||||
|
|
||||||
let padding_class =
|
let padding_class =
|
||||||
style_sheet.insert(bump, Style::Padding(self.padding));
|
style_sheet.insert(bump, css::Rule::Padding(self.padding));
|
||||||
|
|
||||||
let width = style::length(self.width);
|
|
||||||
let height = style::length(self.height);
|
|
||||||
|
|
||||||
let align_items = style::align(self.align_items);
|
|
||||||
|
|
||||||
// TODO: Complete styling
|
// TODO: Complete styling
|
||||||
div(bump)
|
div(bump)
|
||||||
@ -144,12 +139,12 @@ impl<'a, Message> Widget<Message> for Column<'a, Message> {
|
|||||||
)
|
)
|
||||||
.attr("style", bumpalo::format!(
|
.attr("style", bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
"width: {}; height: {}; max-width: {}px; max-height: {}px; align-items: {}",
|
"width: {}; height: {}; max-width: {}; max-height: {}; align-items: {}",
|
||||||
width,
|
css::length(self.width),
|
||||||
height,
|
css::length(self.height),
|
||||||
self.max_width,
|
css::max_length(self.max_width),
|
||||||
self.max_height,
|
css::max_length(self.max_height),
|
||||||
align_items
|
css::align(self.align_items)
|
||||||
).into_bump_str()
|
).into_bump_str()
|
||||||
)
|
)
|
||||||
.children(children)
|
.children(children)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use crate::{bumpalo, style, Align, Bus, Element, Length, Style, Widget};
|
//! Decorate content and apply alignment.
|
||||||
|
use crate::{bumpalo, css, Align, Bus, Css, Element, Length, Widget};
|
||||||
|
|
||||||
|
pub use iced_style::container::{Style, StyleSheet};
|
||||||
|
|
||||||
/// An element decorating some content.
|
/// An element decorating some content.
|
||||||
///
|
///
|
||||||
@ -11,6 +14,7 @@ pub struct Container<'a, Message> {
|
|||||||
max_height: u32,
|
max_height: u32,
|
||||||
horizontal_alignment: Align,
|
horizontal_alignment: Align,
|
||||||
vertical_alignment: Align,
|
vertical_alignment: Align,
|
||||||
|
style_sheet: Box<dyn StyleSheet>,
|
||||||
content: Element<'a, Message>,
|
content: Element<'a, Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +35,7 @@ impl<'a, Message> Container<'a, Message> {
|
|||||||
max_height: u32::MAX,
|
max_height: u32::MAX,
|
||||||
horizontal_alignment: Align::Start,
|
horizontal_alignment: Align::Start,
|
||||||
vertical_alignment: Align::Start,
|
vertical_alignment: Align::Start,
|
||||||
|
style_sheet: Default::default(),
|
||||||
content: content.into(),
|
content: content.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +89,14 @@ impl<'a, Message> Container<'a, Message> {
|
|||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> Widget<Message> for Container<'a, Message>
|
impl<'a, Message> Widget<Message> for Container<'a, Message>
|
||||||
@ -94,17 +107,13 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
let column_class = style_sheet.insert(bump, Style::Column);
|
let column_class = style_sheet.insert(bump, css::Rule::Column);
|
||||||
|
|
||||||
let width = style::length(self.width);
|
let style = self.style_sheet.style();
|
||||||
let height = style::length(self.height);
|
|
||||||
|
|
||||||
let align_items = style::align(self.horizontal_alignment);
|
|
||||||
let justify_content = style::align(self.vertical_alignment);
|
|
||||||
|
|
||||||
let node = div(bump)
|
let node = div(bump)
|
||||||
.attr(
|
.attr(
|
||||||
@ -115,12 +124,17 @@ where
|
|||||||
"style",
|
"style",
|
||||||
bumpalo::format!(
|
bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
"width: {}; height: {}; max-width: {}px; align-items: {}; justify-content: {}",
|
"width: {}; height: {}; max-width: {}; align-items: {}; justify-content: {}; background: {}; color: {}; border-width: {}px; border-color: {}; border-radius: {}px",
|
||||||
width,
|
css::length(self.width),
|
||||||
height,
|
css::length(self.height),
|
||||||
self.max_width,
|
css::max_length(self.max_width),
|
||||||
align_items,
|
css::align(self.horizontal_alignment),
|
||||||
justify_content
|
css::align(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(),
|
.into_bump_str(),
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
use crate::{style, Bus, Element, Length, Widget};
|
//! Display images in your user interface.
|
||||||
|
use crate::{Bus, Css, Element, Hasher, Length, Widget};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
|
use std::{
|
||||||
|
hash::{Hash, Hasher as _},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
/// A frame that displays an image while keeping aspect ratio.
|
/// A frame that displays an image while keeping aspect ratio.
|
||||||
///
|
///
|
||||||
@ -14,7 +20,7 @@ use dodrio::bumpalo;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
/// The image path
|
/// The image path
|
||||||
pub path: String,
|
pub handle: Handle,
|
||||||
|
|
||||||
/// The width of the image
|
/// The width of the image
|
||||||
pub width: Length,
|
pub width: Length,
|
||||||
@ -27,9 +33,9 @@ impl Image {
|
|||||||
/// Creates a new [`Image`] with the given path.
|
/// Creates a new [`Image`] with the given path.
|
||||||
///
|
///
|
||||||
/// [`Image`]: struct.Image.html
|
/// [`Image`]: struct.Image.html
|
||||||
pub fn new<T: Into<String>>(path: T) -> Self {
|
pub fn new<T: Into<Handle>>(handle: T) -> Self {
|
||||||
Image {
|
Image {
|
||||||
path: path.into(),
|
handle: handle.into(),
|
||||||
width: Length::Shrink,
|
width: Length::Shrink,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
}
|
}
|
||||||
@ -57,11 +63,13 @@ impl<Message> Widget<Message> for Image {
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
_bus: &Bus<Message>,
|
_bus: &Bus<Message>,
|
||||||
_style_sheet: &mut style::Sheet<'b>,
|
_style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
let src = bumpalo::format!(in bump, "{}", self.path);
|
let src = bumpalo::format!(in bump, "{}", match self.handle.data.as_ref() {
|
||||||
|
Data::Path(path) => path.to_str().unwrap_or("")
|
||||||
|
});
|
||||||
|
|
||||||
let mut image = img(bump).attr("src", src.into_bump_str());
|
let mut image = img(bump).attr("src", src.into_bump_str());
|
||||||
|
|
||||||
@ -89,3 +97,74 @@ impl<'a, Message> From<Image> for Element<'a, Message> {
|
|||||||
Element::new(image)
|
Element::new(image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An [`Image`] handle.
|
||||||
|
///
|
||||||
|
/// [`Image`]: struct.Image.html
|
||||||
|
#[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.
|
||||||
|
///
|
||||||
|
/// [`Handle`]: struct.Handle.html
|
||||||
|
pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle {
|
||||||
|
Self::from_data(Data::Path(path.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
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`].
|
||||||
|
///
|
||||||
|
/// [`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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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`].
|
||||||
|
///
|
||||||
|
/// [`Image`]: struct.Image.html
|
||||||
|
#[derive(Clone, Hash)]
|
||||||
|
pub enum Data {
|
||||||
|
/// A remote image
|
||||||
|
Path(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
124
web/src/widget/progress_bar.rs
Normal file
124
web/src/widget/progress_bar.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
//! 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 {
|
||||||
|
range: RangeInclusive<f32>,
|
||||||
|
value: f32,
|
||||||
|
width: Length,
|
||||||
|
height: Option<Length>,
|
||||||
|
style: Box<dyn StyleSheet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgressBar {
|
||||||
|
/// Creates a new [`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()),
|
||||||
|
range,
|
||||||
|
width: Length::Fill,
|
||||||
|
height: None,
|
||||||
|
style: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> Widget<Message> for ProgressBar {
|
||||||
|
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> for Element<'a, Message>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(container: ProgressBar) -> Element<'a, Message> {
|
||||||
|
Element::new(container)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
use crate::{style, Bus, Color, Element, Widget};
|
//! Create choices using radio buttons.
|
||||||
|
use crate::{Bus, Css, Element, Widget};
|
||||||
|
|
||||||
|
pub use iced_style::radio::{Style, StyleSheet};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ pub struct Radio<Message> {
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
on_click: Message,
|
on_click: Message,
|
||||||
label: String,
|
label: String,
|
||||||
label_color: Option<Color>,
|
style: Box<dyn StyleSheet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> Radio<Message> {
|
impl<Message> Radio<Message> {
|
||||||
@ -55,15 +58,15 @@ impl<Message> Radio<Message> {
|
|||||||
is_selected: Some(value) == selected,
|
is_selected: Some(value) == selected,
|
||||||
on_click: f(value),
|
on_click: f(value),
|
||||||
label: String::from(label),
|
label: String::from(label),
|
||||||
label_color: None,
|
style: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the `Color` of the label of the [`Radio`].
|
/// Sets the style of the [`Radio`] button.
|
||||||
///
|
///
|
||||||
/// [`Radio`]: struct.Radio.html
|
/// [`Radio`]: struct.Radio.html
|
||||||
pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self {
|
pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self {
|
||||||
self.label_color = Some(color.into());
|
self.style = style.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,7 +79,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
_style_sheet: &mut style::Sheet<'b>,
|
_style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{style, Align, Bus, Element, Length, Style, Widget};
|
use crate::{css, Align, Bus, Css, Element, Length, Widget};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
@ -28,7 +28,7 @@ impl<'a, Message> Row<'a, Message> {
|
|||||||
Row {
|
Row {
|
||||||
spacing: 0,
|
spacing: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
width: Length::Shrink,
|
width: Length::Fill,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
max_width: u32::MAX,
|
max_width: u32::MAX,
|
||||||
max_height: u32::MAX,
|
max_height: u32::MAX,
|
||||||
@ -113,7 +113,7 @@ impl<'a, Message> Widget<Message> for Row<'a, Message> {
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
publish: &Bus<Message>,
|
publish: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
@ -123,18 +123,13 @@ impl<'a, Message> Widget<Message> for Row<'a, Message> {
|
|||||||
.map(|element| element.widget.node(bump, publish, style_sheet))
|
.map(|element| element.widget.node(bump, publish, style_sheet))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let row_class = style_sheet.insert(bump, Style::Row);
|
let row_class = style_sheet.insert(bump, css::Rule::Row);
|
||||||
|
|
||||||
let spacing_class =
|
let spacing_class =
|
||||||
style_sheet.insert(bump, Style::Spacing(self.spacing));
|
style_sheet.insert(bump, css::Rule::Spacing(self.spacing));
|
||||||
|
|
||||||
let padding_class =
|
let padding_class =
|
||||||
style_sheet.insert(bump, Style::Padding(self.padding));
|
style_sheet.insert(bump, css::Rule::Padding(self.padding));
|
||||||
|
|
||||||
let width = style::length(self.width);
|
|
||||||
let height = style::length(self.height);
|
|
||||||
|
|
||||||
let justify_content = style::align(self.align_items);
|
|
||||||
|
|
||||||
// TODO: Complete styling
|
// TODO: Complete styling
|
||||||
div(bump)
|
div(bump)
|
||||||
@ -145,12 +140,12 @@ impl<'a, Message> Widget<Message> for Row<'a, Message> {
|
|||||||
)
|
)
|
||||||
.attr("style", bumpalo::format!(
|
.attr("style", bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
"width: {}; height: {}; max-width: {}px; max-height: {}px; justify-content: {}",
|
"width: {}; height: {}; max-width: {}; max-height: {}; align-items: {}",
|
||||||
width,
|
css::length(self.width),
|
||||||
height,
|
css::length(self.height),
|
||||||
self.max_width,
|
css::max_length(self.max_width),
|
||||||
self.max_height,
|
css::max_length(self.max_height),
|
||||||
justify_content
|
css::align(self.align_items)
|
||||||
).into_bump_str()
|
).into_bump_str()
|
||||||
)
|
)
|
||||||
.children(children)
|
.children(children)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Navigate an endless amount of content with a scrollbar.
|
//! Navigate an endless amount of content with a scrollbar.
|
||||||
use crate::{bumpalo, style, Align, Bus, Column, Element, Length, Widget};
|
use crate::{bumpalo, css, Align, Bus, Column, Css, Element, Length, Widget};
|
||||||
|
|
||||||
|
pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet};
|
||||||
|
|
||||||
/// A widget that can vertically display an infinite amount of content with a
|
/// A widget that can vertically display an infinite amount of content with a
|
||||||
/// scrollbar.
|
/// scrollbar.
|
||||||
@ -9,6 +11,7 @@ pub struct Scrollable<'a, Message> {
|
|||||||
height: Length,
|
height: Length,
|
||||||
max_height: u32,
|
max_height: u32,
|
||||||
content: Column<'a, Message>,
|
content: Column<'a, Message>,
|
||||||
|
style: Box<dyn StyleSheet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> Scrollable<'a, Message> {
|
impl<'a, Message> Scrollable<'a, Message> {
|
||||||
@ -24,6 +27,7 @@ impl<'a, Message> Scrollable<'a, Message> {
|
|||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
max_height: u32::MAX,
|
max_height: u32::MAX,
|
||||||
content: Column::new(),
|
content: Column::new(),
|
||||||
|
style: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +89,14 @@ impl<'a, Message> Scrollable<'a, Message> {
|
|||||||
self
|
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`].
|
/// Adds an element to the [`Scrollable`].
|
||||||
///
|
///
|
||||||
/// [`Scrollable`]: struct.Scrollable.html
|
/// [`Scrollable`]: struct.Scrollable.html
|
||||||
@ -105,12 +117,14 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
let width = style::length(self.width);
|
let width = css::length(self.width);
|
||||||
let height = style::length(self.height);
|
let height = css::length(self.height);
|
||||||
|
|
||||||
|
// TODO: Scrollbar styling
|
||||||
|
|
||||||
let node = div(bump)
|
let node = div(bump)
|
||||||
.attr(
|
.attr(
|
||||||
@ -126,8 +140,6 @@ where
|
|||||||
)
|
)
|
||||||
.children(vec![self.content.node(bump, bus, style_sheet)]);
|
.children(vec![self.content.node(bump, bus, style_sheet)]);
|
||||||
|
|
||||||
// TODO: Complete styling
|
|
||||||
|
|
||||||
node.finish()
|
node.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
//!
|
//!
|
||||||
//! [`Slider`]: struct.Slider.html
|
//! [`Slider`]: struct.Slider.html
|
||||||
//! [`State`]: struct.State.html
|
//! [`State`]: struct.State.html
|
||||||
use crate::{style, Bus, Element, Length, Widget};
|
use crate::{Bus, Css, Element, Length, Widget};
|
||||||
|
|
||||||
|
pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet};
|
||||||
|
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
use std::{ops::RangeInclusive, rc::Rc};
|
use std::{ops::RangeInclusive, rc::Rc};
|
||||||
@ -38,6 +40,7 @@ pub struct Slider<'a, Message> {
|
|||||||
value: f32,
|
value: f32,
|
||||||
on_change: Rc<Box<dyn Fn(f32) -> Message>>,
|
on_change: Rc<Box<dyn Fn(f32) -> Message>>,
|
||||||
width: Length,
|
width: Length,
|
||||||
|
style: Box<dyn StyleSheet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> Slider<'a, Message> {
|
impl<'a, Message> Slider<'a, Message> {
|
||||||
@ -68,6 +71,7 @@ impl<'a, Message> Slider<'a, Message> {
|
|||||||
range,
|
range,
|
||||||
on_change: Rc::new(Box::new(on_change)),
|
on_change: Rc::new(Box::new(on_change)),
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
|
style: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +82,14 @@ impl<'a, Message> Slider<'a, Message> {
|
|||||||
self.width = width;
|
self.width = width;
|
||||||
self
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> Widget<Message> for Slider<'a, Message>
|
impl<'a, Message> Widget<Message> for Slider<'a, Message>
|
||||||
@ -88,7 +100,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
_style_sheet: &mut style::Sheet<'b>,
|
_style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
@ -103,7 +115,7 @@ where
|
|||||||
let event_bus = bus.clone();
|
let event_bus = bus.clone();
|
||||||
|
|
||||||
// TODO: Make `step` configurable
|
// TODO: Make `step` configurable
|
||||||
// TODO: Complete styling
|
// TODO: Styling
|
||||||
input(bump)
|
input(bump)
|
||||||
.attr("type", "range")
|
.attr("type", "range")
|
||||||
.attr("step", "0.01")
|
.attr("step", "0.01")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{style, Bus, Element, Length, Widget};
|
use crate::{css, Bus, Css, Element, Length, Widget};
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
|
|
||||||
/// An amount of empty space.
|
/// An amount of empty space.
|
||||||
@ -44,12 +44,12 @@ impl<'a, Message> Widget<Message> for Space {
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
_publish: &Bus<Message>,
|
_publish: &Bus<Message>,
|
||||||
_style_sheet: &mut style::Sheet<'b>,
|
_css: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
let width = style::length(self.width);
|
let width = css::length(self.width);
|
||||||
let height = style::length(self.height);
|
let height = css::length(self.height);
|
||||||
|
|
||||||
let style = bumpalo::format!(
|
let style = bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
style, Bus, Color, Element, Font, HorizontalAlignment, Length,
|
css, Bus, Color, Css, Element, Font, HorizontalAlignment, Length,
|
||||||
VerticalAlignment, Widget,
|
VerticalAlignment, Widget,
|
||||||
};
|
};
|
||||||
use dodrio::bumpalo;
|
use dodrio::bumpalo;
|
||||||
@ -112,15 +112,18 @@ impl<'a, Message> Widget<Message> for Text {
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
_publish: &Bus<Message>,
|
_publish: &Bus<Message>,
|
||||||
_style_sheet: &mut style::Sheet<'b>,
|
_style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
|
|
||||||
let content = bumpalo::format!(in bump, "{}", self.content);
|
let content = bumpalo::format!(in bump, "{}", self.content);
|
||||||
let color = style::color(self.color.unwrap_or(Color::BLACK));
|
let color = self
|
||||||
|
.color
|
||||||
|
.map(css::color)
|
||||||
|
.unwrap_or(String::from("inherit"));
|
||||||
|
|
||||||
let width = style::length(self.width);
|
let width = css::length(self.width);
|
||||||
let height = style::length(self.height);
|
let height = css::length(self.height);
|
||||||
|
|
||||||
let text_align = match self.horizontal_alignment {
|
let text_align = match self.horizontal_alignment {
|
||||||
HorizontalAlignment::Left => "left",
|
HorizontalAlignment::Left => "left",
|
||||||
@ -130,12 +133,16 @@ impl<'a, Message> Widget<Message> for Text {
|
|||||||
|
|
||||||
let style = bumpalo::format!(
|
let style = bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
"width: {}; height: {}; font-size: {}px; color: {}; text-align: {}",
|
"width: {}; height: {}; font-size: {}px; color: {}; text-align: {}; font-family: {}",
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
self.size.unwrap_or(20),
|
self.size.unwrap_or(20),
|
||||||
color,
|
color,
|
||||||
text_align
|
text_align,
|
||||||
|
match self.font {
|
||||||
|
Font::Default => "inherit",
|
||||||
|
Font::External { name, .. } => name,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: Complete styling
|
// TODO: Complete styling
|
||||||
|
@ -4,8 +4,11 @@
|
|||||||
//!
|
//!
|
||||||
//! [`TextInput`]: struct.TextInput.html
|
//! [`TextInput`]: struct.TextInput.html
|
||||||
//! [`State`]: struct.State.html
|
//! [`State`]: struct.State.html
|
||||||
use crate::{bumpalo, style, Bus, Element, Length, Style, Widget};
|
use crate::{bumpalo, css, Bus, Css, Element, Length, Widget};
|
||||||
use std::rc::Rc;
|
|
||||||
|
pub use iced_style::text_input::{Style, StyleSheet};
|
||||||
|
|
||||||
|
use std::{rc::Rc, u32};
|
||||||
|
|
||||||
/// A field that can be filled with text.
|
/// A field that can be filled with text.
|
||||||
///
|
///
|
||||||
@ -34,11 +37,12 @@ pub struct TextInput<'a, Message> {
|
|||||||
value: String,
|
value: String,
|
||||||
is_secure: bool,
|
is_secure: bool,
|
||||||
width: Length,
|
width: Length,
|
||||||
max_width: Length,
|
max_width: u32,
|
||||||
padding: u16,
|
padding: u16,
|
||||||
size: Option<u16>,
|
size: Option<u16>,
|
||||||
on_change: Rc<Box<dyn Fn(String) -> Message>>,
|
on_change: Rc<Box<dyn Fn(String) -> Message>>,
|
||||||
on_submit: Option<Message>,
|
on_submit: Option<Message>,
|
||||||
|
style_sheet: Box<dyn StyleSheet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> TextInput<'a, Message> {
|
impl<'a, Message> TextInput<'a, Message> {
|
||||||
@ -67,11 +71,12 @@ impl<'a, Message> TextInput<'a, Message> {
|
|||||||
value: String::from(value),
|
value: String::from(value),
|
||||||
is_secure: false,
|
is_secure: false,
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
max_width: Length::Shrink,
|
max_width: u32::MAX,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
size: None,
|
size: None,
|
||||||
on_change: Rc::new(Box::new(on_change)),
|
on_change: Rc::new(Box::new(on_change)),
|
||||||
on_submit: None,
|
on_submit: None,
|
||||||
|
style_sheet: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +99,7 @@ impl<'a, Message> TextInput<'a, Message> {
|
|||||||
/// Sets the maximum width of the [`TextInput`].
|
/// Sets the maximum width of the [`TextInput`].
|
||||||
///
|
///
|
||||||
/// [`TextInput`]: struct.TextInput.html
|
/// [`TextInput`]: struct.TextInput.html
|
||||||
pub fn max_width(mut self, max_width: Length) -> Self {
|
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||||
self.max_width = max_width;
|
self.max_width = max_width;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -123,6 +128,14 @@ impl<'a, Message> TextInput<'a, Message> {
|
|||||||
self.on_submit = Some(message);
|
self.on_submit = Some(message);
|
||||||
self
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> Widget<Message> for TextInput<'a, Message>
|
impl<'a, Message> Widget<Message> for TextInput<'a, Message>
|
||||||
@ -133,18 +146,19 @@ where
|
|||||||
&self,
|
&self,
|
||||||
bump: &'b bumpalo::Bump,
|
bump: &'b bumpalo::Bump,
|
||||||
bus: &Bus<Message>,
|
bus: &Bus<Message>,
|
||||||
style_sheet: &mut style::Sheet<'b>,
|
style_sheet: &mut Css<'b>,
|
||||||
) -> dodrio::Node<'b> {
|
) -> dodrio::Node<'b> {
|
||||||
use dodrio::builder::*;
|
use dodrio::builder::*;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
let width = style::length(self.width);
|
|
||||||
let max_width = style::length(self.max_width);
|
|
||||||
let padding_class =
|
let padding_class =
|
||||||
style_sheet.insert(bump, Style::Padding(self.padding));
|
style_sheet.insert(bump, css::Rule::Padding(self.padding));
|
||||||
|
|
||||||
let on_change = self.on_change.clone();
|
let on_change = self.on_change.clone();
|
||||||
let event_bus = bus.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)
|
input(bump)
|
||||||
.attr(
|
.attr(
|
||||||
@ -155,10 +169,15 @@ where
|
|||||||
"style",
|
"style",
|
||||||
bumpalo::format!(
|
bumpalo::format!(
|
||||||
in bump,
|
in bump,
|
||||||
"width: {}; max-width: {}; font-size: {}px",
|
"width: {}; max-width: {}; font-size: {}px; background: {}; border-width: {}px; border-color: {}; border-radius: {}px; color: {}",
|
||||||
width,
|
css::length(self.width),
|
||||||
max_width,
|
css::max_length(self.max_width),
|
||||||
self.size.unwrap_or(20)
|
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(),
|
.into_bump_str(),
|
||||||
)
|
)
|
||||||
@ -183,7 +202,17 @@ where
|
|||||||
Some(text_input) => text_input,
|
Some(text_input) => text_input,
|
||||||
};
|
};
|
||||||
|
|
||||||
event_bus.publish(on_change(text_input.value()));
|
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()
|
.finish()
|
||||||
}
|
}
|
||||||
@ -211,4 +240,12 @@ impl State {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`State`], representing a focused [`TextInput`].
|
||||||
|
///
|
||||||
|
/// [`State`]: struct.State.html
|
||||||
|
pub fn focused() -> Self {
|
||||||
|
// TODO
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user