summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-10-22 00:13:42 +0200
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-11-05 23:52:56 +0100
commit5c33ce18ed8b12db9a6ba138112804761d26fddb (patch)
treecb51c63fb14c4c84e20fd6f201ffaaaf86dc7b0c
parent42a2cb6d4f78343f43d6a68a28e5502d9426ed2c (diff)
downloadiced-5c33ce18ed8b12db9a6ba138112804761d26fddb.tar.gz
iced-5c33ce18ed8b12db9a6ba138112804761d26fddb.tar.bz2
iced-5c33ce18ed8b12db9a6ba138112804761d26fddb.zip
Draft `reactive-rendering` feature for `button`
-rw-r--r--Cargo.toml26
-rw-r--r--examples/multi_window/Cargo.toml2
-rw-r--r--widget/src/button.rs112
-rw-r--r--winit/Cargo.toml2
-rw-r--r--winit/src/program.rs95
-rw-r--r--winit/src/program/state.rs5
6 files changed, 156 insertions, 86 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 2db66da2..bf85ada2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,20 +22,20 @@ all-features = true
maintenance = { status = "actively-developed" }
[features]
-default = ["wgpu", "tiny-skia", "fira-sans", "auto-detect-theme"]
-# Enable the `wgpu` GPU-accelerated renderer backend
+default = ["wgpu", "tiny-skia", "fira-sans", "auto-detect-theme", "reactive-rendering"]
+# Enables the `wgpu` GPU-accelerated renderer backend
wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"]
-# Enable the `tiny-skia` software renderer backend
+# Enables the `tiny-skia` software renderer backend
tiny-skia = ["iced_renderer/tiny-skia"]
-# Enables the `Image` widget
+# Enables the `image` widget
image = ["image-without-codecs", "image/default"]
-# Enables the `Image` widget, without any built-in codecs of the `image` crate
+# Enables the `image` widget, without any built-in codecs of the `image` crate
image-without-codecs = ["iced_widget/image", "dep:image"]
-# Enables the `Svg` widget
+# Enables the `svg` widget
svg = ["iced_widget/svg"]
-# Enables the `Canvas` widget
+# Enables the `canvas` widget
canvas = ["iced_widget/canvas"]
-# Enables the `QRCode` widget
+# Enables the `qr_code` widget
qr_code = ["iced_widget/qr_code"]
# Enables the `markdown` widget
markdown = ["iced_widget/markdown"]
@@ -55,18 +55,18 @@ system = ["iced_winit/system"]
web-colors = ["iced_renderer/web-colors"]
# Enables the WebGL backend, replacing WebGPU
webgl = ["iced_renderer/webgl"]
-# Enables the syntax `highlighter` module
+# Enables syntax highligthing
highlighter = ["iced_highlighter", "iced_widget/highlighter"]
-# Enables experimental multi-window support.
-multi-window = ["iced_winit/multi-window"]
# Enables the advanced module
advanced = ["iced_core/advanced", "iced_widget/advanced"]
-# Enables embedding Fira Sans as the default font on Wasm builds
+# Embeds Fira Sans as the default font on Wasm builds
fira-sans = ["iced_renderer/fira-sans"]
-# Enables auto-detecting light/dark mode for the built-in theme
+# Auto-detects light/dark mode for the built-in theme
auto-detect-theme = ["iced_core/auto-detect-theme"]
# Enables strict assertions for debugging purposes at the expense of performance
strict-assertions = ["iced_renderer/strict-assertions"]
+# Redraws only when widgets react to some runtime event
+reactive-rendering = ["iced_winit/reactive-rendering"]
[dependencies]
iced_core.workspace = true
diff --git a/examples/multi_window/Cargo.toml b/examples/multi_window/Cargo.toml
index 2e222dfb..3f89417f 100644
--- a/examples/multi_window/Cargo.toml
+++ b/examples/multi_window/Cargo.toml
@@ -6,4 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../..", features = ["debug", "multi-window"] }
+iced = { path = "../..", features = ["debug"] }
diff --git a/widget/src/button.rs b/widget/src/button.rs
index a3394a01..5850cea0 100644
--- a/widget/src/button.rs
+++ b/widget/src/button.rs
@@ -26,6 +26,7 @@ use crate::core::theme::palette;
use crate::core::touch;
use crate::core::widget::tree::{self, Tree};
use crate::core::widget::Operation;
+use crate::core::window;
use crate::core::{
Background, Clipboard, Color, Element, Layout, Length, Padding, Rectangle,
Shadow, Shell, Size, Theme, Vector, Widget,
@@ -81,6 +82,7 @@ where
padding: Padding,
clip: bool,
class: Theme::Class<'a>,
+ status: Option<Status>,
}
enum OnPress<'a, Message> {
@@ -117,6 +119,7 @@ where
padding: DEFAULT_PADDING,
clip: false,
class: Theme::default(),
+ status: None,
}
}
@@ -294,49 +297,85 @@ where
return event::Status::Captured;
}
- match event {
- Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerPressed { .. }) => {
- if self.on_press.is_some() {
- let bounds = layout.bounds();
+ let mut update = || {
+ match event {
+ Event::Mouse(mouse::Event::ButtonPressed(
+ mouse::Button::Left,
+ ))
+ | Event::Touch(touch::Event::FingerPressed { .. }) => {
+ if self.on_press.is_some() {
+ let bounds = layout.bounds();
- if cursor.is_over(bounds) {
- let state = tree.state.downcast_mut::<State>();
+ if cursor.is_over(bounds) {
+ let state = tree.state.downcast_mut::<State>();
- state.is_pressed = true;
+ state.is_pressed = true;
- return event::Status::Captured;
+ return event::Status::Captured;
+ }
}
}
- }
- Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerLifted { .. }) => {
- if let Some(on_press) = self.on_press.as_ref().map(OnPress::get)
- {
- let state = tree.state.downcast_mut::<State>();
+ Event::Mouse(mouse::Event::ButtonReleased(
+ mouse::Button::Left,
+ ))
+ | Event::Touch(touch::Event::FingerLifted { .. }) => {
+ if let Some(on_press) =
+ self.on_press.as_ref().map(OnPress::get)
+ {
+ let state = tree.state.downcast_mut::<State>();
- if state.is_pressed {
- state.is_pressed = false;
+ if state.is_pressed {
+ state.is_pressed = false;
- let bounds = layout.bounds();
+ let bounds = layout.bounds();
- if cursor.is_over(bounds) {
- shell.publish(on_press);
- }
+ if cursor.is_over(bounds) {
+ shell.publish(on_press);
+ }
- return event::Status::Captured;
+ return event::Status::Captured;
+ }
}
}
+ Event::Touch(touch::Event::FingerLost { .. }) => {
+ let state = tree.state.downcast_mut::<State>();
+
+ state.is_pressed = false;
+ }
+ _ => {}
+ }
+
+ event::Status::Ignored
+ };
+
+ let update_status = update();
+
+ let current_status = if self.on_press.is_none() {
+ Status::Disabled
+ } else if cursor.is_over(layout.bounds()) {
+ let state = tree.state.downcast_ref::<State>();
+
+ if state.is_pressed {
+ Status::Pressed
+ } else {
+ Status::Hovered
}
- Event::Touch(touch::Event::FingerLost { .. }) => {
- let state = tree.state.downcast_mut::<State>();
+ } else {
+ Status::Active
+ };
- state.is_pressed = false;
+ if let Event::Window(window::Event::RedrawRequested(_now)) = event {
+ self.status = Some(current_status);
+ } else {
+ match self.status {
+ Some(status) if status != current_status => {
+ shell.request_redraw(window::RedrawRequest::NextFrame);
+ }
+ _ => {}
}
- _ => {}
}
- event::Status::Ignored
+ update_status
}
fn draw(
@@ -351,23 +390,8 @@ where
) {
let bounds = layout.bounds();
let content_layout = layout.children().next().unwrap();
- let is_mouse_over = cursor.is_over(bounds);
-
- let status = if self.on_press.is_none() {
- Status::Disabled
- } else if is_mouse_over {
- let state = tree.state.downcast_ref::<State>();
-
- if state.is_pressed {
- Status::Pressed
- } else {
- Status::Hovered
- }
- } else {
- Status::Active
- };
-
- let style = theme.style(&self.class, status);
+ let style =
+ theme.style(&self.class, self.status.unwrap_or(Status::Disabled));
if style.background.is_some()
|| style.border.width > 0.0
diff --git a/winit/Cargo.toml b/winit/Cargo.toml
index bd6feb00..b8f5a723 100644
--- a/winit/Cargo.toml
+++ b/winit/Cargo.toml
@@ -22,7 +22,7 @@ x11 = ["winit/x11"]
wayland = ["winit/wayland"]
wayland-dlopen = ["winit/wayland-dlopen"]
wayland-csd-adwaita = ["winit/wayland-csd-adwaita"]
-multi-window = ["iced_runtime/multi-window"]
+reactive-rendering = []
[dependencies]
iced_futures.workspace = true
diff --git a/winit/src/program.rs b/winit/src/program.rs
index 8d1eec3a..2ac7ad0d 100644
--- a/winit/src/program.rs
+++ b/winit/src/program.rs
@@ -691,6 +691,7 @@ async fn run_instance<P, C>(
let mut ui_caches = FxHashMap::default();
let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());
let mut clipboard = Clipboard::unconnected();
+ let mut redraw_queue = Vec::new();
debug.startup_finished();
@@ -758,14 +759,30 @@ async fn run_instance<P, C>(
}
Event::EventLoopAwakened(event) => {
match event {
- event::Event::NewEvents(
- event::StartCause::Init
- | event::StartCause::ResumeTimeReached { .. },
- ) => {
+ event::Event::NewEvents(event::StartCause::Init) => {
for (_id, window) in window_manager.iter_mut() {
window.raw.request_redraw();
}
}
+ event::Event::NewEvents(
+ event::StartCause::ResumeTimeReached { .. },
+ ) => {
+ let now = Instant::now();
+
+ while let Some((target, id)) =
+ redraw_queue.last().copied()
+ {
+ if target > now {
+ break;
+ }
+
+ let _ = redraw_queue.pop();
+
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.request_redraw();
+ }
+ }
+ }
event::Event::PlatformSpecific(
event::PlatformSpecific::MacOS(
event::MacOS::ReceivedUrl(url),
@@ -857,23 +874,19 @@ async fn run_instance<P, C>(
status: core::event::Status::Ignored,
});
- let _ = control_sender.start_send(Control::ChangeFlow(
- match ui_state {
- user_interface::State::Updated {
- redraw_request: Some(redraw_request),
- } => match redraw_request {
- window::RedrawRequest::NextFrame => {
- window.raw.request_redraw();
-
- ControlFlow::Wait
- }
- window::RedrawRequest::At(at) => {
- ControlFlow::WaitUntil(at)
- }
- },
- _ => ControlFlow::Wait,
- },
- ));
+ if let user_interface::State::Updated {
+ redraw_request: Some(redraw_request),
+ } = ui_state
+ {
+ match redraw_request {
+ window::RedrawRequest::NextFrame => {
+ window.raw.request_redraw();
+ }
+ window::RedrawRequest::At(at) => {
+ redraw_queue.push((at, id));
+ }
+ }
+ }
let physical_size = window.state.physical_size();
@@ -1065,13 +1078,25 @@ async fn run_instance<P, C>(
&mut messages,
);
+ #[cfg(not(feature = "reactive-rendering"))]
window.raw.request_redraw();
- if !uis_stale {
- uis_stale = matches!(
- ui_state,
- user_interface::State::Outdated
- );
+ match ui_state {
+ #[cfg(feature = "reactive-rendering")]
+ user_interface::State::Updated {
+ redraw_request: Some(redraw_request),
+ } => match redraw_request {
+ window::RedrawRequest::NextFrame => {
+ window.raw.request_redraw();
+ }
+ window::RedrawRequest::At(at) => {
+ redraw_queue.push((at, id));
+ }
+ },
+ user_interface::State::Outdated => {
+ uis_stale = true;
+ }
+ user_interface::State::Updated { .. } => {}
}
for (event, status) in window_events
@@ -1139,6 +1164,24 @@ async fn run_instance<P, C>(
actions = 0;
}
}
+
+ if !redraw_queue.is_empty() {
+ redraw_queue.sort_by(
+ |(target_a, _), (target_b, _)| {
+ target_a.cmp(target_b).reverse()
+ },
+ );
+
+ let (target, _id) = redraw_queue
+ .last()
+ .copied()
+ .expect("Redraw queue is not empty");
+
+ let _ =
+ control_sender.start_send(Control::ChangeFlow(
+ ControlFlow::WaitUntil(target),
+ ));
+ }
}
_ => {}
}
diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs
index a7fa2788..b8a58960 100644
--- a/winit/src/program/state.rs
+++ b/winit/src/program/state.rs
@@ -190,7 +190,10 @@ where
..
},
..
- } => _debug.toggle(),
+ } => {
+ _debug.toggle();
+ window.request_redraw();
+ }
_ => {}
}
}