From ebdfa688a326f2ff977b6891fc723db05bdfa9fe Mon Sep 17 00:00:00 2001 From: Eduardo Flores Date: Wed, 30 Oct 2024 16:42:25 +0100 Subject: [PATCH 01/18] improv: expose set_blur --- runtime/src/window.rs | 10 ++++++++++ winit/src/program.rs | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/runtime/src/window.rs b/runtime/src/window.rs index 48c200ec99..2086ffecba 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -159,6 +159,9 @@ pub enum Action { /// This enables mouse events for the window and stops mouse events /// from being passed to whatever is underneath. DisableMousePassthrough(Id), + + /// Set window blur. + SetBlur(bool), } /// Subscribes to the frames of the window of the running application. @@ -456,3 +459,10 @@ pub fn enable_mouse_passthrough(id: Id) -> Task { pub fn disable_mouse_passthrough(id: Id) -> Task { task::effect(crate::Action::Window(Action::DisableMousePassthrough(id))) } + +/// Sets the blur effect for the window. +/// +/// This is only supported on platforms that support window blur. +pub fn set_blur(enable: bool) -> Task { + task::effect(crate::Action::Window(Action::SetBlur(enable))) +} diff --git a/winit/src/program.rs b/winit/src/program.rs index 70b00f0c54..ffc8dd020f 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -2202,6 +2202,11 @@ fn run_action( let _ = window.raw.set_cursor_hittest(true); } } + window::Action::SetBlur(enable) => { + if let Some(window) = window_manager.get_mut(0) { + window.raw.set_blur(enable); + } + } }, Action::System(action) => match action { system::Action::QueryInformation(_channel) => { From 2212e584ccce7db74aa65077585b50690a648282 Mon Sep 17 00:00:00 2001 From: Eduardo Flores Date: Wed, 30 Oct 2024 17:19:19 +0100 Subject: [PATCH 02/18] fix: use enable and disable methods --- runtime/src/window.rs | 20 +++++++++++++++----- winit/src/program.rs | 11 ++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/runtime/src/window.rs b/runtime/src/window.rs index 2086ffecba..ff9660b744 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -160,8 +160,11 @@ pub enum Action { /// from being passed to whatever is underneath. DisableMousePassthrough(Id), - /// Set window blur. - SetBlur(bool), + /// Enable window blur. + EnableBlur(Id), + + /// Disable window blur. + DisableBlur(Id), } /// Subscribes to the frames of the window of the running application. @@ -460,9 +463,16 @@ pub fn disable_mouse_passthrough(id: Id) -> Task { task::effect(crate::Action::Window(Action::DisableMousePassthrough(id))) } -/// Sets the blur effect for the window. +/// Enable the blur effect for a window. +/// +/// This is only supported on platforms that support window blur. +pub fn enable_blur(id: Id) -> Task { + task::effect(crate::Action::Window(Action::EnableBlur(id))) +} + +/// Enable the blur effect for a window. /// /// This is only supported on platforms that support window blur. -pub fn set_blur(enable: bool) -> Task { - task::effect(crate::Action::Window(Action::SetBlur(enable))) +pub fn disable_blur(id: Id) -> Task { + task::effect(crate::Action::Window(Action::DisableBlur(id))) } diff --git a/winit/src/program.rs b/winit/src/program.rs index ffc8dd020f..a12ec20bc2 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -2202,9 +2202,14 @@ fn run_action( let _ = window.raw.set_cursor_hittest(true); } } - window::Action::SetBlur(enable) => { - if let Some(window) = window_manager.get_mut(0) { - window.raw.set_blur(enable); + window::Action::EnableBlur(id) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_blur(true); + } + } + window::Action::DisableBlur(id) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_blur(false); } } }, From 02b8d08728bb12ea23e82babbd3f05602f7d578f Mon Sep 17 00:00:00 2001 From: Eduardo Flores Date: Wed, 30 Oct 2024 17:49:24 +0100 Subject: [PATCH 03/18] fix: documentation --- runtime/src/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/window.rs b/runtime/src/window.rs index ff9660b744..59c8d59296 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -470,7 +470,7 @@ pub fn enable_blur(id: Id) -> Task { task::effect(crate::Action::Window(Action::EnableBlur(id))) } -/// Enable the blur effect for a window. +/// Disable the blur effect for a window. /// /// This is only supported on platforms that support window blur. pub fn disable_blur(id: Id) -> Task { From a7e035cd852ab13ce0ef04671c1e683fc343aff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vuka=C5=A1in=20Vojinovi=C4=87?= Date: Mon, 4 Nov 2024 20:33:27 +0100 Subject: [PATCH 04/18] fix: scrollbar width --- widget/src/scrollable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index d125c7f4c8..fcf6f2421d 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -376,9 +376,9 @@ pub struct Scrollbar { impl Default for Scrollbar { fn default() -> Self { Self { - width: 10.0, + width: 8.0, margin: 0.0, - scroller_width: 10.0, + scroller_width: 8.0, alignment: Anchor::Start, spacing: None, } From 57288e5a3e1d238572f15153d7c0a7fe5f1e43da Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 4 Nov 2024 22:30:38 -0500 Subject: [PATCH 05/18] fix: avoid overwriting id when diffing children this can interact with the named IDs, and cause state mismatches, and doesn't need to be done, because the ID will be updated by the diff method if there is a Tag match anyways. --- core/src/widget/tree.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index 963a738137..b17d9a123f 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -334,9 +334,6 @@ impl Tree { ) { let c = &mut id_list[child_state_i]; - if len_changed { - c.id.clone_from(new_id); - } child_state_i += 1; c } else { From 65c7a3d9cbb096191cec0c40c39310a54570fdab Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 5 Nov 2024 17:27:13 -0500 Subject: [PATCH 06/18] fix: offset DnD events in scrollable --- widget/src/scrollable.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index fcf6f2421d..87bcf9319c 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -763,10 +763,40 @@ where let translation = state.translation(self.direction, bounds, content_bounds); + let mut c_event = match event.clone() { + Event::Dnd(dnd::DndEvent::Offer( + id, + dnd::OfferEvent::Enter { + x, + y, + mime_types, + surface, + }, + )) => Event::Dnd(dnd::DndEvent::Offer( + id.clone(), + dnd::OfferEvent::Enter { + x: x + translation.x as f64, + y: y + translation.y as f64, + mime_types: mime_types.clone(), + surface: surface.clone(), + }, + )), + Event::Dnd(dnd::DndEvent::Offer( + id, + dnd::OfferEvent::Motion { x, y }, + )) => Event::Dnd(dnd::DndEvent::Offer( + id.clone(), + dnd::OfferEvent::Motion { + x: x + translation.x as f64, + y: y + translation.y as f64, + }, + )), + e => e, + }; self.content.as_widget_mut().on_event( &mut tree.children[0], - event.clone(), + c_event, content, cursor, renderer, From 51ad918bf1b8ec1da0799217e917ae089a307d39 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 6 Nov 2024 22:31:05 -0500 Subject: [PATCH 07/18] fix: tree diff improvements Search for the old child state with matching index --- core/src/widget/tree.rs | 195 +++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 91 deletions(-) diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index b17d9a123f..f9edd0360d 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -3,7 +3,7 @@ use crate::id::{Id, Internal}; use crate::Widget; use std::any::{self, Any}; use std::borrow::{Borrow, BorrowMut, Cow}; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::hash::Hash; use std::{fmt, mem}; @@ -164,93 +164,102 @@ impl Tree { { let borrowed: &mut dyn Widget = new.borrow_mut(); - let mut needs_reset = false; - let tag_match = self.tag == borrowed.tag(); - if let Some(Id(Internal::Custom(_, n))) = borrowed.id() { - if let Some((mut state, children)) = NAMED - .with(|named| named.borrow_mut().remove(&n)) - .or_else(|| { - //check self.id - if let Some(Id(Internal::Custom(_, ref name))) = self.id { - if name == &n { - Some(( - mem::replace(&mut self.state, State::None), - self.children - .iter_mut() - .map(|s| { - // take the data - mem::replace( - s, - Tree { - id: s.id.clone(), - tag: s.tag, - ..Tree::empty() - }, - ) - }) - .enumerate() - .collect(), - )) + + let mut tag_match = self.tag == borrowed.tag(); + if tag_match { + if let Some(Id(Internal::Custom(_, n))) = borrowed.id() { + if let Some((mut state, children)) = NAMED + .with(|named| named.borrow_mut().remove(&n)) + .or_else(|| { + //check self.id + if let Some(Id(Internal::Custom(_, ref name))) = self.id + { + if name == &n { + Some(( + mem::replace(&mut self.state, State::None), + self.children + .iter_mut() + .map(|s| { + // take the data + mem::replace( + s, + Tree { + id: s.id.clone(), + tag: s.tag, + ..Tree::empty() + }, + ) + }) + .enumerate() + .collect(), + )) + } else { + None + } } else { None } + }) + { + std::mem::swap(&mut self.state, &mut state); + let widget_children = borrowed.children(); + if !tag_match + || self.children.len() != widget_children.len() + { + self.children = borrowed.children(); } else { - None - } - }) - { - std::mem::swap(&mut self.state, &mut state); - let widget_children = borrowed.children(); - if !tag_match || self.children.len() != widget_children.len() { - self.children = borrowed.children(); - } else { - for (old_i, mut old) in children { - let Some(my_state) = self.children.get_mut(old_i) - else { - continue; - }; - if my_state.tag != old.tag || { - !match (&old.id, &my_state.id) { - ( - Some(Id(Internal::Custom(_, ref old_name))), - Some(Id(Internal::Custom(_, ref my_name))), - ) => old_name == my_name, - ( - Some(Id(Internal::Set(a))), - Some(Id(Internal::Set(b))), - ) => a.len() == b.len(), - ( - Some(Id(Internal::Unique(_))), - Some(Id(Internal::Unique(_))), - ) => true, - (None, None) => true, - _ => false, + for (old_i, mut old) in children { + let Some(my_state) = self.children.get_mut(old_i) + else { + continue; + }; + if my_state.tag != old.tag || { + !match (&old.id, &my_state.id) { + ( + Some(Id(Internal::Custom( + _, + ref old_name, + ))), + Some(Id(Internal::Custom( + _, + ref my_name, + ))), + ) => old_name == my_name, + ( + Some(Id(Internal::Set(a))), + Some(Id(Internal::Set(b))), + ) => a.len() == b.len(), + ( + Some(Id(Internal::Unique(_))), + Some(Id(Internal::Unique(_))), + ) => true, + (None, None) => true, + _ => false, + } + } { + continue; } - } { - continue; - } - mem::swap(my_state, &mut old); + mem::swap(my_state, &mut old); + } } + } else { + tag_match = false; } } else { - needs_reset = true; - } - } else if tag_match { - if let Some(id) = self.id.clone() { - borrowed.set_id(id); - } - if self.children.len() != borrowed.children().len() { - self.children = borrowed.children(); + if let Some(id) = self.id.clone() { + borrowed.set_id(id); + } + if self.children.len() != borrowed.children().len() { + self.children = borrowed.children(); + } } - } else { - needs_reset = true; } - if needs_reset { - *self = Self::new(borrowed); - let borrowed = new.borrow_mut(); + if tag_match { borrowed.diff(self); } else { + *self = Self::new(borrowed); + let borrowed = new.borrow_mut(); borrowed.diff(self); } } @@ -269,6 +278,7 @@ impl Tree { new_children.iter().map(|c| c.borrow().id()).collect(), |tree, widget| { let borrowed: &mut dyn Widget<_, _, _> = widget.borrow_mut(); + tree.diff(borrowed); }, |widget| { @@ -291,29 +301,26 @@ impl Tree { self.children.truncate(new_children.len()); } - let len_changed = self.children.len() != new_children.len(); - let children_len = self.children.len(); let (mut id_map, mut id_list): ( HashMap, - Vec<&mut Tree>, - ) = self.children.iter_mut().fold( + VecDeque<(usize, &mut Tree)>, + ) = self.children.iter_mut().enumerate().fold( (HashMap::new(), Vec::with_capacity(children_len)), - |(mut id_map, mut id_list), c| { + |(mut id_map, mut id_list), (i, c)| { if let Some(id) = c.id.as_ref() { if let Internal::Custom(_, ref name) = id.0 { let _ = id_map.insert(name.to_string(), c); } else { - id_list.push(c); + id_list.push((i, c)); } } else { - id_list.push(c); + id_list.push((i, c)); } (id_map, id_list) }, ); - let mut child_state_i = 0; let mut new_trees: Vec<(Tree, usize)> = Vec::with_capacity(new_children.len()); for (i, (new, new_id)) in @@ -327,14 +334,20 @@ impl Tree { } }) { c - } else if child_state_i < id_list.len() - && !matches!( - id_list[child_state_i].id, - Some(Id(Internal::Custom(_, _))) - ) - { - let c = &mut id_list[child_state_i]; - child_state_i += 1; + } else if let Some(i) = { + let mut found = None; + for c_i in 0..id_list.len() { + if id_list[c_i].0 == i { + found = Some(c_i); + break; + } + if i < c_i { + break; + } + } + found + } { + let c = id_list.remove(i).unwrap().1; c } else { let mut my_new_state = new_state(new); From 3297263977365a5d7e4c4cd9bbc13613b674763b Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 7 Nov 2024 17:00:12 -0500 Subject: [PATCH 08/18] fix --- core/src/widget/tree.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index f9edd0360d..c15e99ab6a 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -306,16 +306,16 @@ impl Tree { HashMap, VecDeque<(usize, &mut Tree)>, ) = self.children.iter_mut().enumerate().fold( - (HashMap::new(), Vec::with_capacity(children_len)), + (HashMap::new(), VecDeque::with_capacity(children_len)), |(mut id_map, mut id_list), (i, c)| { if let Some(id) = c.id.as_ref() { if let Internal::Custom(_, ref name) = id.0 { let _ = id_map.insert(name.to_string(), c); } else { - id_list.push((i, c)); + id_list.push_back((i, c)); } } else { - id_list.push((i, c)); + id_list.push_back((i, c)); } (id_map, id_list) }, From deb719e147ddcfee2c2cb52c9ca7a7b04fcd025a Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 11 Nov 2024 16:44:54 -0500 Subject: [PATCH 09/18] fix: a11y nodes --- widget/src/container.rs | 7 +------ widget/src/lazy/responsive.rs | 18 ++++++------------ widget/src/mouse_area.rs | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/widget/src/container.rs b/widget/src/container.rs index b693108084..dbc3e8fd82 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -394,13 +394,8 @@ where cursor: mouse::Cursor, ) -> iced_accessibility::A11yTree { let c_layout = layout.children().next().unwrap(); - let c_state = state.children.get(0); - self.content.as_widget().a11y_nodes( - c_layout, - c_state.unwrap_or(&Tree::empty()), - cursor, - ) + self.content.as_widget().a11y_nodes(c_layout, state, cursor) } fn drag_destinations( diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index e9ddf8c41a..bf139237ae 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -334,18 +334,12 @@ where tree: &Tree, cursor_position: mouse::Cursor, ) -> iced_accessibility::A11yTree { - use std::rc::Rc; - - let tree = tree.state.downcast_ref::>>>(); - if let Some(tree) = tree.borrow().as_ref() { - self.content.borrow().element.as_widget().a11y_nodes( - layout, - &tree.children[0], - cursor_position, - ) - } else { - iced_accessibility::A11yTree::default() - } + let state = tree.state.downcast_ref::().tree.borrow(); + self.content.borrow().element.as_widget().a11y_nodes( + layout, + &*state, + cursor_position, + ) } fn id(&self) -> Option { diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index c125a06c1c..09469028f9 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -355,6 +355,22 @@ where ); } } + + fn a11y_nodes( + &self, + layout: Layout<'_>, + state: &Tree, + cursor: mouse::Cursor, + ) -> iced_accessibility::A11yTree { + let c_state = state.children.get(0); + + let ret = self.content.as_widget().a11y_nodes( + layout, + c_state.unwrap_or(&Tree::empty()), + cursor, + ); + return ret; + } } impl<'a, Message, Theme, Renderer> From> From d0c2a0371b476866cb0f7045fba0215425493d62 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 12 Nov 2024 09:32:31 -0500 Subject: [PATCH 10/18] fix: feature gate a11y_nodes for mouse_area --- widget/src/mouse_area.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 09469028f9..544d394aba 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -356,6 +356,7 @@ where } } + #[cfg(feature = "a11y")] fn a11y_nodes( &self, layout: Layout<'_>, From ac897b9f697fc19192c6de442239cfa4e91ef8fa Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 13 Nov 2024 17:53:17 -0500 Subject: [PATCH 11/18] fix: close subscriptions when exiting --- winit/src/program.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/winit/src/program.rs b/winit/src/program.rs index a12ec20bc2..405f9ece4c 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -1096,7 +1096,7 @@ async fn run_instance<'a, P, C>( is_window_opening = false; } Event::UserEvent(action) => { - run_action( + let exited = run_action( action, &program, &mut compositor, @@ -1111,6 +1111,9 @@ async fn run_instance<'a, P, C>( &mut is_window_opening, &mut platform_specific_handler, ); + if exited { + runtime.track(None.into_iter()); + } actions += 1; } Event::NewEvents( @@ -1442,6 +1445,7 @@ async fn run_instance<'a, P, C>( && !is_window_opening && window_manager.is_empty() { + runtime.track(None.into_iter()); control_sender .start_send(Control::Exit) .expect("Send control action"); @@ -1469,7 +1473,7 @@ async fn run_instance<'a, P, C>( winit::event::WindowEvent::CloseRequested ) && window.exit_on_close_request { - run_action( + _ = run_action( Action::Window(runtime::window::Action::Close( id, )), @@ -1919,7 +1923,8 @@ fn run_action( ui_caches: &mut FxHashMap, is_window_opening: &mut bool, platform_specific: &mut crate::platform_specific::PlatformSpecific, -) where +) -> bool +where P: Program, C: Compositor + 'static, P::Theme: DefaultStyle, @@ -2257,6 +2262,7 @@ fn run_action( control_sender .start_send(Control::Exit) .expect("Send control action"); + return true; } Action::Dnd(a) => match a { iced_runtime::dnd::DndAction::RegisterDndDestination { @@ -2295,6 +2301,7 @@ fn run_action( platform_specific.send_action(a); } } + false } /// Build the user interface for every window. From 256863574bacfb1d2797c2a48cba7a3388cbeb59 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 13 Nov 2024 18:43:31 -0500 Subject: [PATCH 12/18] refactor: allow apps to request to be treated as a daemon --- src/program.rs | 1 + src/settings.rs | 5 +++++ winit/src/program.rs | 2 +- winit/src/settings.rs | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/program.rs b/src/program.rs index cb44eadb3f..464cc68bbc 100644 --- a/src/program.rs +++ b/src/program.rs @@ -191,6 +191,7 @@ pub trait Program: Sized { default_text_size: settings.default_text_size, antialiasing: settings.antialiasing, exit_on_close_request: settings.exit_on_close_request, + is_daemon: settings.exit_on_close_request, } .into(), renderer_settings, diff --git a/src/settings.rs b/src/settings.rs index 3b48ca1e31..77d0c0f4dd 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -35,6 +35,9 @@ pub struct Settings { /// If set to true the application will exit when the main window is closed. pub exit_on_close_request: bool, + + /// Whether the application is a daemon + pub is_daemon: bool, } impl Default for Settings { @@ -46,6 +49,7 @@ impl Default for Settings { default_text_size: Pixels(14.0), antialiasing: false, exit_on_close_request: false, + is_daemon: false, } } } @@ -56,6 +60,7 @@ impl From for iced_winit::Settings { iced_winit::Settings { id: settings.id, fonts: settings.fonts, + is_daemon: settings.is_daemon, } } } diff --git a/winit/src/program.rs b/winit/src/program.rs index 405f9ece4c..4ceb2d82da 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -193,7 +193,7 @@ where }; let (program, task) = runtime.enter(|| P::new(flags)); - let is_daemon = window_settings.is_none(); + let is_daemon = window_settings.is_none() || settings.is_daemon; let task = if let Some(window_settings) = window_settings { let mut task = Some(task); diff --git a/winit/src/settings.rs b/winit/src/settings.rs index 78368a04aa..8723c1f554 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -12,4 +12,7 @@ pub struct Settings { /// The fonts to load on boot. pub fonts: Vec>, + + /// Whether the application should exit when no windows are left + pub is_daemon: bool, } From d7b189d2aa2b4b3b2eabed7a796081a521aedacf Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 18 Nov 2024 15:25:43 -0800 Subject: [PATCH 13/18] Send `KeyboardEventVariant::Enter` on keyboard enter `cosmic-osd` expected this event, but it wasn't being sent. This isn't hard to fix, unless there's some non-obvious issues that this was commented to avoid (that is still applicable). --- .../wayland/handlers/seat/keyboard.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs b/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs index faba2fe2f1..2a37d7f8e6 100644 --- a/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs +++ b/winit/src/platform_specific/wayland/handlers/seat/keyboard.rs @@ -20,7 +20,7 @@ impl KeyboardHandler for SctkState { _keysyms: &[Keysym], ) { self.request_redraw(surface); - let (i, mut is_active, _seat) = { + let (i, mut is_active, seat) = { let (i, is_active, my_seat) = match self.seats.iter_mut().enumerate().find_map(|(i, s)| { if s.kbd.as_ref() == Some(keyboard) { @@ -55,11 +55,12 @@ impl KeyboardHandler for SctkState { id, winit::event::WindowEvent::Focused(true), )); - // self.sctk_events.push(SctkEvent::KeyboardEvent { - // variant: KeyboardEventVariant::Enter(surface.clone()), - // kbd_id: keyboard.clone(), - // seat_id: seat, - // }) + self.sctk_events.push(SctkEvent::KeyboardEvent { + variant: KeyboardEventVariant::Enter(surface.clone()), + kbd_id: keyboard.clone(), + seat_id: seat, + surface: surface.clone(), + }); } } From 501d7aaebe113a785f53e3f139e48be8a6dd4d1a Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 19 Nov 2024 12:58:25 -0500 Subject: [PATCH 14/18] fix: request logical size when autosizing --- winit/src/program.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/winit/src/program.rs b/winit/src/program.rs index 4ceb2d82da..6d512c3d77 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -5,6 +5,7 @@ mod state; mod window_manager; pub use runtime::{default, Appearance, DefaultStyle}; +use winit::dpi::PhysicalSize; use winit::event_loop::OwnedDisplayHandle; use crate::conversion; @@ -1557,15 +1558,12 @@ async fn run_instance<'a, P, C>( if let Some(requested_size) = clipboard.requested_logical_size.lock().unwrap().take() { - let requested_physical_size = - winit::dpi::PhysicalSize::new( - (requested_size.width as f64 - * window.state.scale_factor()) - .ceil() as u32, - (requested_size.height as f64 - * window.state.scale_factor()) - .ceil() as u32, + let requested_physical_size: PhysicalSize = + winit::dpi::PhysicalSize::from_logical( + requested_size.cast::(), + window.state.scale_factor(), ); + let physical_size = window.state.physical_size(); if requested_physical_size.width != physical_size.width || requested_physical_size.height @@ -1576,8 +1574,8 @@ async fn run_instance<'a, P, C>( window.resize_enabled = true; resized = true; needs_redraw = true; - let s = winit::dpi::Size::Physical( - requested_physical_size, + let s = winit::dpi::Size::Logical( + requested_size.cast(), ); _ = window.raw.request_surface_size(s); window.raw.set_min_surface_size(Some(s)); From 9f71c81dc6e18e07b70161ab51f59dfecfb89813 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 20 Nov 2024 18:08:33 -0500 Subject: [PATCH 15/18] fix: rebuild a11y tree when rebuilding widget tree --- winit/src/program.rs | 178 ++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 95 deletions(-) diff --git a/winit/src/program.rs b/winit/src/program.rs index 6d512c3d77..8324cb1f05 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -1235,104 +1235,11 @@ async fn run_instance<'a, P, C>( { let logical_size = window.state.logical_size(); debug.layout_started(); - let mut ui = user_interfaces + let ui = user_interfaces .remove(&id) .expect("Remove user interface") .relayout(logical_size, &mut window.renderer); - #[cfg(feature = "a11y")] - { - use iced_accessibility::{ - accesskit::{ - NodeBuilder, NodeId, Role, Tree, - TreeUpdate, - }, - A11yId, A11yNode, A11yTree, - }; - if let Some(Some((a11y_id, adapter))) = - a11y_enabled.then(|| adapters.get_mut(&id)) - { - // TODO cleanup duplication - let child_tree = - ui.a11y_nodes(window.state.cursor()); - let mut root = - NodeBuilder::new(Role::Window); - root.set_name( - window.state.title.to_string(), - ); - let window_tree = - A11yTree::node_with_child_tree( - A11yNode::new(root, *a11y_id), - child_tree, - ); - let tree = Tree::new(NodeId(*a11y_id)); - - let focus = - Arc::new(std::sync::Mutex::new(None)); - let focus_clone = focus.clone(); - let operation: Box> = - Box::new(operation::map( - Box::new( - operation::focusable::find_focused( - ), - ), - move |id| { - let mut guard = focus.lock().unwrap(); - _ = guard.replace(id); - }, - )); - let mut current_operation = Some(operation); - - while let Some(mut operation) = - current_operation.take() - { - ui.operate( - &window.renderer, - operation.as_mut(), - ); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(()) => { - break; - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); - } - } - } - let mut guard = focus_clone.lock().unwrap(); - let focus = guard - .take() - .map(|id| A11yId::Widget(id)); - tracing::debug!( - "focus: {:?}\ntree root: {:?}\n children: {:?}", - &focus, - window_tree - .root() - .iter() - .map(|n| (n.node().role(), n.id())) - .collect::>(), - window_tree - .children() - .iter() - .map(|n| (n.node().role(), n.id())) - .collect::>() - ); - let focus = focus - .filter(|f_id| { - window_tree.contains(f_id) - }) - .map(|id| id.into()) - .unwrap_or_else(|| tree.root); - adapter.update_if_active(|| TreeUpdate { - nodes: window_tree.into(), - tree: Some(tree), - focus, - }); - } - } - let _ = user_interfaces.insert(id, ui); debug.layout_finished(); @@ -1665,6 +1572,10 @@ async fn run_instance<'a, P, C>( &mut window_manager, cached_interfaces, &mut clipboard, + #[cfg(feature = "a11y")] + a11y_enabled, + #[cfg(feature = "a11y")] + &mut adapters, )); if actions > 0 { @@ -2309,6 +2220,11 @@ pub fn build_user_interfaces<'a, P: Program, C>( window_manager: &mut WindowManager, mut cached_user_interfaces: FxHashMap, clipboard: &mut Clipboard, + #[cfg(feature = "a11y")] a11y_enabled: bool, + #[cfg(feature = "a11y")] adapters: &mut HashMap< + window::Id, + (u64, iced_accessibility::accesskit_winit::Adapter), + >, ) -> FxHashMap> where C: Compositor, @@ -2318,7 +2234,7 @@ where .drain() .filter_map(|(id, cache)| { let window = window_manager.get_mut(id)?; - let interface = build_user_interface( + let mut interface = build_user_interface( program, cache, &mut window.renderer, @@ -2329,6 +2245,78 @@ where window.prev_dnd_destination_rectangles_count, clipboard, ); + #[cfg(feature = "a11y")] + { + use iced_accessibility::{ + accesskit::{NodeBuilder, NodeId, Role, Tree, TreeUpdate}, + A11yId, A11yNode, A11yTree, + }; + if let Some(Some((a11y_id, adapter))) = + a11y_enabled.then(|| adapters.get_mut(&id)) + { + // TODO cleanup duplication + let child_tree = + interface.a11y_nodes(window.state.cursor()); + let mut root = NodeBuilder::new(Role::Window); + root.set_name(window.state.title.to_string()); + let window_tree = A11yTree::node_with_child_tree( + A11yNode::new(root, *a11y_id), + child_tree, + ); + let tree = Tree::new(NodeId(*a11y_id)); + + let focus = Arc::new(std::sync::Mutex::new(None)); + let focus_clone = focus.clone(); + let operation: Box> = + Box::new(operation::map( + Box::new(operation::focusable::find_focused()), + move |id| { + let mut guard = focus.lock().unwrap(); + _ = guard.replace(id); + }, + )); + let mut current_operation = Some(operation); + + while let Some(mut operation) = current_operation.take() { + interface.operate(&window.renderer, operation.as_mut()); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(()) => { + break; + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } + } + } + let mut guard = focus_clone.lock().unwrap(); + let focus = guard.take().map(|id| A11yId::Widget(id)); + tracing::debug!( + "focus: {:?}\ntree root: {:?}\n children: {:?}", + &focus, + window_tree + .root() + .iter() + .map(|n| (n.node().role(), n.id())) + .collect::>(), + window_tree + .children() + .iter() + .map(|n| (n.node().role(), n.id())) + .collect::>() + ); + let focus = focus + .filter(|f_id| window_tree.contains(f_id)) + .map(|id| id.into()) + .unwrap_or_else(|| tree.root); + adapter.update_if_active(|| TreeUpdate { + nodes: window_tree.into(), + tree: Some(tree), + focus, + }); + } + } let dnd_rectangles = interface.dnd_rectangles( window.prev_dnd_destination_rectangles_count, From eb432c94fdc4b357a3bc679c37e75965190332e2 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 21 Nov 2024 20:16:53 -0500 Subject: [PATCH 16/18] Revert "fix: rebuild a11y tree when rebuilding widget tree" This reverts commit c9bad732043282e9a43d7cba2608c444f4f2f253. --- winit/src/program.rs | 178 +++++++++++++++++++++++-------------------- 1 file changed, 95 insertions(+), 83 deletions(-) diff --git a/winit/src/program.rs b/winit/src/program.rs index 8324cb1f05..6d512c3d77 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -1235,11 +1235,104 @@ async fn run_instance<'a, P, C>( { let logical_size = window.state.logical_size(); debug.layout_started(); - let ui = user_interfaces + let mut ui = user_interfaces .remove(&id) .expect("Remove user interface") .relayout(logical_size, &mut window.renderer); + #[cfg(feature = "a11y")] + { + use iced_accessibility::{ + accesskit::{ + NodeBuilder, NodeId, Role, Tree, + TreeUpdate, + }, + A11yId, A11yNode, A11yTree, + }; + if let Some(Some((a11y_id, adapter))) = + a11y_enabled.then(|| adapters.get_mut(&id)) + { + // TODO cleanup duplication + let child_tree = + ui.a11y_nodes(window.state.cursor()); + let mut root = + NodeBuilder::new(Role::Window); + root.set_name( + window.state.title.to_string(), + ); + let window_tree = + A11yTree::node_with_child_tree( + A11yNode::new(root, *a11y_id), + child_tree, + ); + let tree = Tree::new(NodeId(*a11y_id)); + + let focus = + Arc::new(std::sync::Mutex::new(None)); + let focus_clone = focus.clone(); + let operation: Box> = + Box::new(operation::map( + Box::new( + operation::focusable::find_focused( + ), + ), + move |id| { + let mut guard = focus.lock().unwrap(); + _ = guard.replace(id); + }, + )); + let mut current_operation = Some(operation); + + while let Some(mut operation) = + current_operation.take() + { + ui.operate( + &window.renderer, + operation.as_mut(), + ); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(()) => { + break; + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } + } + } + let mut guard = focus_clone.lock().unwrap(); + let focus = guard + .take() + .map(|id| A11yId::Widget(id)); + tracing::debug!( + "focus: {:?}\ntree root: {:?}\n children: {:?}", + &focus, + window_tree + .root() + .iter() + .map(|n| (n.node().role(), n.id())) + .collect::>(), + window_tree + .children() + .iter() + .map(|n| (n.node().role(), n.id())) + .collect::>() + ); + let focus = focus + .filter(|f_id| { + window_tree.contains(f_id) + }) + .map(|id| id.into()) + .unwrap_or_else(|| tree.root); + adapter.update_if_active(|| TreeUpdate { + nodes: window_tree.into(), + tree: Some(tree), + focus, + }); + } + } + let _ = user_interfaces.insert(id, ui); debug.layout_finished(); @@ -1572,10 +1665,6 @@ async fn run_instance<'a, P, C>( &mut window_manager, cached_interfaces, &mut clipboard, - #[cfg(feature = "a11y")] - a11y_enabled, - #[cfg(feature = "a11y")] - &mut adapters, )); if actions > 0 { @@ -2220,11 +2309,6 @@ pub fn build_user_interfaces<'a, P: Program, C>( window_manager: &mut WindowManager, mut cached_user_interfaces: FxHashMap, clipboard: &mut Clipboard, - #[cfg(feature = "a11y")] a11y_enabled: bool, - #[cfg(feature = "a11y")] adapters: &mut HashMap< - window::Id, - (u64, iced_accessibility::accesskit_winit::Adapter), - >, ) -> FxHashMap> where C: Compositor, @@ -2234,7 +2318,7 @@ where .drain() .filter_map(|(id, cache)| { let window = window_manager.get_mut(id)?; - let mut interface = build_user_interface( + let interface = build_user_interface( program, cache, &mut window.renderer, @@ -2245,78 +2329,6 @@ where window.prev_dnd_destination_rectangles_count, clipboard, ); - #[cfg(feature = "a11y")] - { - use iced_accessibility::{ - accesskit::{NodeBuilder, NodeId, Role, Tree, TreeUpdate}, - A11yId, A11yNode, A11yTree, - }; - if let Some(Some((a11y_id, adapter))) = - a11y_enabled.then(|| adapters.get_mut(&id)) - { - // TODO cleanup duplication - let child_tree = - interface.a11y_nodes(window.state.cursor()); - let mut root = NodeBuilder::new(Role::Window); - root.set_name(window.state.title.to_string()); - let window_tree = A11yTree::node_with_child_tree( - A11yNode::new(root, *a11y_id), - child_tree, - ); - let tree = Tree::new(NodeId(*a11y_id)); - - let focus = Arc::new(std::sync::Mutex::new(None)); - let focus_clone = focus.clone(); - let operation: Box> = - Box::new(operation::map( - Box::new(operation::focusable::find_focused()), - move |id| { - let mut guard = focus.lock().unwrap(); - _ = guard.replace(id); - }, - )); - let mut current_operation = Some(operation); - - while let Some(mut operation) = current_operation.take() { - interface.operate(&window.renderer, operation.as_mut()); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(()) => { - break; - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); - } - } - } - let mut guard = focus_clone.lock().unwrap(); - let focus = guard.take().map(|id| A11yId::Widget(id)); - tracing::debug!( - "focus: {:?}\ntree root: {:?}\n children: {:?}", - &focus, - window_tree - .root() - .iter() - .map(|n| (n.node().role(), n.id())) - .collect::>(), - window_tree - .children() - .iter() - .map(|n| (n.node().role(), n.id())) - .collect::>() - ); - let focus = focus - .filter(|f_id| window_tree.contains(f_id)) - .map(|id| id.into()) - .unwrap_or_else(|| tree.root); - adapter.update_if_active(|| TreeUpdate { - nodes: window_tree.into(), - tree: Some(tree), - focus, - }); - } - } let dnd_rectangles = interface.dnd_rectangles( window.prev_dnd_destination_rectangles_count, From 630612a7b1cb4fb236edabb2dee88b1754976549 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 21 Nov 2024 20:19:19 -0500 Subject: [PATCH 17/18] fix: send a11y tree after actions this ensures focus is updated correctly --- winit/src/program.rs | 192 ++++++++++++++++++++++--------------------- 1 file changed, 99 insertions(+), 93 deletions(-) diff --git a/winit/src/program.rs b/winit/src/program.rs index 6d512c3d77..fd61f88f18 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -802,6 +802,7 @@ async fn run_instance<'a, P, C>( let Some(event) = event else { break; }; + let mut rebuild_a11y_tree = false; match event { Event::StartDnd => { @@ -1097,6 +1098,7 @@ async fn run_instance<'a, P, C>( is_window_opening = false; } Event::UserEvent(action) => { + rebuild_a11y_tree = true; let exited = run_action( action, &program, @@ -1130,6 +1132,18 @@ async fn run_instance<'a, P, C>( } } Event::Winit(window_id, event) => { + #[cfg(feature = "a11y")] + { + if let Some((id, window)) = + window_manager.get_mut_alias(window_id) + { + if let Some(Some((_, adapter))) = + a11y_enabled.then(|| adapters.get_mut(&id)) + { + adapter.process_event(window.raw.as_ref(), &event); + }; + } + } match event { event::WindowEvent::RedrawRequested => { let Some((id, window)) = @@ -1240,99 +1254,6 @@ async fn run_instance<'a, P, C>( .expect("Remove user interface") .relayout(logical_size, &mut window.renderer); - #[cfg(feature = "a11y")] - { - use iced_accessibility::{ - accesskit::{ - NodeBuilder, NodeId, Role, Tree, - TreeUpdate, - }, - A11yId, A11yNode, A11yTree, - }; - if let Some(Some((a11y_id, adapter))) = - a11y_enabled.then(|| adapters.get_mut(&id)) - { - // TODO cleanup duplication - let child_tree = - ui.a11y_nodes(window.state.cursor()); - let mut root = - NodeBuilder::new(Role::Window); - root.set_name( - window.state.title.to_string(), - ); - let window_tree = - A11yTree::node_with_child_tree( - A11yNode::new(root, *a11y_id), - child_tree, - ); - let tree = Tree::new(NodeId(*a11y_id)); - - let focus = - Arc::new(std::sync::Mutex::new(None)); - let focus_clone = focus.clone(); - let operation: Box> = - Box::new(operation::map( - Box::new( - operation::focusable::find_focused( - ), - ), - move |id| { - let mut guard = focus.lock().unwrap(); - _ = guard.replace(id); - }, - )); - let mut current_operation = Some(operation); - - while let Some(mut operation) = - current_operation.take() - { - ui.operate( - &window.renderer, - operation.as_mut(), - ); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(()) => { - break; - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); - } - } - } - let mut guard = focus_clone.lock().unwrap(); - let focus = guard - .take() - .map(|id| A11yId::Widget(id)); - tracing::debug!( - "focus: {:?}\ntree root: {:?}\n children: {:?}", - &focus, - window_tree - .root() - .iter() - .map(|n| (n.node().role(), n.id())) - .collect::>(), - window_tree - .children() - .iter() - .map(|n| (n.node().role(), n.id())) - .collect::>() - ); - let focus = focus - .filter(|f_id| { - window_tree.contains(f_id) - }) - .map(|id| id.into()) - .unwrap_or_else(|| tree.root); - adapter.update_if_active(|| TreeUpdate { - nodes: window_tree.into(), - tree: Some(tree), - focus, - }); - } - } - let _ = user_interfaces.insert(id, ui); debug.layout_finished(); @@ -1658,6 +1579,7 @@ async fn run_instance<'a, P, C>( window.request_redraw(); } + rebuild_a11y_tree = true; user_interfaces = ManuallyDrop::new(build_user_interfaces( &program, @@ -1807,6 +1729,7 @@ async fn run_instance<'a, P, C>( } #[cfg(feature = "a11y")] Event::Accessibility(id, e) => { + rebuild_a11y_tree = true; match e.action { iced_accessibility::accesskit::Action::Focus => { // TODO send a command for this @@ -1838,6 +1761,89 @@ async fn run_instance<'a, P, C>( // log ignored events? } } + #[cfg(feature = "a11y")] + { + use iced_accessibility::{ + accesskit::{NodeBuilder, NodeId, Role, Tree, TreeUpdate}, + A11yId, A11yNode, A11yTree, + }; + if !a11y_enabled || !rebuild_a11y_tree { + continue; + } + + for id in window_manager.ids() { + let Some((a11y_id, adapter)) = adapters.get_mut(&id) else { + continue; + }; + let Some(window) = window_manager.get(id) else { + continue; + }; + let interface = + user_interfaces.get_mut(&id).expect("Get user interface"); + + // TODO cleanup duplication + let child_tree = interface.a11y_nodes(window.state.cursor()); + let mut root = NodeBuilder::new(Role::Window); + root.set_name(window.state.title.to_string()); + let window_tree = A11yTree::node_with_child_tree( + A11yNode::new(root, *a11y_id), + child_tree, + ); + let tree = Tree::new(NodeId(*a11y_id)); + + let focus = Arc::new(std::sync::Mutex::new(None)); + let focus_clone = focus.clone(); + let operation: Box> = + Box::new(operation::map( + Box::new(operation::focusable::find_focused()), + move |id| { + let mut guard = focus.lock().unwrap(); + _ = guard.replace(id); + }, + )); + let mut current_operation = Some(operation); + + while let Some(mut operation) = current_operation.take() { + interface.operate(&window.renderer, operation.as_mut()); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(()) => { + break; + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } + } + } + let mut guard = focus_clone.lock().unwrap(); + let focus = guard + .take() + .map(|id| A11yId::Widget(id)) + .filter(|f_id| window_tree.contains(f_id)); + tracing::debug!( + "tree root: {:?}\nchildren: {:?}\nfocus: {:?}\n", + window_tree + .root() + .iter() + .map(|n| (n.node(), n.id())) + .collect::>(), + window_tree + .children() + .iter() + .map(|n| (n.node(), n.id())) + .collect::>(), + &focus, + ); + let focus = + focus.map(|id| id.into()).unwrap_or_else(|| tree.root); + adapter.update_if_active(|| TreeUpdate { + nodes: window_tree.into(), + tree: Some(tree), + focus, + }); + } + } } let _ = ManuallyDrop::into_inner(user_interfaces); From aee94ca1ffa74c65c4e0e936bc850d1ff4d97e49 Mon Sep 17 00:00:00 2001 From: Matt George Date: Sun, 27 Oct 2024 13:45:57 -0600 Subject: [PATCH 18/18] fix: correct event types for windows --- winit/src/program.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/winit/src/program.rs b/winit/src/program.rs index fd61f88f18..933c643e0b 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -315,7 +315,7 @@ where #[cfg(target_os = "windows")] let is_move_or_resize = matches!( event, - winit::event::WindowEvent::Resized(_) + winit::event::WindowEvent::SurfaceResized(_) | winit::event::WindowEvent::Moved(_) ); @@ -331,12 +331,7 @@ where #[cfg(target_os = "windows")] { if is_move_or_resize { - self.process_event( - event_loop, - Event::EventLoopAwakened( - winit::event::Event::AboutToWait, - ), - ); + self.process_event(event_loop, Some(Event::AboutToWait)); } } }