Merge pull request #1748 from iced-rs/feature/software-renderer
Software renderer, runtime renderer fallback, and core consolidation
This commit is contained in:
commit
caf2836b1b
261 changed files with 5933 additions and 3961 deletions
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
|
@ -37,5 +37,5 @@ jobs:
|
||||||
run: cargo build --package tour --target wasm32-unknown-unknown
|
run: cargo build --package tour --target wasm32-unknown-unknown
|
||||||
- name: Check compilation of `todos` example
|
- name: Check compilation of `todos` example
|
||||||
run: cargo build --package todos --target wasm32-unknown-unknown
|
run: cargo build --package todos --target wasm32-unknown-unknown
|
||||||
- name: Check compilation of `integration_wgpu` example
|
- name: Check compilation of `integration` example
|
||||||
run: cargo build --package integration --target wasm32-unknown-unknown
|
run: cargo build --package integration --target wasm32-unknown-unknown
|
||||||
|
|
|
||||||
40
Cargo.toml
40
Cargo.toml
|
|
@ -12,14 +12,21 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
|
||||||
categories = ["gui"]
|
categories = ["gui"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["wgpu", "tiny-skia"]
|
||||||
|
# Enable the `wgpu` GPU-accelerated renderer backend
|
||||||
|
wgpu = ["iced_renderer/wgpu"]
|
||||||
|
# Enable the `tiny-skia` software renderer backend
|
||||||
|
tiny-skia = ["iced_renderer/tiny-skia"]
|
||||||
# Enables the `Image` widget
|
# Enables the `Image` widget
|
||||||
image = ["iced_wgpu/image", "image_rs"]
|
image = ["iced_widget/image", "image_rs"]
|
||||||
# Enables the `Svg` widget
|
# Enables the `Svg` widget
|
||||||
svg = ["iced_wgpu/svg"]
|
svg = ["iced_widget/svg"]
|
||||||
# Enables the `Canvas` widget
|
# Enables the `Canvas` widget
|
||||||
canvas = ["iced_graphics/canvas"]
|
canvas = ["iced_widget/canvas"]
|
||||||
# Enables the `QRCode` widget
|
# Enables the `QRCode` widget
|
||||||
qr_code = ["iced_graphics/qr_code"]
|
qr_code = ["iced_widget/qr_code"]
|
||||||
|
# Enables lazy widgets
|
||||||
|
lazy = ["iced_widget/lazy"]
|
||||||
# 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
|
# Enables `tokio` as the `executor::Default` on native platforms
|
||||||
|
|
@ -32,11 +39,8 @@ smol = ["iced_futures/smol"]
|
||||||
palette = ["iced_core/palette"]
|
palette = ["iced_core/palette"]
|
||||||
# Enables querying system information
|
# Enables querying system information
|
||||||
system = ["iced_winit/system"]
|
system = ["iced_winit/system"]
|
||||||
# Enables chrome traces
|
# Enables the advanced module
|
||||||
chrome-trace = [
|
advanced = []
|
||||||
"iced_winit/chrome-trace",
|
|
||||||
"iced_wgpu/tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
@ -46,10 +50,12 @@ members = [
|
||||||
"core",
|
"core",
|
||||||
"futures",
|
"futures",
|
||||||
"graphics",
|
"graphics",
|
||||||
"lazy",
|
"runtime",
|
||||||
"native",
|
"renderer",
|
||||||
"style",
|
"style",
|
||||||
|
"tiny_skia",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
"widget",
|
||||||
"winit",
|
"winit",
|
||||||
"examples/*",
|
"examples/*",
|
||||||
]
|
]
|
||||||
|
|
@ -57,22 +63,16 @@ members = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_core = { version = "0.8", path = "core" }
|
iced_core = { version = "0.8", path = "core" }
|
||||||
iced_futures = { version = "0.6", path = "futures" }
|
iced_futures = { version = "0.6", path = "futures" }
|
||||||
iced_native = { version = "0.9", path = "native" }
|
iced_renderer = { version = "0.1", path = "renderer" }
|
||||||
iced_graphics = { version = "0.7", path = "graphics" }
|
iced_widget = { version = "0.1", path = "widget" }
|
||||||
iced_winit = { version = "0.8", path = "winit", features = ["application"] }
|
iced_winit = { version = "0.8", path = "winit", features = ["application"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1"
|
||||||
|
|
||||||
[dependencies.image_rs]
|
[dependencies.image_rs]
|
||||||
version = "0.24"
|
version = "0.24"
|
||||||
package = "image"
|
package = "image"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
|
||||||
iced_wgpu = { version = "0.9", path = "wgpu" }
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
iced_wgpu = { version = "0.9", path = "wgpu", features = ["webgl"] }
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
features = ["image", "svg", "canvas", "qr_code"]
|
features = ["image", "svg", "canvas", "qr_code"]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "iced_core"
|
name = "iced_core"
|
||||||
version = "0.8.0"
|
version = "0.8.1"
|
||||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "The essential concepts of Iced"
|
description = "The essential concepts of Iced"
|
||||||
|
|
@ -9,6 +9,8 @@ repository = "https://github.com/iced-rs/iced"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
|
thiserror = "1"
|
||||||
|
twox-hash = { version = "1.5", default-features = false }
|
||||||
|
|
||||||
[dependencies.palette]
|
[dependencies.palette]
|
||||||
version = "0.6"
|
version = "0.6"
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,6 @@ pub enum Alignment {
|
||||||
|
|
||||||
/// Align at the end of the axis.
|
/// Align at the end of the axis.
|
||||||
End,
|
End,
|
||||||
|
|
||||||
/// Fill the entire axis.
|
|
||||||
Fill,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Horizontal> for Alignment {
|
impl From<Horizontal> for Alignment {
|
||||||
|
|
|
||||||
23
core/src/clipboard.rs
Normal file
23
core/src/clipboard.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
//! Access the clipboard.
|
||||||
|
|
||||||
|
/// A buffer for short-term storage and transfer within and between
|
||||||
|
/// applications.
|
||||||
|
pub trait Clipboard {
|
||||||
|
/// Reads the current content of the [`Clipboard`] as text.
|
||||||
|
fn read(&self) -> Option<String>;
|
||||||
|
|
||||||
|
/// Writes the given text contents to the [`Clipboard`].
|
||||||
|
fn write(&mut self, contents: String);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A null implementation of the [`Clipboard`] trait.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Null;
|
||||||
|
|
||||||
|
impl Clipboard for Null {
|
||||||
|
fn read(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _contents: String) {}
|
||||||
|
}
|
||||||
|
|
@ -90,41 +90,65 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
|
||||||
/// We compose the previous __messages__ with the index of the counter
|
/// We compose the previous __messages__ with the index of the counter
|
||||||
/// producing them. Let's implement our __view logic__ now:
|
/// producing them. Let's implement our __view logic__ now:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// # mod counter {
|
/// # mod counter {
|
||||||
/// # type Text<'a> = iced_native::widget::Text<'a, iced_native::renderer::Null>;
|
|
||||||
/// #
|
|
||||||
/// # #[derive(Debug, Clone, Copy)]
|
/// # #[derive(Debug, Clone, Copy)]
|
||||||
/// # pub enum Message {}
|
/// # pub enum Message {}
|
||||||
/// # pub struct Counter;
|
/// # pub struct Counter;
|
||||||
/// #
|
/// #
|
||||||
/// # impl Counter {
|
/// # impl Counter {
|
||||||
/// # pub fn view(&mut self) -> Text {
|
/// # pub fn view(
|
||||||
/// # Text::new("")
|
/// # &self,
|
||||||
|
/// # ) -> iced_core::Element<Message, iced_core::renderer::Null> {
|
||||||
|
/// # unimplemented!()
|
||||||
/// # }
|
/// # }
|
||||||
/// # }
|
/// # }
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
/// # mod iced_wgpu {
|
/// # mod iced {
|
||||||
/// # pub use iced_native::renderer::Null as Renderer;
|
/// # pub use iced_core::renderer::Null as Renderer;
|
||||||
|
/// # pub use iced_core::Element;
|
||||||
|
/// #
|
||||||
|
/// # pub mod widget {
|
||||||
|
/// # pub struct Row<Message> {
|
||||||
|
/// # _t: std::marker::PhantomData<Message>,
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
/// # use counter::Counter;
|
/// # impl<Message> Row<Message> {
|
||||||
/// #
|
/// # pub fn new() -> Self {
|
||||||
/// # struct ManyCounters {
|
/// # unimplemented!()
|
||||||
/// # counters: Vec<Counter>,
|
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug, Clone, Copy)]
|
/// # pub fn spacing(mut self, _: u32) -> Self {
|
||||||
/// # pub enum Message {
|
/// # unimplemented!()
|
||||||
/// # Counter(usize, counter::Message)
|
|
||||||
/// # }
|
/// # }
|
||||||
/// use iced_native::Element;
|
/// #
|
||||||
/// use iced_native::widget::Row;
|
/// # pub fn push(
|
||||||
/// use iced_wgpu::Renderer;
|
/// # mut self,
|
||||||
|
/// # _: iced_core::Element<Message, iced_core::renderer::Null>,
|
||||||
|
/// # ) -> Self {
|
||||||
|
/// # unimplemented!()
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// use counter::Counter;
|
||||||
|
///
|
||||||
|
/// use iced::widget::Row;
|
||||||
|
/// use iced::{Element, Renderer};
|
||||||
|
///
|
||||||
|
/// struct ManyCounters {
|
||||||
|
/// counters: Vec<Counter>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Debug, Clone, Copy)]
|
||||||
|
/// pub enum Message {
|
||||||
|
/// Counter(usize, counter::Message),
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// impl ManyCounters {
|
/// impl ManyCounters {
|
||||||
/// pub fn view(&mut self) -> Row<Message, Renderer> {
|
/// pub fn view(&mut self) -> Row<Message> {
|
||||||
/// // We can quickly populate a `Row` by folding over our counters
|
/// // We can quickly populate a `Row` by folding over our counters
|
||||||
/// self.counters.iter_mut().enumerate().fold(
|
/// self.counters.iter_mut().enumerate().fold(
|
||||||
/// Row::new().spacing(20),
|
/// Row::new().spacing(20),
|
||||||
|
|
@ -137,9 +161,10 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
|
||||||
/// // Here we turn our `Element<counter::Message>` into
|
/// // Here we turn our `Element<counter::Message>` into
|
||||||
/// // an `Element<Message>` by combining the `index` and the
|
/// // an `Element<Message>` by combining the `index` and the
|
||||||
/// // message of the `element`.
|
/// // message of the `element`.
|
||||||
/// element.map(move |message| Message::Counter(index, message))
|
/// element
|
||||||
|
/// .map(move |message| Message::Counter(index, message)),
|
||||||
/// )
|
/// )
|
||||||
/// }
|
/// },
|
||||||
/// )
|
/// )
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
|
@ -62,7 +62,7 @@ impl Status {
|
||||||
/// `Captured` takes precedence over `Ignored`:
|
/// `Captured` takes precedence over `Ignored`:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use iced_native::event::Status;
|
/// use iced_core::event::Status;
|
||||||
///
|
///
|
||||||
/// assert_eq!(Status::Ignored.merge(Status::Ignored), Status::Ignored);
|
/// assert_eq!(Status::Ignored.merge(Status::Ignored), Status::Ignored);
|
||||||
/// assert_eq!(Status::Ignored.merge(Status::Captured), Status::Captured);
|
/// assert_eq!(Status::Ignored.merge(Status::Captured), Status::Captured);
|
||||||
|
|
@ -81,32 +81,6 @@ where
|
||||||
let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
|
let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
|
||||||
nodes.resize(items.len(), Node::default());
|
nodes.resize(items.len(), Node::default());
|
||||||
|
|
||||||
if align_items == Alignment::Fill {
|
|
||||||
let mut fill_cross = axis.cross(limits.min());
|
|
||||||
|
|
||||||
items.iter().for_each(|child| {
|
|
||||||
let cross_fill_factor = match axis {
|
|
||||||
Axis::Horizontal => child.as_widget().height(),
|
|
||||||
Axis::Vertical => child.as_widget().width(),
|
|
||||||
}
|
|
||||||
.fill_factor();
|
|
||||||
|
|
||||||
if cross_fill_factor == 0 {
|
|
||||||
let (max_width, max_height) = axis.pack(available, max_cross);
|
|
||||||
|
|
||||||
let child_limits =
|
|
||||||
Limits::new(Size::ZERO, Size::new(max_width, max_height));
|
|
||||||
|
|
||||||
let layout = child.as_widget().layout(renderer, &child_limits);
|
|
||||||
let size = layout.size();
|
|
||||||
|
|
||||||
fill_cross = fill_cross.max(axis.cross(size));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cross = fill_cross;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, child) in items.iter().enumerate() {
|
for (i, child) in items.iter().enumerate() {
|
||||||
let fill_factor = match axis {
|
let fill_factor = match axis {
|
||||||
Axis::Horizontal => child.as_widget().width(),
|
Axis::Horizontal => child.as_widget().width(),
|
||||||
|
|
@ -115,31 +89,16 @@ where
|
||||||
.fill_factor();
|
.fill_factor();
|
||||||
|
|
||||||
if fill_factor == 0 {
|
if fill_factor == 0 {
|
||||||
let (min_width, min_height) = if align_items == Alignment::Fill {
|
let (max_width, max_height) = axis.pack(available, max_cross);
|
||||||
axis.pack(0.0, cross)
|
|
||||||
} else {
|
|
||||||
axis.pack(0.0, 0.0)
|
|
||||||
};
|
|
||||||
|
|
||||||
let (max_width, max_height) = if align_items == Alignment::Fill {
|
let child_limits =
|
||||||
axis.pack(available, cross)
|
Limits::new(Size::ZERO, Size::new(max_width, max_height));
|
||||||
} else {
|
|
||||||
axis.pack(available, max_cross)
|
|
||||||
};
|
|
||||||
|
|
||||||
let child_limits = Limits::new(
|
|
||||||
Size::new(min_width, min_height),
|
|
||||||
Size::new(max_width, max_height),
|
|
||||||
);
|
|
||||||
|
|
||||||
let layout = child.as_widget().layout(renderer, &child_limits);
|
let layout = child.as_widget().layout(renderer, &child_limits);
|
||||||
let size = layout.size();
|
let size = layout.size();
|
||||||
|
|
||||||
available -= axis.main(size);
|
available -= axis.main(size);
|
||||||
|
|
||||||
if align_items != Alignment::Fill {
|
|
||||||
cross = cross.max(axis.cross(size));
|
cross = cross.max(axis.cross(size));
|
||||||
}
|
|
||||||
|
|
||||||
nodes[i] = layout;
|
nodes[i] = layout;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -164,17 +123,10 @@ where
|
||||||
max_main
|
max_main
|
||||||
};
|
};
|
||||||
|
|
||||||
let (min_width, min_height) = if align_items == Alignment::Fill {
|
let (min_width, min_height) =
|
||||||
axis.pack(min_main, cross)
|
axis.pack(min_main, axis.cross(limits.min()));
|
||||||
} else {
|
|
||||||
axis.pack(min_main, axis.cross(limits.min()))
|
|
||||||
};
|
|
||||||
|
|
||||||
let (max_width, max_height) = if align_items == Alignment::Fill {
|
let (max_width, max_height) = axis.pack(max_main, max_cross);
|
||||||
axis.pack(max_main, cross)
|
|
||||||
} else {
|
|
||||||
axis.pack(max_main, max_cross)
|
|
||||||
};
|
|
||||||
|
|
||||||
let child_limits = Limits::new(
|
let child_limits = Limits::new(
|
||||||
Size::new(min_width, min_height),
|
Size::new(min_width, min_height),
|
||||||
|
|
@ -182,10 +134,7 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
let layout = child.as_widget().layout(renderer, &child_limits);
|
let layout = child.as_widget().layout(renderer, &child_limits);
|
||||||
|
|
||||||
if align_items != Alignment::Fill {
|
|
||||||
cross = cross.max(axis.cross(layout.size()));
|
cross = cross.max(axis.cross(layout.size()));
|
||||||
}
|
|
||||||
|
|
||||||
nodes[i] = layout;
|
nodes[i] = layout;
|
||||||
}
|
}
|
||||||
|
|
@ -56,9 +56,6 @@ impl Node {
|
||||||
Alignment::End => {
|
Alignment::End => {
|
||||||
self.bounds.x += space.width - self.bounds.width;
|
self.bounds.x += space.width - self.bounds.width;
|
||||||
}
|
}
|
||||||
Alignment::Fill => {
|
|
||||||
self.bounds.width = space.width;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match vertical_alignment {
|
match vertical_alignment {
|
||||||
|
|
@ -69,9 +66,6 @@ impl Node {
|
||||||
Alignment::End => {
|
Alignment::End => {
|
||||||
self.bounds.y += space.height - self.bounds.height;
|
self.bounds.y += space.height - self.bounds.height;
|
||||||
}
|
}
|
||||||
Alignment::Fill => {
|
|
||||||
self.bounds.height = space.height;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,31 +25,57 @@
|
||||||
#![forbid(unsafe_code, rust_2018_idioms)]
|
#![forbid(unsafe_code, rust_2018_idioms)]
|
||||||
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
|
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
|
||||||
pub mod alignment;
|
pub mod alignment;
|
||||||
|
pub mod clipboard;
|
||||||
|
pub mod event;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
|
pub mod gradient;
|
||||||
|
pub mod image;
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
|
pub mod layout;
|
||||||
pub mod mouse;
|
pub mod mouse;
|
||||||
|
pub mod overlay;
|
||||||
|
pub mod renderer;
|
||||||
|
pub mod svg;
|
||||||
|
pub mod text;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
pub mod touch;
|
||||||
|
pub mod widget;
|
||||||
|
pub mod window;
|
||||||
|
|
||||||
mod background;
|
mod background;
|
||||||
mod color;
|
mod color;
|
||||||
mod content_fit;
|
mod content_fit;
|
||||||
|
mod element;
|
||||||
|
mod hasher;
|
||||||
mod length;
|
mod length;
|
||||||
mod padding;
|
mod padding;
|
||||||
mod pixels;
|
mod pixels;
|
||||||
mod point;
|
mod point;
|
||||||
mod rectangle;
|
mod rectangle;
|
||||||
|
mod shell;
|
||||||
mod size;
|
mod size;
|
||||||
mod vector;
|
mod vector;
|
||||||
|
|
||||||
pub use alignment::Alignment;
|
pub use alignment::Alignment;
|
||||||
pub use background::Background;
|
pub use background::Background;
|
||||||
|
pub use clipboard::Clipboard;
|
||||||
pub use color::Color;
|
pub use color::Color;
|
||||||
pub use content_fit::ContentFit;
|
pub use content_fit::ContentFit;
|
||||||
|
pub use element::Element;
|
||||||
|
pub use event::Event;
|
||||||
pub use font::Font;
|
pub use font::Font;
|
||||||
|
pub use gradient::Gradient;
|
||||||
|
pub use hasher::Hasher;
|
||||||
|
pub use layout::Layout;
|
||||||
pub use length::Length;
|
pub use length::Length;
|
||||||
|
pub use overlay::Overlay;
|
||||||
pub use padding::Padding;
|
pub use padding::Padding;
|
||||||
pub use pixels::Pixels;
|
pub use pixels::Pixels;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
pub use rectangle::Rectangle;
|
pub use rectangle::Rectangle;
|
||||||
|
pub use renderer::Renderer;
|
||||||
|
pub use shell::Shell;
|
||||||
pub use size::Size;
|
pub use size::Size;
|
||||||
|
pub use text::Text;
|
||||||
pub use vector::Vector;
|
pub use vector::Vector;
|
||||||
|
pub use widget::Widget;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
//! Handle mouse events.
|
//! Handle mouse events.
|
||||||
|
pub mod click;
|
||||||
|
|
||||||
mod button;
|
mod button;
|
||||||
mod event;
|
mod event;
|
||||||
mod interaction;
|
mod interaction;
|
||||||
|
|
||||||
pub use button::Button;
|
pub use button::Button;
|
||||||
|
pub use click::Click;
|
||||||
pub use event::{Event, ScrollDelta};
|
pub use event::{Event, ScrollDelta};
|
||||||
pub use interaction::Interaction;
|
pub use interaction::Interaction;
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,8 @@
|
||||||
mod element;
|
mod element;
|
||||||
mod group;
|
mod group;
|
||||||
|
|
||||||
pub mod menu;
|
|
||||||
|
|
||||||
pub use element::Element;
|
pub use element::Element;
|
||||||
pub use group::Group;
|
pub use group::Group;
|
||||||
pub use menu::Menu;
|
|
||||||
|
|
||||||
use crate::event::{self, Event};
|
use crate::event::{self, Event};
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
use iced_core::{Point, Rectangle, Size};
|
|
||||||
|
|
||||||
use crate::event;
|
use crate::event;
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
use crate::mouse;
|
use crate::mouse;
|
||||||
use crate::overlay;
|
use crate::overlay;
|
||||||
use crate::renderer;
|
use crate::renderer;
|
||||||
use crate::widget;
|
use crate::widget;
|
||||||
use crate::{Clipboard, Event, Layout, Overlay, Shell};
|
use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size};
|
||||||
|
|
||||||
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
|
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
|
||||||
/// children.
|
/// children.
|
||||||
|
|
@ -77,12 +77,14 @@ impl Padding {
|
||||||
/// Fits the [`Padding`] between the provided `inner` and `outer` [`Size`].
|
/// Fits the [`Padding`] between the provided `inner` and `outer` [`Size`].
|
||||||
pub fn fit(self, inner: Size, outer: Size) -> Self {
|
pub fn fit(self, inner: Size, outer: Size) -> Self {
|
||||||
let available = (outer - inner).max(Size::ZERO);
|
let available = (outer - inner).max(Size::ZERO);
|
||||||
|
let new_top = self.top.min(available.height);
|
||||||
|
let new_left = self.left.min(available.width);
|
||||||
|
|
||||||
Padding {
|
Padding {
|
||||||
top: self.top.min(available.height / 2.0),
|
top: new_top,
|
||||||
right: self.right.min(available.width / 2.0),
|
bottom: self.bottom.min(available.height - new_top),
|
||||||
bottom: self.bottom.min(available.height / 2.0),
|
left: new_left,
|
||||||
left: self.left.min(available.width / 2.0),
|
right: self.right.min(available.width - new_left),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//! Write your own renderer.
|
//! Write your own renderer.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
mod null;
|
mod null;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub use null::Null;
|
pub use null::Null;
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::renderer::{self, Renderer};
|
use crate::renderer::{self, Renderer};
|
||||||
use crate::text::{self, Text};
|
use crate::text::{self, Text};
|
||||||
use crate::{Background, Font, Point, Rectangle, Size, Theme, Vector};
|
use crate::{Background, Font, Point, Rectangle, Size, Vector};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ impl Null {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer for Null {
|
impl Renderer for Null {
|
||||||
type Theme = Theme;
|
type Theme = ();
|
||||||
|
|
||||||
fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {}
|
fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {}
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{Padding, Vector};
|
use crate::{Padding, Vector};
|
||||||
|
|
||||||
/// An amount of space in 2 dimensions.
|
/// An amount of space in 2 dimensions.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Size<T = f32> {
|
pub struct Size<T = f32> {
|
||||||
/// The width.
|
/// The width.
|
||||||
pub width: T,
|
pub width: T,
|
||||||
|
|
|
||||||
|
|
@ -1,98 +1,21 @@
|
||||||
//! Use the built-in widgets or create your own.
|
//! Create custom widgets and operate on them.
|
||||||
//!
|
|
||||||
//! # Built-in widgets
|
|
||||||
//! Every built-in drawable widget has its own module with a `Renderer` trait
|
|
||||||
//! that must be implemented by a [renderer] before being able to use it as
|
|
||||||
//! a [`Widget`].
|
|
||||||
//!
|
|
||||||
//! # 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.
|
|
||||||
//!
|
|
||||||
//! [renderer]: crate::renderer
|
|
||||||
pub mod button;
|
|
||||||
pub mod checkbox;
|
|
||||||
pub mod column;
|
|
||||||
pub mod container;
|
|
||||||
pub mod helpers;
|
|
||||||
pub mod image;
|
|
||||||
pub mod operation;
|
pub mod operation;
|
||||||
pub mod pane_grid;
|
|
||||||
pub mod pick_list;
|
|
||||||
pub mod progress_bar;
|
|
||||||
pub mod radio;
|
|
||||||
pub mod row;
|
|
||||||
pub mod rule;
|
|
||||||
pub mod scrollable;
|
|
||||||
pub mod slider;
|
|
||||||
pub mod space;
|
|
||||||
pub mod svg;
|
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod text_input;
|
|
||||||
pub mod toggler;
|
|
||||||
pub mod tooltip;
|
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
pub mod vertical_slider;
|
|
||||||
|
|
||||||
mod action;
|
|
||||||
mod id;
|
mod id;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use button::Button;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use checkbox::Checkbox;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use column::Column;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use container::Container;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use helpers::*;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use image::Image;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use pane_grid::PaneGrid;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use pick_list::PickList;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use progress_bar::ProgressBar;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use radio::Radio;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use row::Row;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use rule::Rule;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use scrollable::Scrollable;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use slider::Slider;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use space::Space;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use svg::Svg;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use text::Text;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use text_input::TextInput;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use toggler::Toggler;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use tooltip::Tooltip;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use tree::Tree;
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use vertical_slider::VerticalSlider;
|
|
||||||
|
|
||||||
pub use action::Action;
|
|
||||||
pub use id::Id;
|
pub use id::Id;
|
||||||
pub use operation::Operation;
|
pub use operation::Operation;
|
||||||
|
pub use text::Text;
|
||||||
|
pub use tree::Tree;
|
||||||
|
|
||||||
use crate::event::{self, Event};
|
use crate::event::{self, Event};
|
||||||
use crate::layout;
|
use crate::layout::{self, Layout};
|
||||||
use crate::mouse;
|
use crate::mouse;
|
||||||
use crate::overlay;
|
use crate::overlay;
|
||||||
use crate::renderer;
|
use crate::renderer;
|
||||||
use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};
|
use crate::{Clipboard, Length, Point, Rectangle, Shell};
|
||||||
|
|
||||||
/// A component that displays information and allows interaction.
|
/// A component that displays information and allows interaction.
|
||||||
///
|
///
|
||||||
|
|
@ -24,7 +24,7 @@ impl Id {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Internal {
|
enum Internal {
|
||||||
Unique(usize),
|
Unique(usize),
|
||||||
Custom(borrow::Cow<'static, str>),
|
Custom(borrow::Cow<'static, str>),
|
||||||
}
|
}
|
||||||
226
core/src/widget/operation.rs
Normal file
226
core/src/widget/operation.rs
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
//! Query or update internal widget state.
|
||||||
|
pub mod focusable;
|
||||||
|
pub mod scrollable;
|
||||||
|
pub mod text_input;
|
||||||
|
|
||||||
|
pub use focusable::Focusable;
|
||||||
|
pub use scrollable::Scrollable;
|
||||||
|
pub use text_input::TextInput;
|
||||||
|
|
||||||
|
use crate::widget::Id;
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
use std::fmt;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A piece of logic that can traverse the widget tree of an application in
|
||||||
|
/// order to query or update some widget state.
|
||||||
|
pub trait Operation<T> {
|
||||||
|
/// Operates on a widget that contains other widgets.
|
||||||
|
///
|
||||||
|
/// The `operate_on_children` function can be called to return control to
|
||||||
|
/// the widget tree and keep traversing it.
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&Id>,
|
||||||
|
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Operates on a widget that can be focused.
|
||||||
|
fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {}
|
||||||
|
|
||||||
|
/// Operates on a widget that can be scrolled.
|
||||||
|
fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {}
|
||||||
|
|
||||||
|
/// Operates on a widget that has text input.
|
||||||
|
fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
|
||||||
|
|
||||||
|
/// Operates on a custom widget with some state.
|
||||||
|
fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {}
|
||||||
|
|
||||||
|
/// Finishes the [`Operation`] and returns its [`Outcome`].
|
||||||
|
fn finish(&self) -> Outcome<T> {
|
||||||
|
Outcome::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of an [`Operation`].
|
||||||
|
pub enum Outcome<T> {
|
||||||
|
/// The [`Operation`] produced no result.
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// The [`Operation`] produced some result.
|
||||||
|
Some(T),
|
||||||
|
|
||||||
|
/// The [`Operation`] needs to be followed by another [`Operation`].
|
||||||
|
Chain(Box<dyn Operation<T>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Debug for Outcome<T>
|
||||||
|
where
|
||||||
|
T: fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::None => write!(f, "Outcome::None"),
|
||||||
|
Self::Some(output) => write!(f, "Outcome::Some({output:?})"),
|
||||||
|
Self::Chain(_) => write!(f, "Outcome::Chain(...)"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps the output of an [`Operation`] using the given function.
|
||||||
|
pub fn map<A, B>(
|
||||||
|
operation: Box<dyn Operation<A>>,
|
||||||
|
f: impl Fn(A) -> B + 'static,
|
||||||
|
) -> impl Operation<B>
|
||||||
|
where
|
||||||
|
A: 'static,
|
||||||
|
B: 'static,
|
||||||
|
{
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
struct Map<A, B> {
|
||||||
|
operation: Box<dyn Operation<A>>,
|
||||||
|
f: Rc<dyn Fn(A) -> B>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, B> Operation<B> for Map<A, B>
|
||||||
|
where
|
||||||
|
A: 'static,
|
||||||
|
B: 'static,
|
||||||
|
{
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&Id>,
|
||||||
|
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
|
||||||
|
) {
|
||||||
|
struct MapRef<'a, A> {
|
||||||
|
operation: &'a mut dyn Operation<A>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, A, B> Operation<B> for MapRef<'a, A> {
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&Id>,
|
||||||
|
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
|
||||||
|
) {
|
||||||
|
let Self { operation, .. } = self;
|
||||||
|
|
||||||
|
operation.container(id, &mut |operation| {
|
||||||
|
operate_on_children(&mut MapRef { operation });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scrollable(
|
||||||
|
&mut self,
|
||||||
|
state: &mut dyn Scrollable,
|
||||||
|
id: Option<&Id>,
|
||||||
|
) {
|
||||||
|
self.operation.scrollable(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focusable(
|
||||||
|
&mut self,
|
||||||
|
state: &mut dyn Focusable,
|
||||||
|
id: Option<&Id>,
|
||||||
|
) {
|
||||||
|
self.operation.focusable(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_input(
|
||||||
|
&mut self,
|
||||||
|
state: &mut dyn TextInput,
|
||||||
|
id: Option<&Id>,
|
||||||
|
) {
|
||||||
|
self.operation.text_input(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
|
||||||
|
self.operation.custom(state, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Self { operation, .. } = self;
|
||||||
|
|
||||||
|
MapRef {
|
||||||
|
operation: operation.as_mut(),
|
||||||
|
}
|
||||||
|
.container(id, operate_on_children);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
|
||||||
|
self.operation.focusable(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) {
|
||||||
|
self.operation.scrollable(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
|
||||||
|
self.operation.text_input(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {
|
||||||
|
self.operation.custom(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self) -> Outcome<B> {
|
||||||
|
match self.operation.finish() {
|
||||||
|
Outcome::None => Outcome::None,
|
||||||
|
Outcome::Some(output) => Outcome::Some((self.f)(output)),
|
||||||
|
Outcome::Chain(next) => Outcome::Chain(Box::new(Map {
|
||||||
|
operation: next,
|
||||||
|
f: self.f.clone(),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map {
|
||||||
|
operation,
|
||||||
|
f: Rc::new(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces an [`Operation`] that applies the given [`Operation`] to the
|
||||||
|
/// children of a container with the given [`Id`].
|
||||||
|
pub fn scope<T: 'static>(
|
||||||
|
target: Id,
|
||||||
|
operation: impl Operation<T> + 'static,
|
||||||
|
) -> impl Operation<T> {
|
||||||
|
struct ScopedOperation<Message> {
|
||||||
|
target: Id,
|
||||||
|
operation: Box<dyn Operation<Message>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message: 'static> Operation<Message> for ScopedOperation<Message> {
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&Id>,
|
||||||
|
operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>),
|
||||||
|
) {
|
||||||
|
if id == Some(&self.target) {
|
||||||
|
operate_on_children(self.operation.as_mut());
|
||||||
|
} else {
|
||||||
|
operate_on_children(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self) -> Outcome<Message> {
|
||||||
|
match self.operation.finish() {
|
||||||
|
Outcome::Chain(next) => {
|
||||||
|
Outcome::Chain(Box::new(ScopedOperation {
|
||||||
|
target: self.target.clone(),
|
||||||
|
operation: next,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
outcome => outcome,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedOperation {
|
||||||
|
target,
|
||||||
|
operation: Box::new(operation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,27 +4,13 @@ use crate::layout;
|
||||||
use crate::renderer;
|
use crate::renderer;
|
||||||
use crate::text;
|
use crate::text;
|
||||||
use crate::widget::Tree;
|
use crate::widget::Tree;
|
||||||
use crate::{Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget};
|
use crate::{
|
||||||
|
Color, Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget,
|
||||||
|
};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub use iced_style::text::{Appearance, StyleSheet};
|
|
||||||
|
|
||||||
/// A paragraph of text.
|
/// A paragraph of text.
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use iced_native::Color;
|
|
||||||
/// #
|
|
||||||
/// # type Text<'a> = iced_native::widget::Text<'a, iced_native::renderer::Null>;
|
|
||||||
/// #
|
|
||||||
/// Text::new("I <3 iced!")
|
|
||||||
/// .size(40)
|
|
||||||
/// .style(Color::from([0.0, 0.0, 1.0]));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// 
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
pub struct Text<'a, Renderer>
|
pub struct Text<'a, Renderer>
|
||||||
where
|
where
|
||||||
|
|
@ -211,7 +197,7 @@ pub fn draw<Renderer>(
|
||||||
alignment::Vertical::Bottom => bounds.y + bounds.height,
|
alignment::Vertical::Bottom => bounds.y + bounds.height,
|
||||||
};
|
};
|
||||||
|
|
||||||
renderer.fill_text(crate::text::Text {
|
renderer.fill_text(crate::Text {
|
||||||
content,
|
content,
|
||||||
size: size.unwrap_or_else(|| renderer.default_size()),
|
size: size.unwrap_or_else(|| renderer.default_size()),
|
||||||
bounds: Rectangle { x, y, ..bounds },
|
bounds: Rectangle { x, y, ..bounds },
|
||||||
|
|
@ -252,12 +238,40 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, Renderer> From<&'a str> for Text<'a, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: text::Renderer,
|
||||||
|
Renderer::Theme: StyleSheet,
|
||||||
|
{
|
||||||
|
fn from(content: &'a str) -> Self {
|
||||||
|
Self::new(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer>
|
impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: text::Renderer + 'a,
|
Renderer: text::Renderer + 'a,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
{
|
{
|
||||||
fn from(contents: &'a str) -> Self {
|
fn from(content: &'a str) -> Self {
|
||||||
Text::new(contents).into()
|
Text::from(content).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The style sheet of some text.
|
||||||
|
pub trait StyleSheet {
|
||||||
|
/// The supported style of the [`StyleSheet`].
|
||||||
|
type Style: Default + Copy;
|
||||||
|
|
||||||
|
/// Produces the [`Appearance`] of some text.
|
||||||
|
fn appearance(&self, style: Self::Style) -> Appearance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The apperance of some text.
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Appearance {
|
||||||
|
/// The [`Color`] of the text.
|
||||||
|
///
|
||||||
|
/// The default, `None`, means using the inherited color.
|
||||||
|
pub color: Option<Color>,
|
||||||
|
}
|
||||||
10
core/src/window.rs
Normal file
10
core/src/window.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
//! Build window-based GUI applications.
|
||||||
|
mod event;
|
||||||
|
mod mode;
|
||||||
|
mod redraw_request;
|
||||||
|
mod user_attention;
|
||||||
|
|
||||||
|
pub use event::Event;
|
||||||
|
pub use mode::Mode;
|
||||||
|
pub use redraw_request::RedrawRequest;
|
||||||
|
pub use user_attention::UserAttention;
|
||||||
|
|
@ -5,8 +5,8 @@ use iced::widget::canvas::{
|
||||||
self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke,
|
self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke,
|
||||||
};
|
};
|
||||||
use iced::{
|
use iced::{
|
||||||
Application, Command, Element, Length, Point, Rectangle, Settings,
|
Application, Command, Element, Length, Point, Rectangle, Renderer,
|
||||||
Subscription, Theme,
|
Settings, Subscription, Theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
|
|
@ -75,11 +75,12 @@ impl<Message> canvas::Program<Message> for Arc {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_state: &Self::State,
|
_state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
_cursor: Cursor,
|
_cursor: Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let geometry = self.cache.draw(bounds.size(), |frame| {
|
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
|
||||||
let palette = theme.palette();
|
let palette = theme.palette();
|
||||||
|
|
||||||
let center = frame.center();
|
let center = frame.center();
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ mod bezier {
|
||||||
use iced::widget::canvas::{
|
use iced::widget::canvas::{
|
||||||
self, Canvas, Cursor, Frame, Geometry, Path, Stroke,
|
self, Canvas, Cursor, Frame, Geometry, Path, Stroke,
|
||||||
};
|
};
|
||||||
use iced::{Element, Length, Point, Rectangle, Theme};
|
use iced::{Element, Length, Point, Rectangle, Renderer, Theme};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
|
@ -152,22 +152,26 @@ mod bezier {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let content =
|
let content = self.state.cache.draw(
|
||||||
self.state.cache.draw(bounds.size(), |frame: &mut Frame| {
|
renderer,
|
||||||
|
bounds.size(),
|
||||||
|
|frame: &mut Frame| {
|
||||||
Curve::draw_all(self.curves, frame);
|
Curve::draw_all(self.curves, frame);
|
||||||
|
|
||||||
frame.stroke(
|
frame.stroke(
|
||||||
&Path::rectangle(Point::ORIGIN, frame.size()),
|
&Path::rectangle(Point::ORIGIN, frame.size()),
|
||||||
Stroke::default().with_width(2.0),
|
Stroke::default().with_width(2.0),
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(pending) = state {
|
if let Some(pending) = state {
|
||||||
let pending_curve = pending.draw(bounds, cursor);
|
let pending_curve = pending.draw(renderer, bounds, cursor);
|
||||||
|
|
||||||
vec![content, pending_curve]
|
vec![content, pending_curve]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -216,8 +220,13 @@ mod bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pending {
|
impl Pending {
|
||||||
fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Geometry {
|
fn draw(
|
||||||
let mut frame = Frame::new(bounds.size());
|
&self,
|
||||||
|
renderer: &Renderer,
|
||||||
|
bounds: Rectangle,
|
||||||
|
cursor: Cursor,
|
||||||
|
) -> Geometry {
|
||||||
|
let mut frame = Frame::new(renderer, bounds.size());
|
||||||
|
|
||||||
if let Some(cursor_position) = cursor.position_in(&bounds) {
|
if let Some(cursor_position) = cursor.position_in(&bounds) {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use iced::widget::canvas::{
|
||||||
};
|
};
|
||||||
use iced::widget::{canvas, container};
|
use iced::widget::{canvas, container};
|
||||||
use iced::{
|
use iced::{
|
||||||
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
|
Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
|
||||||
Subscription, Theme, Vector,
|
Settings, Subscription, Theme, Vector,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
|
|
@ -83,17 +83,18 @@ impl Application for Clock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> canvas::Program<Message> for Clock {
|
impl<Message> canvas::Program<Message, Renderer> for Clock {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_state: &Self::State,
|
_state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
_cursor: Cursor,
|
_cursor: Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let clock = self.clock.draw(bounds.size(), |frame| {
|
let clock = self.clock.draw(renderer, bounds.size(), |frame| {
|
||||||
let center = frame.center();
|
let center = frame.center();
|
||||||
let radius = frame.width().min(frame.height()) / 2.0;
|
let radius = frame.width().min(frame.height()) / 2.0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use iced::widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path};
|
use iced::widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path};
|
||||||
use iced::widget::{column, row, text, Slider};
|
use iced::widget::{column, row, text, Slider};
|
||||||
use iced::{
|
use iced::{
|
||||||
alignment, Alignment, Color, Element, Length, Point, Rectangle, Sandbox,
|
alignment, Alignment, Color, Element, Length, Point, Rectangle, Renderer,
|
||||||
Settings, Size, Vector,
|
Sandbox, Settings, Size, Vector,
|
||||||
};
|
};
|
||||||
use palette::{self, convert::FromColor, Hsl, Srgb};
|
use palette::{self, convert::FromColor, Hsl, Srgb};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
@ -243,11 +243,12 @@ impl<Message> canvas::Program<Message> for Theme {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_state: &Self::State,
|
_state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &iced::Theme,
|
_theme: &iced::Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
_cursor: Cursor,
|
_cursor: Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let theme = self.canvas_cache.draw(bounds.size(), |frame| {
|
let theme = self.canvas_cache.draw(renderer, bounds.size(), |frame| {
|
||||||
self.draw(frame);
|
self.draw(frame);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["debug"] }
|
iced = { path = "../..", features = ["debug", "lazy"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
iced_lazy = { path = "../../lazy" }
|
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,8 @@ impl Sandbox for Component {
|
||||||
|
|
||||||
mod numeric_input {
|
mod numeric_input {
|
||||||
use iced::alignment::{self, Alignment};
|
use iced::alignment::{self, Alignment};
|
||||||
use iced::widget::{self, button, row, text, text_input};
|
use iced::widget::{button, component, row, text, text_input, Component};
|
||||||
use iced::{Element, Length};
|
use iced::{Element, Length, Renderer};
|
||||||
use iced_lazy::{self, Component};
|
|
||||||
|
|
||||||
pub struct NumericInput<Message> {
|
pub struct NumericInput<Message> {
|
||||||
value: Option<u32>,
|
value: Option<u32>,
|
||||||
|
|
@ -82,13 +81,7 @@ mod numeric_input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message, Renderer> Component<Message, Renderer> for NumericInput<Message>
|
impl<Message> Component<Message, Renderer> for NumericInput<Message> {
|
||||||
where
|
|
||||||
Renderer: iced_native::text::Renderer + 'static,
|
|
||||||
Renderer::Theme: widget::button::StyleSheet
|
|
||||||
+ widget::text_input::StyleSheet
|
|
||||||
+ widget::text::StyleSheet,
|
|
||||||
{
|
|
||||||
type State = ();
|
type State = ();
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
|
|
||||||
|
|
@ -127,7 +120,8 @@ mod numeric_input {
|
||||||
.horizontal_alignment(alignment::Horizontal::Center)
|
.horizontal_alignment(alignment::Horizontal::Center)
|
||||||
.vertical_alignment(alignment::Vertical::Center),
|
.vertical_alignment(alignment::Vertical::Center),
|
||||||
)
|
)
|
||||||
.width(50)
|
.width(40)
|
||||||
|
.height(40)
|
||||||
.on_press(on_press)
|
.on_press(on_press)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -145,23 +139,18 @@ mod numeric_input {
|
||||||
.padding(10),
|
.padding(10),
|
||||||
button("+", Event::IncrementPressed),
|
button("+", Event::IncrementPressed),
|
||||||
]
|
]
|
||||||
.align_items(Alignment::Fill)
|
.align_items(Alignment::Center)
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Renderer> From<NumericInput<Message>>
|
impl<'a, Message> From<NumericInput<Message>> for Element<'a, Message, Renderer>
|
||||||
for Element<'a, Message, Renderer>
|
|
||||||
where
|
where
|
||||||
Message: 'a,
|
Message: 'a,
|
||||||
Renderer: 'static + iced_native::text::Renderer,
|
|
||||||
Renderer::Theme: widget::button::StyleSheet
|
|
||||||
+ widget::text_input::StyleSheet
|
|
||||||
+ widget::text::StyleSheet,
|
|
||||||
{
|
{
|
||||||
fn from(numeric_input: NumericInput<Message>) -> Self {
|
fn from(numeric_input: NumericInput<Message>) -> Self {
|
||||||
iced_lazy::component(numeric_input)
|
component(numeric_input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../.." }
|
iced = { path = "../..", features = ["advanced"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
//! This example showcases a drawing a quad.
|
//! This example showcases a drawing a quad.
|
||||||
mod quad {
|
mod quad {
|
||||||
use iced_native::layout::{self, Layout};
|
use iced::advanced::layout::{self, Layout};
|
||||||
use iced_native::renderer;
|
use iced::advanced::renderer;
|
||||||
use iced_native::widget::{self, Widget};
|
use iced::advanced::widget::{self, Widget};
|
||||||
use iced_native::{Color, Element, Length, Point, Rectangle, Size};
|
use iced::{Color, Element, Length, Point, Rectangle, Size};
|
||||||
|
|
||||||
pub struct CustomQuad {
|
pub struct CustomQuad {
|
||||||
size: f32,
|
size: f32,
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../.." }
|
iced = { path = "../..", features = ["advanced"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ mod circle {
|
||||||
// Of course, you can choose to make the implementation renderer-agnostic,
|
// Of course, you can choose to make the implementation renderer-agnostic,
|
||||||
// if you wish to, by creating your own `Renderer` trait, which could be
|
// if you wish to, by creating your own `Renderer` trait, which could be
|
||||||
// implemented by `iced_wgpu` and other renderers.
|
// implemented by `iced_wgpu` and other renderers.
|
||||||
use iced_native::layout::{self, Layout};
|
use iced::advanced::layout::{self, Layout};
|
||||||
use iced_native::renderer;
|
use iced::advanced::renderer;
|
||||||
use iced_native::widget::{self, Widget};
|
use iced::advanced::widget::{self, Widget};
|
||||||
use iced_native::{Color, Element, Length, Point, Rectangle, Size};
|
use iced::{Color, Element, Length, Point, Rectangle, Size};
|
||||||
|
|
||||||
pub struct Circle {
|
pub struct Circle {
|
||||||
radius: f32,
|
radius: f32,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["tokio"] }
|
iced = { path = "../..", features = ["tokio"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
iced_futures = { path = "../../futures" }
|
|
||||||
|
|
||||||
[dependencies.reqwest]
|
[dependencies.reqwest]
|
||||||
version = "0.11"
|
version = "0.11"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use iced_native::subscription;
|
use iced::subscription;
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,3 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["debug"] }
|
iced = { path = "../..", features = ["debug"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use iced::alignment;
|
use iced::alignment;
|
||||||
use iced::executor;
|
use iced::executor;
|
||||||
|
use iced::subscription;
|
||||||
use iced::widget::{button, checkbox, container, text, Column};
|
use iced::widget::{button, checkbox, container, text, Column};
|
||||||
use iced::window;
|
use iced::window;
|
||||||
|
use iced::Event;
|
||||||
use iced::{
|
use iced::{
|
||||||
Alignment, Application, Command, Element, Length, Settings, Subscription,
|
Alignment, Application, Command, Element, Length, Settings, Subscription,
|
||||||
Theme,
|
Theme,
|
||||||
};
|
};
|
||||||
use iced_native::Event;
|
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
Events::run(Settings {
|
Events::run(Settings {
|
||||||
|
|
@ -17,13 +18,13 @@ pub fn main() -> iced::Result {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct Events {
|
struct Events {
|
||||||
last: Vec<iced_native::Event>,
|
last: Vec<Event>,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Message {
|
enum Message {
|
||||||
EventOccurred(iced_native::Event),
|
EventOccurred(Event),
|
||||||
Toggled(bool),
|
Toggled(bool),
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +71,7 @@ impl Application for Events {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
iced_native::subscription::events().map(Message::EventOccurred)
|
subscription::events().map(Message::EventOccurred)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ impl Application for GameOfLife {
|
||||||
self.grid
|
self.grid
|
||||||
.view()
|
.view()
|
||||||
.map(move |message| Message::Grid(message, version)),
|
.map(move |message| Message::Grid(message, version)),
|
||||||
controls
|
controls,
|
||||||
];
|
];
|
||||||
|
|
||||||
container(content)
|
container(content)
|
||||||
|
|
@ -211,8 +211,8 @@ mod grid {
|
||||||
Cache, Canvas, Cursor, Frame, Geometry, Path, Text,
|
Cache, Canvas, Cursor, Frame, Geometry, Path, Text,
|
||||||
};
|
};
|
||||||
use iced::{
|
use iced::{
|
||||||
alignment, mouse, Color, Element, Length, Point, Rectangle, Size,
|
alignment, mouse, Color, Element, Length, Point, Rectangle, Renderer,
|
||||||
Theme, Vector,
|
Size, Theme, Vector,
|
||||||
};
|
};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
@ -536,13 +536,14 @@ mod grid {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_interaction: &Interaction,
|
_interaction: &Interaction,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0);
|
let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0);
|
||||||
|
|
||||||
let life = self.life_cache.draw(bounds.size(), |frame| {
|
let life = self.life_cache.draw(renderer, bounds.size(), |frame| {
|
||||||
let background = Path::rectangle(Point::ORIGIN, frame.size());
|
let background = Path::rectangle(Point::ORIGIN, frame.size());
|
||||||
frame.fill(&background, Color::from_rgb8(0x40, 0x44, 0x4B));
|
frame.fill(&background, Color::from_rgb8(0x40, 0x44, 0x4B));
|
||||||
|
|
||||||
|
|
@ -565,7 +566,7 @@ mod grid {
|
||||||
});
|
});
|
||||||
|
|
||||||
let overlay = {
|
let overlay = {
|
||||||
let mut frame = Frame::new(bounds.size());
|
let mut frame = Frame::new(renderer, bounds.size());
|
||||||
|
|
||||||
let hovered_cell =
|
let hovered_cell =
|
||||||
cursor.position_in(&bounds).map(|position| {
|
cursor.position_in(&bounds).map(|position| {
|
||||||
|
|
@ -626,7 +627,8 @@ mod grid {
|
||||||
if self.scaling < 0.2 || !self.show_lines {
|
if self.scaling < 0.2 || !self.show_lines {
|
||||||
vec![life, overlay]
|
vec![life, overlay]
|
||||||
} else {
|
} else {
|
||||||
let grid = self.grid_cache.draw(bounds.size(), |frame| {
|
let grid =
|
||||||
|
self.grid_cache.draw(renderer, bounds.size(), |frame| {
|
||||||
frame.translate(center);
|
frame.translate(center);
|
||||||
frame.scale(self.scaling);
|
frame.scale(self.scaling);
|
||||||
frame.translate(self.translation);
|
frame.translate(self.translation);
|
||||||
|
|
@ -640,7 +642,8 @@ mod grid {
|
||||||
let width = 2.0 / Cell::SIZE as f32;
|
let width = 2.0 / Cell::SIZE as f32;
|
||||||
let color = Color::from_rgb8(70, 74, 83);
|
let color = Color::from_rgb8(70, 74, 83);
|
||||||
|
|
||||||
frame.translate(Vector::new(-width / 2.0, -width / 2.0));
|
frame
|
||||||
|
.translate(Vector::new(-width / 2.0, -width / 2.0));
|
||||||
|
|
||||||
for row in region.rows() {
|
for row in region.rows() {
|
||||||
frame.fill_rectangle(
|
frame.fill_rectangle(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,5 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../.." }
|
iced = { path = "../..", features = ["advanced"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
iced_graphics = { path = "../../graphics" }
|
iced_graphics = { path = "../../graphics" }
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,13 @@
|
||||||
//! This example showcases a simple native custom widget that renders using
|
//! This example showcases a simple native custom widget that renders using
|
||||||
//! arbitrary low-level geometry.
|
//! arbitrary low-level geometry.
|
||||||
mod rainbow {
|
mod rainbow {
|
||||||
// For now, to implement a custom native widget you will need to add
|
use iced_graphics::primitive::{ColoredVertex2D, Primitive};
|
||||||
// `iced_native` and `iced_wgpu` to your dependencies.
|
|
||||||
//
|
|
||||||
// Then, you simply need to define your widget type and implement the
|
|
||||||
// `iced_native::Widget` trait with the `iced_wgpu::Renderer`.
|
|
||||||
//
|
|
||||||
// Of course, you can choose to make the implementation renderer-agnostic,
|
|
||||||
// if you wish to, by creating your own `Renderer` trait, which could be
|
|
||||||
// implemented by `iced_wgpu` and other renderers.
|
|
||||||
use iced_graphics::renderer::{self, Renderer};
|
|
||||||
use iced_graphics::triangle::ColoredVertex2D;
|
|
||||||
use iced_graphics::{Backend, Primitive};
|
|
||||||
|
|
||||||
use iced_native::layout;
|
use iced::advanced::layout::{self, Layout};
|
||||||
use iced_native::widget::{self, Widget};
|
use iced::advanced::renderer;
|
||||||
use iced_native::{
|
use iced::advanced::widget::{self, Widget};
|
||||||
Element, Layout, Length, Point, Rectangle, Size, Vector,
|
use iced::{
|
||||||
|
Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
|
@ -27,10 +17,7 @@ mod rainbow {
|
||||||
Rainbow
|
Rainbow
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message, B, T> Widget<Message, Renderer<B, T>> for Rainbow
|
impl<Message> Widget<Message, Renderer> for Rainbow {
|
||||||
where
|
|
||||||
B: Backend,
|
|
||||||
{
|
|
||||||
fn width(&self) -> Length {
|
fn width(&self) -> Length {
|
||||||
Length::Fill
|
Length::Fill
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +28,7 @@ mod rainbow {
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
_renderer: &Renderer<B, T>,
|
_renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
let size = limits.width(Length::Fill).resolve(Size::ZERO);
|
let size = limits.width(Length::Fill).resolve(Size::ZERO);
|
||||||
|
|
@ -52,15 +39,15 @@ mod rainbow {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_tree: &widget::Tree,
|
_tree: &widget::Tree,
|
||||||
renderer: &mut Renderer<B, T>,
|
renderer: &mut Renderer,
|
||||||
_theme: &T,
|
_theme: &Theme,
|
||||||
_style: &renderer::Style,
|
_style: &renderer::Style,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
use iced_graphics::triangle::Mesh2D;
|
use iced::advanced::Renderer as _;
|
||||||
use iced_native::Renderer as _;
|
use iced_graphics::primitive::Mesh2D;
|
||||||
|
|
||||||
let b = layout.bounds();
|
let b = layout.bounds();
|
||||||
|
|
||||||
|
|
@ -151,10 +138,7 @@ mod rainbow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, B, T> From<Rainbow> for Element<'a, Message, Renderer<B, T>>
|
impl<'a, Message> From<Rainbow> for Element<'a, Message, Renderer> {
|
||||||
where
|
|
||||||
B: Backend,
|
|
||||||
{
|
|
||||||
fn from(rainbow: Rainbow) -> Self {
|
fn from(rainbow: Rainbow) -> Self {
|
||||||
Self::new(rainbow)
|
Self::new(rainbow)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_winit = { path = "../../winit" }
|
iced_winit = { path = "../../winit" }
|
||||||
iced_wgpu = { path = "../../wgpu", features = ["webgl"] }
|
iced_wgpu = { path = "../../wgpu" }
|
||||||
|
iced_widget = { path = "../../widget" }
|
||||||
|
iced_renderer = { path = "../../renderer", features = ["wgpu", "tiny-skia"] }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use iced_wgpu::Renderer;
|
use iced_wgpu::Renderer;
|
||||||
use iced_winit::widget::{slider, text_input, Column, Row, Text};
|
use iced_widget::{slider, text_input, Column, Row, Text};
|
||||||
use iced_winit::{Alignment, Color, Command, Element, Length, Program};
|
use iced_winit::core::{Alignment, Color, Element, Length};
|
||||||
|
use iced_winit::runtime::{Command, Program};
|
||||||
|
use iced_winit::style::Theme;
|
||||||
|
|
||||||
pub struct Controls {
|
pub struct Controls {
|
||||||
background_color: Color,
|
background_color: Color,
|
||||||
|
|
@ -27,7 +29,7 @@ impl Controls {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program for Controls {
|
impl Program for Controls {
|
||||||
type Renderer = Renderer;
|
type Renderer = Renderer<Theme>;
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
|
@ -43,7 +45,7 @@ impl Program for Controls {
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<Message, Renderer> {
|
fn view(&self) -> Element<Message, Renderer<Theme>> {
|
||||||
let background_color = self.background_color;
|
let background_color = self.background_color;
|
||||||
let text = &self.text;
|
let text = &self.text;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,14 @@ mod scene;
|
||||||
use controls::Controls;
|
use controls::Controls;
|
||||||
use scene::Scene;
|
use scene::Scene;
|
||||||
|
|
||||||
use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport};
|
use iced_wgpu::graphics::Viewport;
|
||||||
use iced_winit::{
|
use iced_wgpu::{wgpu, Backend, Renderer, Settings};
|
||||||
conversion, futures, program, renderer, winit, Clipboard, Color, Debug,
|
use iced_winit::core::renderer;
|
||||||
Size,
|
use iced_winit::core::{Color, Size};
|
||||||
};
|
use iced_winit::runtime::program;
|
||||||
|
use iced_winit::runtime::Debug;
|
||||||
|
use iced_winit::style::Theme;
|
||||||
|
use iced_winit::{conversion, futures, winit, Clipboard};
|
||||||
|
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::PhysicalPosition,
|
dpi::PhysicalPosition,
|
||||||
|
|
@ -73,7 +76,8 @@ pub fn main() {
|
||||||
let instance = wgpu::Instance::new(backend);
|
let instance = wgpu::Instance::new(backend);
|
||||||
let surface = unsafe { instance.create_surface(&window) };
|
let surface = unsafe { instance.create_surface(&window) };
|
||||||
|
|
||||||
let (format, (device, queue)) = futures::executor::block_on(async {
|
let (format, (device, queue)) =
|
||||||
|
futures::futures::executor::block_on(async {
|
||||||
let adapter = wgpu::util::initialize_adapter_from_env_or_default(
|
let adapter = wgpu::util::initialize_adapter_from_env_or_default(
|
||||||
&instance,
|
&instance,
|
||||||
backend,
|
backend,
|
||||||
|
|
@ -101,7 +105,8 @@ pub fn main() {
|
||||||
.request_device(
|
.request_device(
|
||||||
&wgpu::DeviceDescriptor {
|
&wgpu::DeviceDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
features: adapter_features & wgpu::Features::default(),
|
features: adapter_features
|
||||||
|
& wgpu::Features::default(),
|
||||||
limits: needed_limits,
|
limits: needed_limits,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
|
@ -188,7 +193,7 @@ pub fn main() {
|
||||||
viewport.scale_factor(),
|
viewport.scale_factor(),
|
||||||
),
|
),
|
||||||
&mut renderer,
|
&mut renderer,
|
||||||
&iced_wgpu::Theme::Dark,
|
&Theme::Dark,
|
||||||
&renderer::Style { text_color: Color::WHITE },
|
&renderer::Style { text_color: Color::WHITE },
|
||||||
&mut clipboard,
|
&mut clipboard,
|
||||||
&mut debug,
|
&mut debug,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use iced_wgpu::wgpu;
|
use iced_wgpu::wgpu;
|
||||||
use iced_winit::Color;
|
use iced_winit::core::Color;
|
||||||
|
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pipeline: wgpu::RenderPipeline,
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["debug"] }
|
iced = { path = "../..", features = ["debug", "lazy"] }
|
||||||
iced_lazy = { path = "../../lazy" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use iced::theme;
|
use iced::theme;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, column, horizontal_space, pick_list, row, scrollable, text,
|
button, column, horizontal_space, lazy, pick_list, row, scrollable, text,
|
||||||
text_input,
|
text_input,
|
||||||
};
|
};
|
||||||
use iced::{Element, Length, Sandbox, Settings};
|
use iced::{Element, Length, Sandbox, Settings};
|
||||||
use iced_lazy::lazy;
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = [] }
|
iced = { path = "../..", features = ["advanced"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
|
|
|
||||||
|
|
@ -178,12 +178,15 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod modal {
|
mod modal {
|
||||||
use iced_native::alignment::Alignment;
|
use iced::advanced::layout::{self, Layout};
|
||||||
use iced_native::widget::{self, Tree};
|
use iced::advanced::overlay;
|
||||||
use iced_native::{
|
use iced::advanced::renderer;
|
||||||
event, layout, mouse, overlay, renderer, Clipboard, Color, Element,
|
use iced::advanced::widget::{self, Widget};
|
||||||
Event, Layout, Length, Point, Rectangle, Shell, Size, Widget,
|
use iced::advanced::{self, Clipboard, Shell};
|
||||||
};
|
use iced::alignment::Alignment;
|
||||||
|
use iced::event;
|
||||||
|
use iced::mouse;
|
||||||
|
use iced::{Color, Element, Event, Length, Point, Rectangle, Size};
|
||||||
|
|
||||||
/// A widget that centers a modal element over some base element
|
/// A widget that centers a modal element over some base element
|
||||||
pub struct Modal<'a, Message, Renderer> {
|
pub struct Modal<'a, Message, Renderer> {
|
||||||
|
|
@ -218,14 +221,17 @@ mod modal {
|
||||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||||
for Modal<'a, Message, Renderer>
|
for Modal<'a, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer,
|
Renderer: advanced::Renderer,
|
||||||
Message: Clone,
|
Message: Clone,
|
||||||
{
|
{
|
||||||
fn children(&self) -> Vec<Tree> {
|
fn children(&self) -> Vec<widget::Tree> {
|
||||||
vec![Tree::new(&self.base), Tree::new(&self.modal)]
|
vec![
|
||||||
|
widget::Tree::new(&self.base),
|
||||||
|
widget::Tree::new(&self.modal),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&self, tree: &mut Tree) {
|
fn diff(&self, tree: &mut widget::Tree) {
|
||||||
tree.diff_children(&[&self.base, &self.modal]);
|
tree.diff_children(&[&self.base, &self.modal]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,7 +253,7 @@ mod modal {
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut Tree,
|
state: &mut widget::Tree,
|
||||||
event: Event,
|
event: Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
|
|
@ -268,9 +274,9 @@ mod modal {
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
state: &Tree,
|
state: &widget::Tree,
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
theme: &<Renderer as iced_native::Renderer>::Theme,
|
theme: &<Renderer as advanced::Renderer>::Theme,
|
||||||
style: &renderer::Style,
|
style: &renderer::Style,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
|
|
@ -289,7 +295,7 @@ mod modal {
|
||||||
|
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
state: &'b mut Tree,
|
state: &'b mut widget::Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||||
|
|
@ -306,7 +312,7 @@ mod modal {
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
&self,
|
&self,
|
||||||
state: &Tree,
|
state: &widget::Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
|
|
@ -323,7 +329,7 @@ mod modal {
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&self,
|
&self,
|
||||||
state: &mut Tree,
|
state: &mut widget::Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn widget::Operation<Message>,
|
operation: &mut dyn widget::Operation<Message>,
|
||||||
|
|
@ -339,7 +345,7 @@ mod modal {
|
||||||
|
|
||||||
struct Overlay<'a, 'b, Message, Renderer> {
|
struct Overlay<'a, 'b, Message, Renderer> {
|
||||||
content: &'b mut Element<'a, Message, Renderer>,
|
content: &'b mut Element<'a, Message, Renderer>,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut widget::Tree,
|
||||||
size: Size,
|
size: Size,
|
||||||
on_blur: Option<Message>,
|
on_blur: Option<Message>,
|
||||||
}
|
}
|
||||||
|
|
@ -347,7 +353,7 @@ mod modal {
|
||||||
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
|
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
|
||||||
for Overlay<'a, 'b, Message, Renderer>
|
for Overlay<'a, 'b, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer,
|
Renderer: advanced::Renderer,
|
||||||
Message: Clone,
|
Message: Clone,
|
||||||
{
|
{
|
||||||
fn layout(
|
fn layout(
|
||||||
|
|
@ -469,7 +475,7 @@ mod modal {
|
||||||
impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>>
|
impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>>
|
||||||
for Element<'a, Message, Renderer>
|
for Element<'a, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: 'a + iced_native::Renderer,
|
Renderer: 'a + advanced::Renderer,
|
||||||
Message: 'a + Clone,
|
Message: 'a + Clone,
|
||||||
{
|
{
|
||||||
fn from(modal: Modal<'a, Message, Renderer>) -> Self {
|
fn from(modal: Modal<'a, Message, Renderer>) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -55,17 +55,18 @@ impl Application for ModernArt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> canvas::Program<Message> for ModernArt {
|
impl<Message> canvas::Program<Message, Renderer> for ModernArt {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_state: &Self::State,
|
_state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
_cursor: Cursor,
|
_cursor: Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let geometry = self.cache.draw(bounds.size(), |frame| {
|
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
|
||||||
let num_squares = thread_rng().gen_range(0..1200);
|
let num_squares = thread_rng().gen_range(0..1200);
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use iced::widget::canvas::stroke::{self, Stroke};
|
||||||
use iced::widget::canvas::{self, Canvas, Cursor, Geometry};
|
use iced::widget::canvas::{self, Canvas, Cursor, Geometry};
|
||||||
use iced::{
|
use iced::{
|
||||||
executor, touch, window, Application, Color, Command, Element, Length,
|
executor, touch, window, Application, Color, Command, Element, Length,
|
||||||
Point, Rectangle, Settings, Subscription, Theme,
|
Point, Rectangle, Renderer, Settings, Subscription, Theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
@ -95,7 +95,7 @@ impl Application for Multitouch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl canvas::Program<Message> for State {
|
impl canvas::Program<Message, Renderer> for State {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
|
|
@ -125,11 +125,12 @@ impl canvas::Program<Message> for State {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_state: &Self::State,
|
_state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
_cursor: Cursor,
|
_cursor: Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let fingerweb = self.cache.draw(bounds.size(), |frame| {
|
let fingerweb = self.cache.draw(renderer, bounds.size(), |frame| {
|
||||||
if self.fingers.len() < 2 {
|
if self.fingers.len() < 2 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["debug"] }
|
iced = { path = "../..", features = ["debug", "lazy"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
iced_lazy = { path = "../../lazy" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
use iced::alignment::{self, Alignment};
|
use iced::alignment::{self, Alignment};
|
||||||
|
use iced::event::{self, Event};
|
||||||
use iced::executor;
|
use iced::executor;
|
||||||
use iced::keyboard;
|
use iced::keyboard;
|
||||||
|
use iced::subscription;
|
||||||
use iced::theme::{self, Theme};
|
use iced::theme::{self, Theme};
|
||||||
use iced::widget::pane_grid::{self, PaneGrid};
|
use iced::widget::pane_grid::{self, PaneGrid};
|
||||||
use iced::widget::{button, column, container, row, scrollable, text};
|
use iced::widget::{
|
||||||
|
button, column, container, responsive, row, scrollable, text,
|
||||||
|
};
|
||||||
use iced::{
|
use iced::{
|
||||||
Application, Color, Command, Element, Length, Settings, Size, Subscription,
|
Application, Color, Command, Element, Length, Settings, Size, Subscription,
|
||||||
};
|
};
|
||||||
use iced_lazy::responsive;
|
|
||||||
use iced_native::{event, subscription, Event};
|
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
Example::run(Settings::default())
|
Example::run(Settings::default())
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,6 @@ impl Application for ScrollableDemo {
|
||||||
scroll_to_beginning_button(),
|
scroll_to_beginning_button(),
|
||||||
vertical_space(40),
|
vertical_space(40),
|
||||||
]
|
]
|
||||||
.align_items(Alignment::Fill)
|
|
||||||
.spacing(40),
|
.spacing(40),
|
||||||
horizontal_space(1200),
|
horizontal_space(1200),
|
||||||
text("Horizontal - End!"),
|
text("Horizontal - End!"),
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ use iced::widget::canvas::event::{self, Event};
|
||||||
use iced::widget::canvas::{self, Canvas};
|
use iced::widget::canvas::{self, Canvas};
|
||||||
use iced::widget::{column, row, slider, text};
|
use iced::widget::{column, row, slider, text};
|
||||||
use iced::{
|
use iced::{
|
||||||
Application, Color, Command, Length, Point, Rectangle, Settings, Size,
|
Application, Color, Command, Length, Point, Rectangle, Renderer, Settings,
|
||||||
Theme,
|
Size, Theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
@ -134,11 +134,12 @@ impl canvas::Program<Message> for SierpinskiGraph {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_state: &Self::State,
|
_state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
_cursor: canvas::Cursor,
|
_cursor: canvas::Cursor,
|
||||||
) -> Vec<canvas::Geometry> {
|
) -> Vec<canvas::Geometry> {
|
||||||
let geom = self.cache.draw(bounds.size(), |frame| {
|
let geom = self.cache.draw(renderer, bounds.size(), |frame| {
|
||||||
frame.stroke(
|
frame.stroke(
|
||||||
&canvas::Path::rectangle(Point::ORIGIN, frame.size()),
|
&canvas::Path::rectangle(Point::ORIGIN, frame.size()),
|
||||||
canvas::Stroke::default(),
|
canvas::Stroke::default(),
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ use iced::widget::canvas::stroke::{self, Stroke};
|
||||||
use iced::widget::canvas::{Cursor, Path};
|
use iced::widget::canvas::{Cursor, Path};
|
||||||
use iced::window;
|
use iced::window;
|
||||||
use iced::{
|
use iced::{
|
||||||
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
|
Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
|
||||||
Size, Subscription, Vector,
|
Settings, Size, Subscription, Vector,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
@ -156,13 +156,15 @@ impl<Message> canvas::Program<Message> for State {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
_state: &Self::State,
|
_state: &Self::State,
|
||||||
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
_cursor: Cursor,
|
_cursor: Cursor,
|
||||||
) -> Vec<canvas::Geometry> {
|
) -> Vec<canvas::Geometry> {
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
let background = self.space_cache.draw(bounds.size(), |frame| {
|
let background =
|
||||||
|
self.space_cache.draw(renderer, bounds.size(), |frame| {
|
||||||
let stars = Path::new(|path| {
|
let stars = Path::new(|path| {
|
||||||
for (p, size) in &self.stars {
|
for (p, size) in &self.stars {
|
||||||
path.rectangle(*p, Size::new(*size, *size));
|
path.rectangle(*p, Size::new(*size, *size));
|
||||||
|
|
@ -173,7 +175,7 @@ impl<Message> canvas::Program<Message> for State {
|
||||||
frame.fill(&stars, Color::WHITE);
|
frame.fill(&stars, Color::WHITE);
|
||||||
});
|
});
|
||||||
|
|
||||||
let system = self.system_cache.draw(bounds.size(), |frame| {
|
let system = self.system_cache.draw(renderer, bounds.size(), |frame| {
|
||||||
let center = frame.center();
|
let center = frame.center();
|
||||||
|
|
||||||
let sun = Path::circle(center, Self::SUN_RADIUS);
|
let sun = Path::circle(center, Self::SUN_RADIUS);
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = [] }
|
iced = { path = "../..", features = ["advanced"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
|
|
|
||||||
|
|
@ -176,17 +176,23 @@ mod toast {
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use iced::advanced;
|
||||||
|
use iced::advanced::layout::{self, Layout};
|
||||||
|
use iced::advanced::overlay;
|
||||||
|
use iced::advanced::renderer;
|
||||||
|
use iced::advanced::widget::{self, Operation, Tree};
|
||||||
|
use iced::advanced::{Clipboard, Shell, Widget};
|
||||||
|
use iced::event::{self, Event};
|
||||||
|
use iced::mouse;
|
||||||
use iced::theme;
|
use iced::theme;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, column, container, horizontal_rule, horizontal_space, row, text,
|
button, column, container, horizontal_rule, horizontal_space, row, text,
|
||||||
};
|
};
|
||||||
|
use iced::window;
|
||||||
use iced::{
|
use iced::{
|
||||||
Alignment, Element, Length, Point, Rectangle, Renderer, Size, Theme,
|
Alignment, Element, Length, Point, Rectangle, Renderer, Size, Theme,
|
||||||
Vector,
|
Vector,
|
||||||
};
|
};
|
||||||
use iced_native::widget::{tree, Operation, Tree};
|
|
||||||
use iced_native::{event, layout, mouse, overlay, renderer, window};
|
|
||||||
use iced_native::{Clipboard, Event, Layout, Shell, Widget};
|
|
||||||
|
|
||||||
pub const DEFAULT_TIMEOUT: u64 = 5;
|
pub const DEFAULT_TIMEOUT: u64 = 5;
|
||||||
|
|
||||||
|
|
@ -324,13 +330,13 @@ mod toast {
|
||||||
self.content.as_widget().layout(renderer, limits)
|
self.content.as_widget().layout(renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tag(&self) -> tree::Tag {
|
fn tag(&self) -> widget::tree::Tag {
|
||||||
struct Marker(Vec<Instant>);
|
struct Marker(Vec<Instant>);
|
||||||
iced_native::widget::tree::Tag::of::<Marker>()
|
widget::tree::Tag::of::<Marker>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> tree::State {
|
fn state(&self) -> widget::tree::State {
|
||||||
iced_native::widget::tree::State::new(Vec::<Option<Instant>>::new())
|
widget::tree::State::new(Vec::<Option<Instant>>::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> Vec<Tree> {
|
fn children(&self) -> Vec<Tree> {
|
||||||
|
|
@ -584,7 +590,7 @@ mod toast {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
theme: &<Renderer as iced_native::Renderer>::Theme,
|
theme: &<Renderer as advanced::Renderer>::Theme,
|
||||||
style: &renderer::Style,
|
style: &renderer::Style,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
|
|
@ -613,7 +619,7 @@ mod toast {
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn iced_native::widget::Operation<Message>,
|
operation: &mut dyn widget::Operation<Message>,
|
||||||
) {
|
) {
|
||||||
operation.container(None, &mut |operation| {
|
operation.container(None, &mut |operation| {
|
||||||
self.toasts
|
self.toasts
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use iced::alignment::{self, Alignment};
|
use iced::alignment::{self, Alignment};
|
||||||
use iced::event::{self, Event};
|
use iced::event::{self, Event};
|
||||||
use iced::font::{self, Font};
|
use iced::font::{self, Font};
|
||||||
use iced::keyboard;
|
use iced::keyboard::{self, KeyCode, Modifiers};
|
||||||
use iced::subscription;
|
use iced::subscription;
|
||||||
use iced::theme::{self, Theme};
|
use iced::theme::{self, Theme};
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
|
|
@ -52,6 +52,7 @@ enum Message {
|
||||||
FilterChanged(Filter),
|
FilterChanged(Filter),
|
||||||
TaskMessage(usize, TaskMessage),
|
TaskMessage(usize, TaskMessage),
|
||||||
TabPressed { shift: bool },
|
TabPressed { shift: bool },
|
||||||
|
ToggleFullscreen(window::Mode),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Todos {
|
impl Application for Todos {
|
||||||
|
|
@ -162,6 +163,9 @@ impl Application for Todos {
|
||||||
widget::focus_next()
|
widget::focus_next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::ToggleFullscreen(mode) => {
|
||||||
|
window::change_mode(mode)
|
||||||
|
}
|
||||||
_ => Command::none(),
|
_ => Command::none(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -272,6 +276,21 @@ impl Application for Todos {
|
||||||
) => Some(Message::TabPressed {
|
) => Some(Message::TabPressed {
|
||||||
shift: modifiers.shift(),
|
shift: modifiers.shift(),
|
||||||
}),
|
}),
|
||||||
|
(
|
||||||
|
Event::Keyboard(keyboard::Event::KeyPressed {
|
||||||
|
key_code,
|
||||||
|
modifiers: Modifiers::SHIFT,
|
||||||
|
}),
|
||||||
|
event::Status::Ignored,
|
||||||
|
) => match key_code {
|
||||||
|
KeyCode::Up => {
|
||||||
|
Some(Message::ToggleFullscreen(window::Mode::Fullscreen))
|
||||||
|
}
|
||||||
|
KeyCode::Down => {
|
||||||
|
Some(Message::ToggleFullscreen(window::Mode::Windowed))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,3 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../.." }
|
iced = { path = "../.." }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
|
use iced::event::{Event, MacOS, PlatformSpecific};
|
||||||
use iced::executor;
|
use iced::executor;
|
||||||
|
use iced::subscription;
|
||||||
use iced::widget::{container, text};
|
use iced::widget::{container, text};
|
||||||
use iced::{
|
use iced::{
|
||||||
Application, Command, Element, Length, Settings, Subscription, Theme,
|
Application, Command, Element, Length, Settings, Subscription, Theme,
|
||||||
};
|
};
|
||||||
use iced_native::{
|
|
||||||
event::{MacOS, PlatformSpecific},
|
|
||||||
Event,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
App::run(Settings::default())
|
App::run(Settings::default())
|
||||||
|
|
@ -19,7 +17,7 @@ struct App {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Message {
|
enum Message {
|
||||||
EventOccurred(iced_native::Event),
|
EventOccurred(Event),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for App {
|
impl Application for App {
|
||||||
|
|
@ -52,7 +50,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
iced_native::subscription::events().map(Message::EventOccurred)
|
subscription::events().map(Message::EventOccurred)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["tokio", "debug"] }
|
iced = { path = "../..", features = ["tokio", "debug"] }
|
||||||
iced_native = { path = "../../native" }
|
|
||||||
iced_futures = { path = "../../futures" }
|
|
||||||
once_cell = "1.15"
|
once_cell = "1.15"
|
||||||
|
|
||||||
[dependencies.async-tungstenite]
|
[dependencies.async-tungstenite]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
use iced_futures::futures;
|
use iced::futures;
|
||||||
use iced_native::subscription::{self, Subscription};
|
use iced::subscription::{self, Subscription};
|
||||||
|
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures::sink::SinkExt;
|
use futures::sink::SinkExt;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use iced_futures::futures;
|
use iced::futures;
|
||||||
|
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,9 @@ impl Application for WebSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row![input, button].spacing(10).align_items(Alignment::Fill)
|
row![input, button]
|
||||||
|
.spacing(10)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
};
|
};
|
||||||
|
|
||||||
column![message_log, new_message_input]
|
column![message_log, new_message_input]
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ thread-pool = ["futures/thread-pool"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
|
[dependencies.iced_core]
|
||||||
|
version = "0.8"
|
||||||
|
path = "../core"
|
||||||
|
|
||||||
[dependencies.futures]
|
[dependencies.futures]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,28 +18,26 @@ impl crate::Executor for Executor {
|
||||||
|
|
||||||
pub mod time {
|
pub mod time {
|
||||||
//! Listen and react to time.
|
//! Listen and react to time.
|
||||||
|
use crate::core::Hasher;
|
||||||
use crate::subscription::{self, Subscription};
|
use crate::subscription::{self, Subscription};
|
||||||
|
|
||||||
/// Returns a [`Subscription`] that produces messages at a set interval.
|
/// Returns a [`Subscription`] that produces messages at a set interval.
|
||||||
///
|
///
|
||||||
/// The first message is produced after a `duration`, and then continues to
|
/// The first message is produced after a `duration`, and then continues to
|
||||||
/// produce more messages every `duration` after that.
|
/// produce more messages every `duration` after that.
|
||||||
pub fn every<H: std::hash::Hasher, E>(
|
pub fn every(
|
||||||
duration: std::time::Duration,
|
duration: std::time::Duration,
|
||||||
) -> Subscription<H, E, std::time::Instant> {
|
) -> Subscription<std::time::Instant> {
|
||||||
Subscription::from_recipe(Every(duration))
|
Subscription::from_recipe(Every(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Every(std::time::Duration);
|
struct Every(std::time::Duration);
|
||||||
|
|
||||||
impl<H, E> subscription::Recipe<H, E> for Every
|
impl subscription::Recipe for Every {
|
||||||
where
|
|
||||||
H: std::hash::Hasher,
|
|
||||||
{
|
|
||||||
type Output = std::time::Instant;
|
type Output = std::time::Instant;
|
||||||
|
|
||||||
fn hash(&self, state: &mut H) {
|
fn hash(&self, state: &mut Hasher) {
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
std::any::TypeId::of::<Self>().hash(state);
|
std::any::TypeId::of::<Self>().hash(state);
|
||||||
|
|
@ -48,7 +46,7 @@ pub mod time {
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
self: Box<Self>,
|
self: Box<Self>,
|
||||||
_input: futures::stream::BoxStream<'static, E>,
|
_input: subscription::EventStream,
|
||||||
) -> futures::stream::BoxStream<'static, Self::Output> {
|
) -> futures::stream::BoxStream<'static, Self::Output> {
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,28 +19,26 @@ impl crate::Executor for Executor {
|
||||||
|
|
||||||
pub mod time {
|
pub mod time {
|
||||||
//! Listen and react to time.
|
//! Listen and react to time.
|
||||||
|
use crate::core::Hasher;
|
||||||
use crate::subscription::{self, Subscription};
|
use crate::subscription::{self, Subscription};
|
||||||
|
|
||||||
/// Returns a [`Subscription`] that produces messages at a set interval.
|
/// Returns a [`Subscription`] that produces messages at a set interval.
|
||||||
///
|
///
|
||||||
/// The first message is produced after a `duration`, and then continues to
|
/// The first message is produced after a `duration`, and then continues to
|
||||||
/// produce more messages every `duration` after that.
|
/// produce more messages every `duration` after that.
|
||||||
pub fn every<H: std::hash::Hasher, E>(
|
pub fn every(
|
||||||
duration: std::time::Duration,
|
duration: std::time::Duration,
|
||||||
) -> Subscription<H, E, std::time::Instant> {
|
) -> Subscription<std::time::Instant> {
|
||||||
Subscription::from_recipe(Every(duration))
|
Subscription::from_recipe(Every(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Every(std::time::Duration);
|
struct Every(std::time::Duration);
|
||||||
|
|
||||||
impl<H, E> subscription::Recipe<H, E> for Every
|
impl subscription::Recipe for Every {
|
||||||
where
|
|
||||||
H: std::hash::Hasher,
|
|
||||||
{
|
|
||||||
type Output = std::time::Instant;
|
type Output = std::time::Instant;
|
||||||
|
|
||||||
fn hash(&self, state: &mut H) {
|
fn hash(&self, state: &mut Hasher) {
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
std::any::TypeId::of::<Self>().hash(state);
|
std::any::TypeId::of::<Self>().hash(state);
|
||||||
|
|
@ -49,7 +47,7 @@ pub mod time {
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
self: Box<Self>,
|
self: Box<Self>,
|
||||||
_input: futures::stream::BoxStream<'static, E>,
|
_input: subscription::EventStream,
|
||||||
) -> futures::stream::BoxStream<'static, Self::Output> {
|
) -> futures::stream::BoxStream<'static, Self::Output> {
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,28 +22,26 @@ impl crate::Executor for Executor {
|
||||||
|
|
||||||
pub mod time {
|
pub mod time {
|
||||||
//! Listen and react to time.
|
//! Listen and react to time.
|
||||||
|
use crate::core::Hasher;
|
||||||
use crate::subscription::{self, Subscription};
|
use crate::subscription::{self, Subscription};
|
||||||
|
|
||||||
/// Returns a [`Subscription`] that produces messages at a set interval.
|
/// Returns a [`Subscription`] that produces messages at a set interval.
|
||||||
///
|
///
|
||||||
/// The first message is produced after a `duration`, and then continues to
|
/// The first message is produced after a `duration`, and then continues to
|
||||||
/// produce more messages every `duration` after that.
|
/// produce more messages every `duration` after that.
|
||||||
pub fn every<H: std::hash::Hasher, E>(
|
pub fn every(
|
||||||
duration: std::time::Duration,
|
duration: std::time::Duration,
|
||||||
) -> Subscription<H, E, std::time::Instant> {
|
) -> Subscription<std::time::Instant> {
|
||||||
Subscription::from_recipe(Every(duration))
|
Subscription::from_recipe(Every(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Every(std::time::Duration);
|
struct Every(std::time::Duration);
|
||||||
|
|
||||||
impl<H, E> subscription::Recipe<H, E> for Every
|
impl subscription::Recipe for Every {
|
||||||
where
|
|
||||||
H: std::hash::Hasher,
|
|
||||||
{
|
|
||||||
type Output = std::time::Instant;
|
type Output = std::time::Instant;
|
||||||
|
|
||||||
fn hash(&self, state: &mut H) {
|
fn hash(&self, state: &mut Hasher) {
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
std::any::TypeId::of::<Self>().hash(state);
|
std::any::TypeId::of::<Self>().hash(state);
|
||||||
|
|
@ -52,7 +50,7 @@ pub mod time {
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
self: Box<Self>,
|
self: Box<Self>,
|
||||||
_input: futures::stream::BoxStream<'static, E>,
|
_input: subscription::EventStream,
|
||||||
) -> futures::stream::BoxStream<'static, Self::Output> {
|
) -> futures::stream::BoxStream<'static, Self::Output> {
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ impl crate::Executor for Executor {
|
||||||
|
|
||||||
pub mod time {
|
pub mod time {
|
||||||
//! Listen and react to time.
|
//! Listen and react to time.
|
||||||
|
use crate::core::Hasher;
|
||||||
use crate::subscription::{self, Subscription};
|
use crate::subscription::{self, Subscription};
|
||||||
use crate::BoxStream;
|
use crate::BoxStream;
|
||||||
|
|
||||||
|
|
@ -23,22 +24,19 @@ pub mod time {
|
||||||
///
|
///
|
||||||
/// The first message is produced after a `duration`, and then continues to
|
/// The first message is produced after a `duration`, and then continues to
|
||||||
/// produce more messages every `duration` after that.
|
/// produce more messages every `duration` after that.
|
||||||
pub fn every<H: std::hash::Hasher, E>(
|
pub fn every(
|
||||||
duration: std::time::Duration,
|
duration: std::time::Duration,
|
||||||
) -> Subscription<H, E, wasm_timer::Instant> {
|
) -> Subscription<wasm_timer::Instant> {
|
||||||
Subscription::from_recipe(Every(duration))
|
Subscription::from_recipe(Every(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Every(std::time::Duration);
|
struct Every(std::time::Duration);
|
||||||
|
|
||||||
impl<H, E> subscription::Recipe<H, E> for Every
|
impl subscription::Recipe for Every {
|
||||||
where
|
|
||||||
H: std::hash::Hasher,
|
|
||||||
{
|
|
||||||
type Output = wasm_timer::Instant;
|
type Output = wasm_timer::Instant;
|
||||||
|
|
||||||
fn hash(&self, state: &mut H) {
|
fn hash(&self, state: &mut Hasher) {
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
std::any::TypeId::of::<Self>().hash(state);
|
std::any::TypeId::of::<Self>().hash(state);
|
||||||
|
|
@ -47,7 +45,7 @@ pub mod time {
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
self: Box<Self>,
|
self: Box<Self>,
|
||||||
_input: BoxStream<E>,
|
_input: subscription::EventStream,
|
||||||
) -> BoxStream<Self::Output> {
|
) -> BoxStream<Self::Output> {
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
/// A set of asynchronous actions to be performed by some runtime.
|
|
||||||
#[must_use = "`Command` must be returned to runtime to take effect"]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Command<T>(Internal<T>);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Internal<T> {
|
|
||||||
None,
|
|
||||||
Single(T),
|
|
||||||
Batch(Vec<T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Command<T> {
|
|
||||||
/// Creates an empty [`Command`].
|
|
||||||
///
|
|
||||||
/// In other words, a [`Command`] that does nothing.
|
|
||||||
pub const fn none() -> Self {
|
|
||||||
Self(Internal::None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a [`Command`] that performs a single action.
|
|
||||||
pub const fn single(action: T) -> Self {
|
|
||||||
Self(Internal::Single(action))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
let mut batch = Vec::new();
|
|
||||||
|
|
||||||
for Command(command) in commands {
|
|
||||||
match command {
|
|
||||||
Internal::None => {}
|
|
||||||
Internal::Single(command) => batch.push(command),
|
|
||||||
Internal::Batch(commands) => batch.extend(commands),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self(Internal::Batch(batch))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies a transformation to the result of a [`Command`].
|
|
||||||
pub fn map<A>(self, f: impl Fn(T) -> A) -> Command<A>
|
|
||||||
where
|
|
||||||
T: 'static,
|
|
||||||
{
|
|
||||||
let Command(command) = self;
|
|
||||||
|
|
||||||
match command {
|
|
||||||
Internal::None => Command::none(),
|
|
||||||
Internal::Single(action) => Command::single(f(action)),
|
|
||||||
Internal::Batch(batch) => {
|
|
||||||
Command(Internal::Batch(batch.into_iter().map(f).collect()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all of the actions of the [`Command`].
|
|
||||||
pub fn actions(self) -> Vec<T> {
|
|
||||||
let Command(command) = self;
|
|
||||||
|
|
||||||
match command {
|
|
||||||
Internal::None => Vec::new(),
|
|
||||||
Internal::Single(action) => vec![action],
|
|
||||||
Internal::Batch(batch) => batch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
|
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
pub use futures;
|
pub use futures;
|
||||||
|
pub use iced_core as core;
|
||||||
|
|
||||||
mod command;
|
|
||||||
mod maybe_send;
|
mod maybe_send;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
|
|
||||||
|
|
@ -27,7 +27,6 @@ pub mod backend;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
|
|
||||||
pub use command::Command;
|
|
||||||
pub use executor::Executor;
|
pub use executor::Executor;
|
||||||
pub use maybe_send::MaybeSend;
|
pub use maybe_send::MaybeSend;
|
||||||
pub use platform::*;
|
pub use platform::*;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//! Run commands and keep track of subscriptions.
|
//! Run commands and keep track of subscriptions.
|
||||||
|
use crate::core::event::{self, Event};
|
||||||
use crate::subscription;
|
use crate::subscription;
|
||||||
use crate::{BoxFuture, Executor, MaybeSend, Subscription};
|
use crate::{BoxFuture, Executor, MaybeSend};
|
||||||
|
|
||||||
use futures::{channel::mpsc, Sink};
|
use futures::{channel::mpsc, Sink};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
@ -12,18 +13,15 @@ use std::marker::PhantomData;
|
||||||
///
|
///
|
||||||
/// [`Command`]: crate::Command
|
/// [`Command`]: crate::Command
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Runtime<Hasher, Event, Executor, Sender, Message> {
|
pub struct Runtime<Executor, Sender, Message> {
|
||||||
executor: Executor,
|
executor: Executor,
|
||||||
sender: Sender,
|
sender: Sender,
|
||||||
subscriptions: subscription::Tracker<Hasher, Event>,
|
subscriptions: subscription::Tracker,
|
||||||
_message: PhantomData<Message>,
|
_message: PhantomData<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Hasher, Event, Executor, Sender, Message>
|
impl<Executor, Sender, Message> Runtime<Executor, Sender, Message>
|
||||||
Runtime<Hasher, Event, Executor, Sender, Message>
|
|
||||||
where
|
where
|
||||||
Hasher: std::hash::Hasher + Default,
|
|
||||||
Event: Send + Clone + 'static,
|
|
||||||
Executor: self::Executor,
|
Executor: self::Executor,
|
||||||
Sender: Sink<Message, Error = mpsc::SendError>
|
Sender: Sink<Message, Error = mpsc::SendError>
|
||||||
+ Unpin
|
+ Unpin
|
||||||
|
|
@ -79,7 +77,9 @@ where
|
||||||
/// [`Tracker::update`]: subscription::Tracker::update
|
/// [`Tracker::update`]: subscription::Tracker::update
|
||||||
pub fn track(
|
pub fn track(
|
||||||
&mut self,
|
&mut self,
|
||||||
subscription: Subscription<Hasher, Event, Message>,
|
recipes: impl IntoIterator<
|
||||||
|
Item = Box<dyn subscription::Recipe<Output = Message>>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
let Runtime {
|
let Runtime {
|
||||||
executor,
|
executor,
|
||||||
|
|
@ -88,8 +88,9 @@ where
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let futures = executor
|
let futures = executor.enter(|| {
|
||||||
.enter(|| subscriptions.update(subscription, sender.clone()));
|
subscriptions.update(recipes.into_iter(), sender.clone())
|
||||||
|
});
|
||||||
|
|
||||||
for future in futures {
|
for future in futures {
|
||||||
executor.spawn(future);
|
executor.spawn(future);
|
||||||
|
|
@ -102,7 +103,7 @@ where
|
||||||
/// See [`Tracker::broadcast`] to learn more.
|
/// See [`Tracker::broadcast`] to learn more.
|
||||||
///
|
///
|
||||||
/// [`Tracker::broadcast`]: subscription::Tracker::broadcast
|
/// [`Tracker::broadcast`]: subscription::Tracker::broadcast
|
||||||
pub fn broadcast(&mut self, event: Event) {
|
pub fn broadcast(&mut self, event: Event, status: event::Status) {
|
||||||
self.subscriptions.broadcast(event);
|
self.subscriptions.broadcast(event, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,18 @@ mod tracker;
|
||||||
|
|
||||||
pub use tracker::Tracker;
|
pub use tracker::Tracker;
|
||||||
|
|
||||||
use crate::BoxStream;
|
use crate::core::event::{self, Event};
|
||||||
|
use crate::core::window;
|
||||||
|
use crate::core::Hasher;
|
||||||
|
use crate::futures::{Future, Stream};
|
||||||
|
use crate::{BoxStream, MaybeSend};
|
||||||
|
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
/// A stream of runtime events.
|
||||||
|
///
|
||||||
|
/// It is the input of a [`Subscription`].
|
||||||
|
pub type EventStream = BoxStream<(Event, event::Status)>;
|
||||||
|
|
||||||
/// A request to listen to external events.
|
/// A request to listen to external events.
|
||||||
///
|
///
|
||||||
|
|
@ -16,19 +27,13 @@ use crate::BoxStream;
|
||||||
/// For instance, you can use a [`Subscription`] to listen to a WebSocket
|
/// For instance, you can use a [`Subscription`] to listen to a WebSocket
|
||||||
/// connection, keyboard presses, mouse events, time ticks, etc.
|
/// connection, keyboard presses, mouse events, time ticks, etc.
|
||||||
///
|
///
|
||||||
/// This type is normally aliased by runtimes with a specific `Event` and/or
|
|
||||||
/// `Hasher`.
|
|
||||||
///
|
|
||||||
/// [`Command`]: crate::Command
|
/// [`Command`]: crate::Command
|
||||||
#[must_use = "`Subscription` must be returned to runtime to take effect"]
|
#[must_use = "`Subscription` must be returned to runtime to take effect"]
|
||||||
pub struct Subscription<Hasher, Event, Output> {
|
pub struct Subscription<Message> {
|
||||||
recipes: Vec<Box<dyn Recipe<Hasher, Event, Output = Output>>>,
|
recipes: Vec<Box<dyn Recipe<Output = Message>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H, E, O> Subscription<H, E, O>
|
impl<Message> Subscription<Message> {
|
||||||
where
|
|
||||||
H: std::hash::Hasher,
|
|
||||||
{
|
|
||||||
/// Returns an empty [`Subscription`] that will not produce any output.
|
/// Returns an empty [`Subscription`] that will not produce any output.
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -38,7 +43,7 @@ where
|
||||||
|
|
||||||
/// Creates a [`Subscription`] from a [`Recipe`] describing it.
|
/// Creates a [`Subscription`] from a [`Recipe`] describing it.
|
||||||
pub fn from_recipe(
|
pub fn from_recipe(
|
||||||
recipe: impl Recipe<H, E, Output = O> + 'static,
|
recipe: impl Recipe<Output = Message> + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
recipes: vec![Box::new(recipe)],
|
recipes: vec![Box::new(recipe)],
|
||||||
|
|
@ -48,7 +53,7 @@ where
|
||||||
/// Batches all the provided subscriptions and returns the resulting
|
/// Batches all the provided subscriptions and returns the resulting
|
||||||
/// [`Subscription`].
|
/// [`Subscription`].
|
||||||
pub fn batch(
|
pub fn batch(
|
||||||
subscriptions: impl IntoIterator<Item = Subscription<H, E, O>>,
|
subscriptions: impl IntoIterator<Item = Subscription<Message>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
recipes: subscriptions
|
recipes: subscriptions
|
||||||
|
|
@ -59,18 +64,16 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the different recipes of the [`Subscription`].
|
/// Returns the different recipes of the [`Subscription`].
|
||||||
pub fn recipes(self) -> Vec<Box<dyn Recipe<H, E, Output = O>>> {
|
pub fn into_recipes(self) -> Vec<Box<dyn Recipe<Output = Message>>> {
|
||||||
self.recipes
|
self.recipes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a value to the [`Subscription`] context.
|
/// Adds a value to the [`Subscription`] context.
|
||||||
///
|
///
|
||||||
/// The value will be part of the identity of a [`Subscription`].
|
/// The value will be part of the identity of a [`Subscription`].
|
||||||
pub fn with<T>(mut self, value: T) -> Subscription<H, E, (T, O)>
|
pub fn with<T>(mut self, value: T) -> Subscription<(T, Message)>
|
||||||
where
|
where
|
||||||
H: 'static,
|
Message: 'static,
|
||||||
E: 'static,
|
|
||||||
O: 'static,
|
|
||||||
T: std::hash::Hash + Clone + Send + Sync + 'static,
|
T: std::hash::Hash + Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
Subscription {
|
Subscription {
|
||||||
|
|
@ -79,18 +82,16 @@ where
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|recipe| {
|
.map(|recipe| {
|
||||||
Box::new(With::new(recipe, value.clone()))
|
Box::new(With::new(recipe, value.clone()))
|
||||||
as Box<dyn Recipe<H, E, Output = (T, O)>>
|
as Box<dyn Recipe<Output = (T, Message)>>
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms the [`Subscription`] output with the given function.
|
/// Transforms the [`Subscription`] output with the given function.
|
||||||
pub fn map<A>(mut self, f: fn(O) -> A) -> Subscription<H, E, A>
|
pub fn map<A>(mut self, f: fn(Message) -> A) -> Subscription<A>
|
||||||
where
|
where
|
||||||
H: 'static,
|
Message: 'static,
|
||||||
E: 'static,
|
|
||||||
O: 'static,
|
|
||||||
A: 'static,
|
A: 'static,
|
||||||
{
|
{
|
||||||
Subscription {
|
Subscription {
|
||||||
|
|
@ -98,15 +99,14 @@ where
|
||||||
.recipes
|
.recipes
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|recipe| {
|
.map(|recipe| {
|
||||||
Box::new(Map::new(recipe, f))
|
Box::new(Map::new(recipe, f)) as Box<dyn Recipe<Output = A>>
|
||||||
as Box<dyn Recipe<H, E, Output = A>>
|
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
|
impl<Message> std::fmt::Debug for Subscription<Message> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Subscription").finish()
|
f.debug_struct("Subscription").finish()
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +129,7 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
|
||||||
/// [examples]: https://github.com/iced-rs/iced/tree/0.8/examples
|
/// [examples]: https://github.com/iced-rs/iced/tree/0.8/examples
|
||||||
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.8/examples/download_progress
|
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.8/examples/download_progress
|
||||||
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.8/examples/stopwatch
|
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.8/examples/stopwatch
|
||||||
pub trait Recipe<Hasher: std::hash::Hasher, Event> {
|
pub trait Recipe {
|
||||||
/// The events that will be produced by a [`Subscription`] with this
|
/// The events that will be produced by a [`Subscription`] with this
|
||||||
/// [`Recipe`].
|
/// [`Recipe`].
|
||||||
type Output;
|
type Output;
|
||||||
|
|
@ -141,45 +141,33 @@ pub trait Recipe<Hasher: std::hash::Hasher, Event> {
|
||||||
|
|
||||||
/// Executes the [`Recipe`] and produces the stream of events of its
|
/// Executes the [`Recipe`] and produces the stream of events of its
|
||||||
/// [`Subscription`].
|
/// [`Subscription`].
|
||||||
///
|
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output>;
|
||||||
/// It receives some stream of generic events, which is normally defined by
|
|
||||||
/// shells.
|
|
||||||
fn stream(
|
|
||||||
self: Box<Self>,
|
|
||||||
input: BoxStream<Event>,
|
|
||||||
) -> BoxStream<Self::Output>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Map<Hasher, Event, A, B> {
|
struct Map<A, B> {
|
||||||
recipe: Box<dyn Recipe<Hasher, Event, Output = A>>,
|
recipe: Box<dyn Recipe<Output = A>>,
|
||||||
mapper: fn(A) -> B,
|
mapper: fn(A) -> B,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H, E, A, B> Map<H, E, A, B> {
|
impl<A, B> Map<A, B> {
|
||||||
fn new(
|
fn new(recipe: Box<dyn Recipe<Output = A>>, mapper: fn(A) -> B) -> Self {
|
||||||
recipe: Box<dyn Recipe<H, E, Output = A>>,
|
|
||||||
mapper: fn(A) -> B,
|
|
||||||
) -> Self {
|
|
||||||
Map { recipe, mapper }
|
Map { recipe, mapper }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H, E, A, B> Recipe<H, E> for Map<H, E, A, B>
|
impl<A, B> Recipe for Map<A, B>
|
||||||
where
|
where
|
||||||
A: 'static,
|
A: 'static,
|
||||||
B: 'static,
|
B: 'static,
|
||||||
H: std::hash::Hasher,
|
|
||||||
{
|
{
|
||||||
type Output = B;
|
type Output = B;
|
||||||
|
|
||||||
fn hash(&self, state: &mut H) {
|
fn hash(&self, state: &mut Hasher) {
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
self.recipe.hash(state);
|
self.recipe.hash(state);
|
||||||
self.mapper.hash(state);
|
self.mapper.hash(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream(self: Box<Self>, input: BoxStream<E>) -> BoxStream<Self::Output> {
|
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
|
||||||
let mapper = self.mapper;
|
let mapper = self.mapper;
|
||||||
|
|
@ -188,34 +176,31 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct With<Hasher, Event, A, B> {
|
struct With<A, B> {
|
||||||
recipe: Box<dyn Recipe<Hasher, Event, Output = A>>,
|
recipe: Box<dyn Recipe<Output = A>>,
|
||||||
value: B,
|
value: B,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H, E, A, B> With<H, E, A, B> {
|
impl<A, B> With<A, B> {
|
||||||
fn new(recipe: Box<dyn Recipe<H, E, Output = A>>, value: B) -> Self {
|
fn new(recipe: Box<dyn Recipe<Output = A>>, value: B) -> Self {
|
||||||
With { recipe, value }
|
With { recipe, value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H, E, A, B> Recipe<H, E> for With<H, E, A, B>
|
impl<A, B> Recipe for With<A, B>
|
||||||
where
|
where
|
||||||
A: 'static,
|
A: 'static,
|
||||||
B: 'static + std::hash::Hash + Clone + Send + Sync,
|
B: 'static + std::hash::Hash + Clone + Send + Sync,
|
||||||
H: std::hash::Hasher,
|
|
||||||
{
|
{
|
||||||
type Output = (B, A);
|
type Output = (B, A);
|
||||||
|
|
||||||
fn hash(&self, state: &mut H) {
|
fn hash(&self, state: &mut Hasher) {
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
std::any::TypeId::of::<B>().hash(state);
|
std::any::TypeId::of::<B>().hash(state);
|
||||||
self.value.hash(state);
|
self.value.hash(state);
|
||||||
self.recipe.hash(state);
|
self.recipe.hash(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream(self: Box<Self>, input: BoxStream<E>) -> BoxStream<Self::Output> {
|
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
|
||||||
let value = self.value;
|
let value = self.value;
|
||||||
|
|
@ -227,3 +212,222 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Subscription`] to all the ignored runtime events.
|
||||||
|
///
|
||||||
|
/// This subscription will notify your application of any [`Event`] that was
|
||||||
|
/// not captured by any widget.
|
||||||
|
pub fn events() -> Subscription<Event> {
|
||||||
|
events_with(|event, status| match status {
|
||||||
|
event::Status::Ignored => Some(event),
|
||||||
|
event::Status::Captured => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Subscription`] that filters all the runtime events with the
|
||||||
|
/// provided function, producing messages accordingly.
|
||||||
|
///
|
||||||
|
/// This subscription will call the provided function for every [`Event`]
|
||||||
|
/// handled by the runtime. If the function:
|
||||||
|
///
|
||||||
|
/// - Returns `None`, the [`Event`] will be discarded.
|
||||||
|
/// - Returns `Some` message, the `Message` will be produced.
|
||||||
|
pub fn events_with<Message>(
|
||||||
|
f: fn(Event, event::Status) -> Option<Message>,
|
||||||
|
) -> Subscription<Message>
|
||||||
|
where
|
||||||
|
Message: 'static + MaybeSend,
|
||||||
|
{
|
||||||
|
#[derive(Hash)]
|
||||||
|
struct EventsWith;
|
||||||
|
|
||||||
|
Subscription::from_recipe(Runner {
|
||||||
|
id: (EventsWith, f),
|
||||||
|
spawn: move |events| {
|
||||||
|
use futures::future;
|
||||||
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
events.filter_map(move |(event, status)| {
|
||||||
|
future::ready(match event {
|
||||||
|
Event::Window(window::Event::RedrawRequested(_)) => None,
|
||||||
|
_ => f(event, status),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Subscription`] that produces a message for every runtime event,
|
||||||
|
/// including the redraw request events.
|
||||||
|
///
|
||||||
|
/// **Warning:** This [`Subscription`], if unfiltered, may produce messages in
|
||||||
|
/// an infinite loop.
|
||||||
|
pub fn raw_events<Message>(
|
||||||
|
f: fn(Event, event::Status) -> Option<Message>,
|
||||||
|
) -> Subscription<Message>
|
||||||
|
where
|
||||||
|
Message: 'static + MaybeSend,
|
||||||
|
{
|
||||||
|
#[derive(Hash)]
|
||||||
|
struct RawEvents;
|
||||||
|
|
||||||
|
Subscription::from_recipe(Runner {
|
||||||
|
id: (RawEvents, f),
|
||||||
|
spawn: move |events| {
|
||||||
|
use futures::future;
|
||||||
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
events.filter_map(move |(event, status)| {
|
||||||
|
future::ready(f(event, status))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Subscription`] that will call the given function to create and
|
||||||
|
/// asynchronously run the given [`Stream`].
|
||||||
|
pub fn run<S, Message>(builder: fn() -> S) -> Subscription<Message>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Message> + MaybeSend + 'static,
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
Subscription::from_recipe(Runner {
|
||||||
|
id: builder,
|
||||||
|
spawn: move |_| builder(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Subscription`] that will create and asynchronously run the
|
||||||
|
/// given [`Stream`].
|
||||||
|
///
|
||||||
|
/// The `id` will be used to uniquely identify the [`Subscription`].
|
||||||
|
pub fn run_with_id<I, S, Message>(id: I, stream: S) -> Subscription<Message>
|
||||||
|
where
|
||||||
|
I: Hash + 'static,
|
||||||
|
S: Stream<Item = Message> + MaybeSend + 'static,
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
Subscription::from_recipe(Runner {
|
||||||
|
id,
|
||||||
|
spawn: move |_| stream,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Subscription`] that will create and asynchronously run a
|
||||||
|
/// [`Stream`] that will call the provided closure to produce every `Message`.
|
||||||
|
///
|
||||||
|
/// The `id` will be used to uniquely identify the [`Subscription`].
|
||||||
|
///
|
||||||
|
/// # Creating an asynchronous worker with bidirectional communication
|
||||||
|
/// You can leverage this helper to create a [`Subscription`] that spawns
|
||||||
|
/// an asynchronous worker in the background and establish a channel of
|
||||||
|
/// communication with an `iced` application.
|
||||||
|
///
|
||||||
|
/// You can achieve this by creating an `mpsc` channel inside the closure
|
||||||
|
/// and returning the `Sender` as a `Message` for the `Application`:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use iced_futures::subscription::{self, Subscription};
|
||||||
|
/// use iced_futures::futures;
|
||||||
|
///
|
||||||
|
/// use futures::channel::mpsc;
|
||||||
|
///
|
||||||
|
/// pub enum Event {
|
||||||
|
/// Ready(mpsc::Sender<Input>),
|
||||||
|
/// WorkFinished,
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// enum Input {
|
||||||
|
/// DoSomeWork,
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// enum State {
|
||||||
|
/// Starting,
|
||||||
|
/// Ready(mpsc::Receiver<Input>),
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn some_worker() -> Subscription<Event> {
|
||||||
|
/// struct SomeWorker;
|
||||||
|
///
|
||||||
|
/// subscription::unfold(std::any::TypeId::of::<SomeWorker>(), State::Starting, |state| async move {
|
||||||
|
/// match state {
|
||||||
|
/// State::Starting => {
|
||||||
|
/// // Create channel
|
||||||
|
/// let (sender, receiver) = mpsc::channel(100);
|
||||||
|
///
|
||||||
|
/// (Some(Event::Ready(sender)), State::Ready(receiver))
|
||||||
|
/// }
|
||||||
|
/// State::Ready(mut receiver) => {
|
||||||
|
/// use futures::StreamExt;
|
||||||
|
///
|
||||||
|
/// // Read next input sent from `Application`
|
||||||
|
/// let input = receiver.select_next_some().await;
|
||||||
|
///
|
||||||
|
/// match input {
|
||||||
|
/// Input::DoSomeWork => {
|
||||||
|
/// // Do some async work...
|
||||||
|
///
|
||||||
|
/// // Finally, we can optionally return a message to tell the
|
||||||
|
/// // `Application` the work is done
|
||||||
|
/// (Some(Event::WorkFinished), State::Ready(receiver))
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket
|
||||||
|
/// connection open.
|
||||||
|
///
|
||||||
|
/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.8/examples/websocket
|
||||||
|
pub fn unfold<I, T, Fut, Message>(
|
||||||
|
id: I,
|
||||||
|
initial: T,
|
||||||
|
mut f: impl FnMut(T) -> Fut + MaybeSend + Sync + 'static,
|
||||||
|
) -> Subscription<Message>
|
||||||
|
where
|
||||||
|
I: Hash + 'static,
|
||||||
|
T: MaybeSend + 'static,
|
||||||
|
Fut: Future<Output = (Option<Message>, T)> + MaybeSend + 'static,
|
||||||
|
Message: 'static + MaybeSend,
|
||||||
|
{
|
||||||
|
use futures::future::{self, FutureExt};
|
||||||
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
run_with_id(
|
||||||
|
id,
|
||||||
|
futures::stream::unfold(initial, move |state| f(state).map(Some))
|
||||||
|
.filter_map(future::ready),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Runner<I, F, S, Message>
|
||||||
|
where
|
||||||
|
F: FnOnce(EventStream) -> S,
|
||||||
|
S: Stream<Item = Message>,
|
||||||
|
{
|
||||||
|
id: I,
|
||||||
|
spawn: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, S, F, Message> Recipe for Runner<I, F, S, Message>
|
||||||
|
where
|
||||||
|
I: Hash + 'static,
|
||||||
|
F: FnOnce(EventStream) -> S,
|
||||||
|
S: Stream<Item = Message> + MaybeSend + 'static,
|
||||||
|
{
|
||||||
|
type Output = Message;
|
||||||
|
|
||||||
|
fn hash(&self, state: &mut Hasher) {
|
||||||
|
std::any::TypeId::of::<I>().hash(state);
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {
|
||||||
|
crate::boxed_stream((self.spawn)(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,35 @@
|
||||||
use crate::{BoxFuture, MaybeSend, Subscription};
|
use crate::core::event::{self, Event};
|
||||||
|
use crate::core::Hasher;
|
||||||
|
use crate::subscription::Recipe;
|
||||||
|
use crate::{BoxFuture, MaybeSend};
|
||||||
|
|
||||||
use futures::{
|
use futures::channel::mpsc;
|
||||||
channel::mpsc,
|
use futures::sink::{Sink, SinkExt};
|
||||||
sink::{Sink, SinkExt},
|
|
||||||
};
|
use std::collections::HashMap;
|
||||||
use std::{collections::HashMap, marker::PhantomData};
|
use std::hash::Hasher as _;
|
||||||
|
|
||||||
/// A registry of subscription streams.
|
/// A registry of subscription streams.
|
||||||
///
|
///
|
||||||
/// If you have an application that continuously returns a [`Subscription`],
|
/// If you have an application that continuously returns a [`Subscription`],
|
||||||
/// you can use a [`Tracker`] to keep track of the different recipes and keep
|
/// you can use a [`Tracker`] to keep track of the different recipes and keep
|
||||||
/// its executions alive.
|
/// its executions alive.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Tracker<Hasher, Event> {
|
pub struct Tracker {
|
||||||
subscriptions: HashMap<u64, Execution<Event>>,
|
subscriptions: HashMap<u64, Execution>,
|
||||||
_hasher: PhantomData<Hasher>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Execution<Event> {
|
pub struct Execution {
|
||||||
_cancel: futures::channel::oneshot::Sender<()>,
|
_cancel: futures::channel::oneshot::Sender<()>,
|
||||||
listener: Option<futures::channel::mpsc::Sender<Event>>,
|
listener: Option<futures::channel::mpsc::Sender<(Event, event::Status)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Hasher, Event> Tracker<Hasher, Event>
|
impl Tracker {
|
||||||
where
|
|
||||||
Hasher: std::hash::Hasher + Default,
|
|
||||||
Event: 'static + Send + Clone,
|
|
||||||
{
|
|
||||||
/// Creates a new empty [`Tracker`].
|
/// Creates a new empty [`Tracker`].
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
subscriptions: HashMap::new(),
|
subscriptions: HashMap::new(),
|
||||||
_hasher: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +53,7 @@ where
|
||||||
/// [`Recipe`]: crate::subscription::Recipe
|
/// [`Recipe`]: crate::subscription::Recipe
|
||||||
pub fn update<Message, Receiver>(
|
pub fn update<Message, Receiver>(
|
||||||
&mut self,
|
&mut self,
|
||||||
subscription: Subscription<Hasher, Event, Message>,
|
recipes: impl Iterator<Item = Box<dyn Recipe<Output = Message>>>,
|
||||||
receiver: Receiver,
|
receiver: Receiver,
|
||||||
) -> Vec<BoxFuture<()>>
|
) -> Vec<BoxFuture<()>>
|
||||||
where
|
where
|
||||||
|
|
@ -70,8 +67,6 @@ where
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
let mut futures: Vec<BoxFuture<()>> = Vec::new();
|
let mut futures: Vec<BoxFuture<()>> = Vec::new();
|
||||||
|
|
||||||
let recipes = subscription.recipes();
|
|
||||||
let mut alive = std::collections::HashSet::new();
|
let mut alive = std::collections::HashSet::new();
|
||||||
|
|
||||||
for recipe in recipes {
|
for recipe in recipes {
|
||||||
|
|
@ -142,12 +137,12 @@ where
|
||||||
/// currently open.
|
/// currently open.
|
||||||
///
|
///
|
||||||
/// [`Recipe::stream`]: crate::subscription::Recipe::stream
|
/// [`Recipe::stream`]: crate::subscription::Recipe::stream
|
||||||
pub fn broadcast(&mut self, event: Event) {
|
pub fn broadcast(&mut self, event: Event, status: event::Status) {
|
||||||
self.subscriptions
|
self.subscriptions
|
||||||
.values_mut()
|
.values_mut()
|
||||||
.filter_map(|connection| connection.listener.as_mut())
|
.filter_map(|connection| connection.listener.as_mut())
|
||||||
.for_each(|listener| {
|
.for_each(|listener| {
|
||||||
if let Err(error) = listener.try_send(event.clone()) {
|
if let Err(error) = listener.try_send((event.clone(), status)) {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Error sending event to subscription: {:?}",
|
"Error sending event to subscription: {:?}",
|
||||||
error
|
error
|
||||||
|
|
@ -156,13 +151,3 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Hasher, Event> Default for Tracker<Hasher, Event>
|
|
||||||
where
|
|
||||||
Hasher: std::hash::Hasher + Default,
|
|
||||||
Event: 'static + Send + Clone,
|
|
||||||
{
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,9 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
|
||||||
categories = ["gui"]
|
categories = ["gui"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
svg = ["resvg", "usvg", "tiny-skia"]
|
geometry = ["lyon_path"]
|
||||||
image = ["png", "jpeg", "jpeg_rayon", "gif", "webp", "bmp"]
|
|
||||||
png = ["image_rs/png"]
|
|
||||||
jpeg = ["image_rs/jpeg"]
|
|
||||||
jpeg_rayon = ["image_rs/jpeg_rayon"]
|
|
||||||
gif = ["image_rs/gif"]
|
|
||||||
webp = ["image_rs/webp"]
|
|
||||||
pnm = ["image_rs/pnm"]
|
|
||||||
ico = ["image_rs/ico"]
|
|
||||||
bmp = ["image_rs/bmp"]
|
|
||||||
hdr = ["image_rs/hdr"]
|
|
||||||
dds = ["image_rs/dds"]
|
|
||||||
farbfeld = ["image_rs/farbfeld"]
|
|
||||||
canvas = ["lyon"]
|
|
||||||
qr_code = ["qrcode", "canvas"]
|
|
||||||
opengl = []
|
opengl = []
|
||||||
image_rs = ["kamadak-exif"]
|
image = ["dep:image", "kamadak-exif"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glam = "0.21.3"
|
glam = "0.21.3"
|
||||||
|
|
@ -40,45 +26,26 @@ bitflags = "1.2"
|
||||||
version = "1.4"
|
version = "1.4"
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
[dependencies.iced_native]
|
[dependencies.iced_core]
|
||||||
version = "0.9"
|
version = "0.8"
|
||||||
path = "../native"
|
path = "../core"
|
||||||
|
|
||||||
[dependencies.iced_style]
|
|
||||||
version = "0.7"
|
|
||||||
path = "../style"
|
|
||||||
|
|
||||||
[dependencies.lyon]
|
|
||||||
version = "1.0"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.qrcode]
|
|
||||||
version = "0.12"
|
|
||||||
optional = true
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.image_rs]
|
|
||||||
version = "0.24"
|
|
||||||
package = "image"
|
|
||||||
default-features = false
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.resvg]
|
|
||||||
version = "0.18"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.usvg]
|
|
||||||
version = "0.18"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.tiny-skia]
|
[dependencies.tiny-skia]
|
||||||
version = "0.6"
|
version = "0.8"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.image]
|
||||||
|
version = "0.24"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.kamadak-exif]
|
[dependencies.kamadak-exif]
|
||||||
version = "0.5"
|
version = "0.5"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.lyon_path]
|
||||||
|
version = "1"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
//! Write a graphics backend.
|
//! Write a graphics backend.
|
||||||
use iced_native::image;
|
use iced_core::image;
|
||||||
use iced_native::svg;
|
use iced_core::svg;
|
||||||
use iced_native::text;
|
use iced_core::text;
|
||||||
use iced_native::{Font, Point, Size};
|
use iced_core::{Font, Point, Size};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
//! A compositor is responsible for initializing a renderer and managing window
|
//! A compositor is responsible for initializing a renderer and managing window
|
||||||
//! surfaces.
|
//! surfaces.
|
||||||
use crate::{Color, Error, Viewport};
|
use crate::{Error, Viewport};
|
||||||
|
|
||||||
|
use iced_core::Color;
|
||||||
|
|
||||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -11,7 +13,7 @@ pub trait Compositor: Sized {
|
||||||
type Settings: Default;
|
type Settings: Default;
|
||||||
|
|
||||||
/// The iced renderer of the backend.
|
/// The iced renderer of the backend.
|
||||||
type Renderer: iced_native::Renderer;
|
type Renderer: iced_core::Renderer;
|
||||||
|
|
||||||
/// The surface of the backend.
|
/// The surface of the backend.
|
||||||
type Surface;
|
type Surface;
|
||||||
|
|
@ -28,6 +30,8 @@ pub trait Compositor: Sized {
|
||||||
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &W,
|
window: &W,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
) -> Self::Surface;
|
) -> Self::Surface;
|
||||||
|
|
||||||
/// Configures a new [`Surface`] with the given dimensions.
|
/// Configures a new [`Surface`] with the given dimensions.
|
||||||
36
graphics/src/geometry.rs
Normal file
36
graphics/src/geometry.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
//! Draw 2D graphics for your users.
|
||||||
|
//!
|
||||||
|
//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a
|
||||||
|
//! [`Frame`]. It can be used for animation, data visualization, game graphics,
|
||||||
|
//! and more!
|
||||||
|
pub mod fill;
|
||||||
|
pub mod path;
|
||||||
|
pub mod stroke;
|
||||||
|
|
||||||
|
mod style;
|
||||||
|
mod text;
|
||||||
|
|
||||||
|
pub use fill::Fill;
|
||||||
|
pub use path::Path;
|
||||||
|
pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
|
||||||
|
pub use style::Style;
|
||||||
|
pub use text::Text;
|
||||||
|
|
||||||
|
pub use iced_core::gradient::{self, Gradient};
|
||||||
|
|
||||||
|
use crate::Primitive;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Geometry(pub Primitive);
|
||||||
|
|
||||||
|
impl From<Geometry> for Primitive {
|
||||||
|
fn from(geometry: Geometry) -> Self {
|
||||||
|
geometry.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Renderer: iced_core::Renderer {
|
||||||
|
type Geometry;
|
||||||
|
|
||||||
|
fn draw(&mut self, geometry: Vec<Self::Geometry>);
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! Fill [crate::widget::canvas::Geometry] with a certain style.
|
//! Fill [crate::widget::canvas::Geometry] with a certain style.
|
||||||
use crate::{Color, Gradient};
|
use iced_core::{Color, Gradient};
|
||||||
|
|
||||||
pub use crate::widget::canvas::Style;
|
pub use crate::geometry::Style;
|
||||||
|
|
||||||
/// The style used to fill geometry.
|
/// The style used to fill geometry.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -19,14 +19,14 @@ pub struct Fill {
|
||||||
/// By default, it is set to `NonZero`.
|
/// By default, it is set to `NonZero`.
|
||||||
///
|
///
|
||||||
/// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
/// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
||||||
pub rule: FillRule,
|
pub rule: Rule,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Fill {
|
impl Default for Fill {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
style: Style::Solid(Color::BLACK),
|
style: Style::Solid(Color::BLACK),
|
||||||
rule: FillRule::NonZero,
|
rule: Rule::NonZero,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,16 +57,7 @@ impl From<Gradient> for Fill {
|
||||||
/// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
/// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum FillRule {
|
pub enum Rule {
|
||||||
NonZero,
|
NonZero,
|
||||||
EvenOdd,
|
EvenOdd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FillRule> for lyon::tessellation::FillRule {
|
|
||||||
fn from(rule: FillRule) -> lyon::tessellation::FillRule {
|
|
||||||
match rule {
|
|
||||||
FillRule::NonZero => lyon::tessellation::FillRule::NonZero,
|
|
||||||
FillRule::EvenOdd => lyon::tessellation::FillRule::EvenOdd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,18 +7,16 @@ mod builder;
|
||||||
pub use arc::Arc;
|
pub use arc::Arc;
|
||||||
pub use builder::Builder;
|
pub use builder::Builder;
|
||||||
|
|
||||||
use crate::widget::canvas::LineDash;
|
pub use lyon_path;
|
||||||
|
|
||||||
use iced_native::{Point, Size};
|
use iced_core::{Point, Size};
|
||||||
use lyon::algorithms::walk::{walk_along_path, RepeatedPattern, WalkerEvent};
|
|
||||||
use lyon::path::iterator::PathIterator;
|
|
||||||
|
|
||||||
/// An immutable set of points that may or may not be connected.
|
/// An immutable set of points that may or may not be connected.
|
||||||
///
|
///
|
||||||
/// A single [`Path`] can represent different kinds of 2D shapes!
|
/// A single [`Path`] can represent different kinds of 2D shapes!
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
raw: lyon::path::Path,
|
raw: lyon_path::Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
|
|
@ -56,54 +54,14 @@ impl Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn raw(&self) -> &lyon::path::Path {
|
pub fn raw(&self) -> &lyon_path::Path {
|
||||||
&self.raw
|
&self.raw
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn transformed(
|
pub fn transform(&self, transform: &lyon_path::math::Transform) -> Path {
|
||||||
&self,
|
|
||||||
transform: &lyon::math::Transform,
|
|
||||||
) -> Path {
|
|
||||||
Path {
|
Path {
|
||||||
raw: self.raw.clone().transformed(transform),
|
raw: self.raw.clone().transformed(transform),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
|
|
||||||
Path::new(|builder| {
|
|
||||||
let segments_odd = (line_dash.segments.len() % 2 == 1)
|
|
||||||
.then(|| [line_dash.segments, line_dash.segments].concat());
|
|
||||||
|
|
||||||
let mut draw_line = false;
|
|
||||||
|
|
||||||
walk_along_path(
|
|
||||||
path.raw().iter().flattened(0.01),
|
|
||||||
0.0,
|
|
||||||
lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
|
|
||||||
&mut RepeatedPattern {
|
|
||||||
callback: |event: WalkerEvent<'_>| {
|
|
||||||
let point = Point {
|
|
||||||
x: event.position.x,
|
|
||||||
y: event.position.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
if draw_line {
|
|
||||||
builder.line_to(point);
|
|
||||||
} else {
|
|
||||||
builder.move_to(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_line = !draw_line;
|
|
||||||
|
|
||||||
true
|
|
||||||
},
|
|
||||||
index: line_dash.offset,
|
|
||||||
intervals: segments_odd
|
|
||||||
.as_deref()
|
|
||||||
.unwrap_or(line_dash.segments),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue