diff options
author | 2024-10-22 00:13:42 +0200 | |
---|---|---|
committer | 2024-11-05 23:52:56 +0100 | |
commit | 5c33ce18ed8b12db9a6ba138112804761d26fddb (patch) | |
tree | cb51c63fb14c4c84e20fd6f201ffaaaf86dc7b0c | |
parent | 42a2cb6d4f78343f43d6a68a28e5502d9426ed2c (diff) | |
download | iced-5c33ce18ed8b12db9a6ba138112804761d26fddb.tar.gz iced-5c33ce18ed8b12db9a6ba138112804761d26fddb.tar.bz2 iced-5c33ce18ed8b12db9a6ba138112804761d26fddb.zip |
Draft `reactive-rendering` feature for `button`
-rw-r--r-- | Cargo.toml | 26 | ||||
-rw-r--r-- | examples/multi_window/Cargo.toml | 2 | ||||
-rw-r--r-- | widget/src/button.rs | 112 | ||||
-rw-r--r-- | winit/Cargo.toml | 2 | ||||
-rw-r--r-- | winit/src/program.rs | 95 | ||||
-rw-r--r-- | winit/src/program/state.rs | 5 |
6 files changed, 156 insertions, 86 deletions
@@ -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(); + } _ => {} } } |