summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2023-11-29 22:28:31 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2023-11-29 22:28:31 +0100
commite09b4e24dda51b8212d8ece52431dacaa3922a7b (patch)
tree7005e181528134ebdde5bbbe5909273db9f30174 /runtime
parent83c7870c569a2976923ee6243a19813094d44673 (diff)
parent7f8b17604a31e00becc43130ec516c1a53552c88 (diff)
downloadiced-e09b4e24dda51b8212d8ece52431dacaa3922a7b.tar.gz
iced-e09b4e24dda51b8212d8ece52431dacaa3922a7b.tar.bz2
iced-e09b4e24dda51b8212d8ece52431dacaa3922a7b.zip
Merge branch 'master' into feat/multi-window-support
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Cargo.toml28
-rw-r--r--runtime/README.md10
-rw-r--r--runtime/src/command.rs41
-rw-r--r--runtime/src/command/action.rs9
-rw-r--r--runtime/src/debug/basic.rs20
-rw-r--r--runtime/src/lib.rs35
-rw-r--r--runtime/src/overlay/nested.rs21
-rw-r--r--runtime/src/program/state.rs4
-rw-r--r--runtime/src/user_interface.rs76
-rw-r--r--runtime/src/window.rs7
-rw-r--r--runtime/src/window/screenshot.rs2
11 files changed, 141 insertions, 112 deletions
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 3d2976a7..8089d545 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -1,24 +1,22 @@
[package]
name = "iced_runtime"
-version = "0.1.0"
-authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
-edition = "2021"
-description = "A renderer-agnostic library for native GUIs"
-license = "MIT"
-repository = "https://github.com/iced-rs/iced"
+description = "A renderer-agnostic runtime for iced"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+categories.workspace = true
+keywords.workspace = true
[features]
debug = []
multi-window = []
[dependencies]
-thiserror = "1"
+iced_core.workspace = true
+iced_futures.workspace = true
+iced_futures.features = ["thread-pool"]
-[dependencies.iced_core]
-version = "0.9"
-path = "../core"
-
-[dependencies.iced_futures]
-version = "0.6"
-path = "../futures"
-features = ["thread-pool"]
+thiserror.workspace = true
diff --git a/runtime/README.md b/runtime/README.md
index 1b0fa857..35c7eb5e 100644
--- a/runtime/README.md
+++ b/runtime/README.md
@@ -1,12 +1,12 @@
# `iced_runtime`
-[![Documentation](https://docs.rs/iced_native/badge.svg)][documentation]
-[![Crates.io](https://img.shields.io/crates/v/iced_native.svg)](https://crates.io/crates/iced_native)
-[![License](https://img.shields.io/crates/l/iced_native.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
+[![Documentation](https://docs.rs/iced_runtime/badge.svg)][documentation]
+[![Crates.io](https://img.shields.io/crates/v/iced_runtime.svg)](https://crates.io/crates/iced_runtime)
+[![License](https://img.shields.io/crates/l/iced_runtime.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
-`iced_runtime` takes [`iced_core`] and builds a native runtime on top of it.
+`iced_runtime` takes [`iced_core`] and builds a runtime on top of it.
-[documentation]: https://docs.rs/iced_native
+[documentation]: https://docs.rs/iced_runtime
[`iced_core`]: ../core
[`iced_winit`]: ../winit
[`druid`]: https://github.com/xi-editor/druid
diff --git a/runtime/src/command.rs b/runtime/src/command.rs
index cd4c51ff..f70da915 100644
--- a/runtime/src/command.rs
+++ b/runtime/src/command.rs
@@ -4,8 +4,11 @@ mod action;
pub use action::Action;
use crate::core::widget;
+use crate::futures::futures;
use crate::futures::MaybeSend;
+use futures::channel::mpsc;
+use futures::Stream;
use std::fmt;
use std::future::Future;
@@ -40,14 +43,24 @@ impl<T> Command<T> {
/// Creates a [`Command`] that performs the action of the given future.
pub fn perform<A>(
- future: impl Future<Output = T> + 'static + MaybeSend,
- f: impl FnOnce(T) -> A + 'static + MaybeSend,
- ) -> Command<A> {
- use iced_futures::futures::FutureExt;
+ future: impl Future<Output = A> + 'static + MaybeSend,
+ f: impl FnOnce(A) -> T + 'static + MaybeSend,
+ ) -> Command<T> {
+ use futures::FutureExt;
Command::single(Action::Future(Box::pin(future.map(f))))
}
+ /// Creates a [`Command`] that runs the given stream to completion.
+ pub fn run<A>(
+ stream: impl Stream<Item = A> + 'static + MaybeSend,
+ f: impl Fn(A) -> T + 'static + MaybeSend,
+ ) -> Command<T> {
+ use futures::StreamExt;
+
+ Command::single(Action::Stream(Box::pin(stream.map(f))))
+ }
+
/// Creates a [`Command`] that performs the actions of all the given
/// commands.
///
@@ -106,3 +119,23 @@ impl<T> fmt::Debug for Command<T> {
command.fmt(f)
}
}
+
+/// Creates a [`Command`] that produces the `Message`s published from a [`Future`]
+/// to an [`mpsc::Sender`] with the given bounds.
+pub fn channel<Fut, Message>(
+ size: usize,
+ f: impl FnOnce(mpsc::Sender<Message>) -> Fut + MaybeSend + 'static,
+) -> Command<Message>
+where
+ Fut: Future<Output = ()> + MaybeSend + 'static,
+ Message: 'static + MaybeSend,
+{
+ use futures::future;
+ use futures::stream::{self, StreamExt};
+
+ let (sender, receiver) = mpsc::channel(size);
+
+ let runner = stream::once(f(sender)).filter_map(|_| future::ready(None));
+
+ Command::single(Action::Stream(Box::pin(stream::select(receiver, runner))))
+}
diff --git a/runtime/src/command/action.rs b/runtime/src/command/action.rs
index b2594379..7a70920e 100644
--- a/runtime/src/command/action.rs
+++ b/runtime/src/command/action.rs
@@ -18,6 +18,11 @@ pub enum Action<T> {
/// [`Future`]: iced_futures::BoxFuture
Future(iced_futures::BoxFuture<T>),
+ /// Run a [`Stream`] to completion.
+ ///
+ /// [`Stream`]: iced_futures::BoxStream
+ Stream(iced_futures::BoxStream<T>),
+
/// Run a clipboard action.
Clipboard(clipboard::Action<T>),
@@ -52,10 +57,11 @@ impl<T> Action<T> {
A: 'static,
T: 'static,
{
- use iced_futures::futures::FutureExt;
+ use iced_futures::futures::{FutureExt, StreamExt};
match self {
Self::Future(future) => Action::Future(Box::pin(future.map(f))),
+ Self::Stream(stream) => Action::Stream(Box::pin(stream.map(f))),
Self::Clipboard(action) => Action::Clipboard(action.map(f)),
Self::Window(id, window) => Action::Window(id, window.map(f)),
Self::System(system) => Action::System(system.map(f)),
@@ -74,6 +80,7 @@ impl<T> fmt::Debug for Action<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Future(_) => write!(f, "Action::Future"),
+ Self::Stream(_) => write!(f, "Action::Stream"),
Self::Clipboard(action) => {
write!(f, "Action::Clipboard({action:?})")
}
diff --git a/runtime/src/debug/basic.rs b/runtime/src/debug/basic.rs
index 32f725a1..4c994a2f 100644
--- a/runtime/src/debug/basic.rs
+++ b/runtime/src/debug/basic.rs
@@ -75,7 +75,7 @@ impl Debug {
}
pub fn startup_finished(&mut self) {
- self.startup_duration = time::Instant::now() - self.startup_start;
+ self.startup_duration = self.startup_start.elapsed();
}
pub fn update_started(&mut self) {
@@ -83,8 +83,7 @@ impl Debug {
}
pub fn update_finished(&mut self) {
- self.update_durations
- .push(time::Instant::now() - self.update_start);
+ self.update_durations.push(self.update_start.elapsed());
}
pub fn view_started(&mut self) {
@@ -92,8 +91,7 @@ impl Debug {
}
pub fn view_finished(&mut self) {
- self.view_durations
- .push(time::Instant::now() - self.view_start);
+ self.view_durations.push(self.view_start.elapsed());
}
pub fn layout_started(&mut self) {
@@ -101,8 +99,7 @@ impl Debug {
}
pub fn layout_finished(&mut self) {
- self.layout_durations
- .push(time::Instant::now() - self.layout_start);
+ self.layout_durations.push(self.layout_start.elapsed());
}
pub fn event_processing_started(&mut self) {
@@ -110,8 +107,7 @@ impl Debug {
}
pub fn event_processing_finished(&mut self) {
- self.event_durations
- .push(time::Instant::now() - self.event_start);
+ self.event_durations.push(self.event_start.elapsed());
}
pub fn draw_started(&mut self) {
@@ -119,8 +115,7 @@ impl Debug {
}
pub fn draw_finished(&mut self) {
- self.draw_durations
- .push(time::Instant::now() - self.draw_start);
+ self.draw_durations.push(self.draw_start.elapsed());
}
pub fn render_started(&mut self) {
@@ -128,8 +123,7 @@ impl Debug {
}
pub fn render_finished(&mut self) {
- self.render_durations
- .push(time::Instant::now() - self.render_start);
+ self.render_durations.push(self.render_start.elapsed());
}
pub fn log_message<Message: std::fmt::Debug>(&mut self, message: &Message) {
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 4c39f80f..03906f45 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -2,46 +2,19 @@
//!
//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/raw/improvement/update-ecosystem-and-roadmap/docs/graphs/native.png)
//!
-//! `iced_native` takes [`iced_core`] and builds a native runtime on top of it,
-//! featuring:
+//! `iced_runtime` takes [`iced_core`] and builds a native runtime on top of it.
//!
-//! - A custom layout engine, greatly inspired by [`druid`]
-//! - Event handling for all the built-in widgets
-//! - A renderer-agnostic API
-//!
-//! To achieve this, it introduces a couple of reusable interfaces:
-//!
-//! - A [`Widget`] trait, which is used to implement new widgets: from layout
-//! requirements to event and drawing logic.
-//! - A bunch of `Renderer` traits, meant to keep the crate renderer-agnostic.
-//!
-//! # Usage
-//! The strategy to use this crate depends on your particular use case. If you
-//! want to:
-//! - Implement a custom shell or integrate it in your own system, check out the
-//! [`UserInterface`] type.
-//! - Build a new renderer, see the [renderer] module.
-//! - Build a custom widget, start at the [`Widget`] trait.
-//!
-//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.9/core
-//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.9/winit
-//! [`druid`]: https://github.com/xi-editor/druid
-//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
-//! [renderer]: crate::renderer
+//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.10/core
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
+#![forbid(unsafe_code, rust_2018_idioms)]
#![deny(
missing_debug_implementations,
missing_docs,
unused_results,
- clippy::extra_unused_lifetimes,
- clippy::from_over_into,
- clippy::needless_borrow,
- clippy::new_without_default,
- clippy::useless_conversion
+ rustdoc::broken_intra_doc_links
)]
-#![forbid(unsafe_code, rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod clipboard;
pub mod command;
diff --git a/runtime/src/overlay/nested.rs b/runtime/src/overlay/nested.rs
index b729f769..4256efb7 100644
--- a/runtime/src/overlay/nested.rs
+++ b/runtime/src/overlay/nested.rs
@@ -4,9 +4,11 @@ use crate::core::mouse;
use crate::core::overlay;
use crate::core::renderer;
use crate::core::widget;
-use crate::core::{Clipboard, Event, Layout, Point, Rectangle, Shell, Size};
+use crate::core::{
+ Clipboard, Event, Layout, Point, Rectangle, Shell, Size, Vector,
+};
-/// An [`Overlay`] container that displays nested overlays
+/// An overlay container that displays nested overlays
#[allow(missing_debug_implementations)]
pub struct Nested<'a, Message, Renderer> {
overlay: overlay::Element<'a, Message, Renderer>,
@@ -27,23 +29,24 @@ where
}
/// Returns the layout [`Node`] of the [`Nested`] overlay.
+ ///
+ /// [`Node`]: layout::Node
pub fn layout(
&mut self,
renderer: &Renderer,
bounds: Size,
- position: Point,
+ _position: Point,
+ translation: Vector,
) -> layout::Node {
fn recurse<Message, Renderer>(
element: &mut overlay::Element<'_, Message, Renderer>,
renderer: &Renderer,
bounds: Size,
- position: Point,
+ translation: Vector,
) -> layout::Node
where
Renderer: renderer::Renderer,
{
- let translation = position - Point::ORIGIN;
-
let node = element.layout(renderer, bounds, translation);
if let Some(mut nested) =
@@ -53,7 +56,7 @@ where
node.size(),
vec![
node,
- recurse(&mut nested, renderer, bounds, position),
+ recurse(&mut nested, renderer, bounds, translation),
],
)
} else {
@@ -61,7 +64,7 @@ where
}
}
- recurse(&mut self.overlay, renderer, bounds, position)
+ recurse(&mut self.overlay, renderer, bounds, translation)
}
/// Draws the [`Nested`] overlay using the associated `Renderer`.
@@ -162,7 +165,7 @@ where
}
}
- recurse(&mut self.overlay, layout, renderer, operation)
+ recurse(&mut self.overlay, layout, renderer, operation);
}
/// Processes a runtime [`Event`].
diff --git a/runtime/src/program/state.rs b/runtime/src/program/state.rs
index 35df6078..6f8f4063 100644
--- a/runtime/src/program/state.rs
+++ b/runtime/src/program/state.rs
@@ -175,7 +175,7 @@ where
(uncaptured_events, command)
}
- /// Applies [`widget::Operation`]s to the [`State`]
+ /// Applies [`Operation`]s to the [`State`]
pub fn operate(
&mut self,
renderer: &mut P::Renderer,
@@ -200,7 +200,7 @@ where
match operation.finish() {
operation::Outcome::None => {}
operation::Outcome::Some(message) => {
- self.queued_messages.push(message)
+ self.queued_messages.push(message);
}
operation::Outcome::Chain(next) => {
current_operation = Some(next);
diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs
index 619423fd..3594ac18 100644
--- a/runtime/src/user_interface.rs
+++ b/runtime/src/user_interface.rs
@@ -5,7 +5,9 @@ use crate::core::mouse;
use crate::core::renderer;
use crate::core::widget;
use crate::core::window;
-use crate::core::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
+use crate::core::{
+ Clipboard, Element, Layout, Point, Rectangle, Shell, Size, Vector,
+};
use crate::overlay;
/// A set of interactive graphical elements with a specific [`Layout`].
@@ -19,7 +21,7 @@ use crate::overlay;
/// The [`integration`] example uses a [`UserInterface`] to integrate Iced in an
/// existing graphical application.
///
-/// [`integration`]: https://github.com/iced-rs/iced/tree/0.9/examples/integration
+/// [`integration`]: https://github.com/iced-rs/iced/tree/0.10/examples/integration
#[allow(missing_debug_implementations)]
pub struct UserInterface<'a, Message, Renderer> {
root: Element<'a, Message, Renderer>,
@@ -95,8 +97,11 @@ where
let Cache { mut state } = cache;
state.diff(root.as_widget());
- let base =
- renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds));
+ let base = root.as_widget().layout(
+ &mut state,
+ renderer,
+ &layout::Limits::new(Size::ZERO, bounds),
+ );
UserInterface {
root,
@@ -196,7 +201,8 @@ where
let bounds = self.bounds;
let mut overlay = manual_overlay.as_mut().unwrap();
- let mut layout = overlay.layout(renderer, bounds, Point::ORIGIN);
+ let mut layout =
+ overlay.layout(renderer, bounds, Point::ORIGIN, Vector::ZERO);
let mut event_statuses = Vec::new();
for event in events.iter().cloned() {
@@ -226,8 +232,9 @@ where
if shell.is_layout_invalid() {
let _ = ManuallyDrop::into_inner(manual_overlay);
- self.base = renderer.layout(
- &self.root,
+ self.base = self.root.as_widget().layout(
+ &mut self.state,
+ renderer,
&layout::Limits::new(Size::ZERO, self.bounds),
);
@@ -249,8 +256,12 @@ where
overlay = manual_overlay.as_mut().unwrap();
shell.revalidate_layout(|| {
- layout =
- overlay.layout(renderer, bounds, Point::ORIGIN);
+ layout = overlay.layout(
+ renderer,
+ bounds,
+ Point::ORIGIN,
+ Vector::ZERO,
+ );
});
}
@@ -284,12 +295,14 @@ where
(cursor, vec![event::Status::Ignored; events.len()])
};
+ let viewport = Rectangle::with_size(self.bounds);
+
let _ = ManuallyDrop::into_inner(manual_overlay);
let event_statuses = events
.iter()
.cloned()
- .zip(overlay_statuses.into_iter())
+ .zip(overlay_statuses)
.map(|(event, overlay_status)| {
if matches!(overlay_status, event::Status::Captured) {
return overlay_status;
@@ -305,6 +318,7 @@ where
renderer,
clipboard,
&mut shell,
+ &viewport,
);
if matches!(event_status, event::Status::Captured) {
@@ -322,8 +336,9 @@ where
}
shell.revalidate_layout(|| {
- self.base = renderer.layout(
- &self.root,
+ self.base = self.root.as_widget().layout(
+ &mut self.state,
+ renderer,
&layout::Limits::new(Size::ZERO, self.bounds),
);
@@ -353,7 +368,7 @@ where
/// It returns the current [`mouse::Interaction`]. You should update the
/// icon of the mouse cursor accordingly in your system.
///
- /// [`Renderer`]: crate::Renderer
+ /// [`Renderer`]: crate::core::Renderer
///
/// # Example
/// We can finally draw our [counter](index.html#usage) by
@@ -440,7 +455,12 @@ where
.map(overlay::Nested::new)
{
let overlay_layout = self.overlay.take().unwrap_or_else(|| {
- overlay.layout(renderer, self.bounds, Point::ORIGIN)
+ overlay.layout(
+ renderer,
+ self.bounds,
+ Point::ORIGIN,
+ Vector::ZERO,
+ )
});
let cursor = if cursor
@@ -510,17 +530,13 @@ where
renderer,
);
- let overlay_bounds = layout.bounds();
-
- renderer.with_layer(overlay_bounds, |renderer| {
- overlay.draw(
- renderer,
- theme,
- style,
- Layout::new(layout),
- cursor,
- );
- });
+ overlay.draw(
+ renderer,
+ theme,
+ style,
+ Layout::new(layout),
+ cursor,
+ );
if cursor
.position()
@@ -562,8 +578,12 @@ where
.map(overlay::Nested::new)
{
if self.overlay.is_none() {
- self.overlay =
- Some(overlay.layout(renderer, self.bounds, Point::ORIGIN));
+ self.overlay = Some(overlay.layout(
+ renderer,
+ self.bounds,
+ Point::ORIGIN,
+ Vector::ZERO,
+ ));
}
overlay.operate(
@@ -620,7 +640,7 @@ pub enum State {
/// The [`UserInterface`] is up-to-date and can be reused without
/// rebuilding.
Updated {
- /// The [`Instant`] when a redraw should be performed.
+ /// The [`window::RedrawRequest`] when a redraw should be performed.
redraw_request: Option<window::RedrawRequest>,
},
}
diff --git a/runtime/src/window.rs b/runtime/src/window.rs
index 4737dcdd..375ce889 100644
--- a/runtime/src/window.rs
+++ b/runtime/src/window.rs
@@ -11,7 +11,8 @@ use crate::command::{self, Command};
use crate::core::time::Instant;
use crate::core::window::{self, Event, Icon, Level, Mode, UserAttention};
use crate::core::Size;
-use crate::futures::subscription::{self, Subscription};
+use crate::futures::event;
+use crate::futures::Subscription;
/// Subscribes to the frames of the window of the running application.
///
@@ -22,8 +23,8 @@ use crate::futures::subscription::{self, Subscription};
/// In any case, this [`Subscription`] is useful to smoothly draw application-driven
/// animations without missing any frames.
pub fn frames() -> Subscription<Instant> {
- subscription::raw_events(|event, _status| match event {
- iced_core::Event::Window(_, Event::RedrawRequested(at)) => Some(at),
+ event::listen_raw(|event, _status| match event {
+ crate::core::Event::Window(_, Event::RedrawRequested(at)) => Some(at),
_ => None,
})
}
diff --git a/runtime/src/window/screenshot.rs b/runtime/src/window/screenshot.rs
index c84286b6..21e04718 100644
--- a/runtime/src/window/screenshot.rs
+++ b/runtime/src/window/screenshot.rs
@@ -6,7 +6,7 @@ use std::sync::Arc;
/// Data of a screenshot, captured with `window::screenshot()`.
///
-/// The `bytes` of this screenshot will always be ordered as `RGBA` in the sRGB color space.
+/// The `bytes` of this screenshot will always be ordered as `RGBA` in the `sRGB` color space.
#[derive(Clone)]
pub struct Screenshot {
/// The bytes of the [`Screenshot`].