refactor: track focus chain

This commit is contained in:
Ashley Wulber 2025-07-16 13:48:08 -04:00 committed by Michael Murphy
parent 50367b96e3
commit 0943f131c2
4 changed files with 73 additions and 14 deletions

View file

@ -10,7 +10,6 @@ use std::{
io::Write,
path::{Path, PathBuf},
sync::Mutex,
time::Duration,
};
#[cfg(feature = "subscription")]

View file

@ -17,6 +17,8 @@ use iced::Application as IcedApplication;
use iced::event::wayland;
use iced::{Task, window};
use iced_futures::event::listen_with;
#[cfg(feature = "wayland")]
use iced_winit::SurfaceIdWrapper;
use palette::color_difference::EuclideanDistance;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
@ -84,7 +86,11 @@ pub struct Cosmic<App: Application> {
#[cfg(feature = "wayland")]
pub surface_views: HashMap<
window::Id,
(
Option<window::Id>,
SurfaceIdWrapper,
Box<dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>>>,
),
>,
}
@ -449,7 +455,7 @@ where
#[cfg(feature = "multi-window")]
pub fn view(&self, id: window::Id) -> Element<crate::Action<T::Message>> {
#[cfg(feature = "wayland")]
if let Some(v) = self.surface_views.get(&id) {
if let Some((_, _, v)) = self.surface_views.get(&id) {
return v(&self.app);
}
if self
@ -841,13 +847,45 @@ impl<T: Application> Cosmic<T> {
}
Action::Focus(f) => {
self.app.core_mut().focused_window = Some(f);
#[cfg(all(
feature = "wayland",
feature = "multi-window",
feature = "surface-message"
))]
if let Some((
parent,
SurfaceIdWrapper::Subsurface(_) | SurfaceIdWrapper::Popup(_),
_,
)) = self.surface_views.get(&f)
{
// If the parent is already focused, push the new focus
// to the end of the focus chain.
if parent.is_some_and(|p| self.app.core().focused_window.last() == Some(&p)) {
self.app.core_mut().focused_window.push(f);
return iced::Task::none();
} else {
// set the whole parent chain to the focus chain
let mut parent_chain = vec![f];
let mut cur = *parent;
while let Some(p) = cur {
parent_chain.push(p);
cur = self
.surface_views
.get(&p)
.and_then(|(parent, _, _)| *parent);
}
parent_chain.reverse();
self.app.core_mut().focused_window = parent_chain;
return iced::Task::none();
}
}
self.app.core_mut().focused_window = vec![f];
}
Action::Unfocus(id) => {
let core = self.app.core_mut();
if core.focused_window.as_ref().is_some_and(|cur| *cur == id) {
core.focused_window = None;
if core.focused_window().as_ref().is_some_and(|cur| *cur == id) {
core.focused_window.pop();
}
}
#[cfg(feature = "applet")]
@ -886,7 +924,14 @@ impl<App: Application> Cosmic<App> {
) -> Task<crate::Action<App::Message>> {
use iced_winit::commands::subsurface::get_subsurface;
self.surface_views.insert(settings.id, view);
self.surface_views.insert(
settings.id,
(
Some(settings.parent),
SurfaceIdWrapper::Subsurface(settings.id),
view,
),
);
get_subsurface(settings)
}
@ -901,7 +946,14 @@ impl<App: Application> Cosmic<App> {
) -> Task<crate::Action<App::Message>> {
use iced_winit::commands::popup::get_popup;
self.surface_views.insert(settings.id, view);
self.surface_views.insert(
settings.id,
(
Some(settings.parent),
SurfaceIdWrapper::Popup(settings.id),
view,
),
);
get_popup(settings)
}
}

View file

@ -548,8 +548,9 @@ impl<App: Application> ApplicationExt for App {
let show_context = core.window.show_context;
let nav_bar_active = core.nav_bar_active();
let focused = core
.focused_window()
.is_some_and(|i| Some(i) == self.core().main_window_id());
.focus_chain()
.iter()
.any(|i| Some(*i) == self.core().main_window_id());
let border_padding = if sharp_corners { 8 } else { 7 };

View file

@ -64,7 +64,7 @@ pub struct Core {
scale_factor: f32,
/// Window focus state
pub(super) focused_window: Option<window::Id>,
pub(super) focused_window: Vec<window::Id>,
pub(super) theme_sub_counter: u64,
/// Last known system theme
@ -141,7 +141,7 @@ impl Default for Core {
height: 0.,
width: 0.,
},
focused_window: None,
focused_window: Vec::new(),
#[cfg(feature = "applet")]
applet: crate::applet::Context::default(),
#[cfg(feature = "single-instance")]
@ -384,8 +384,15 @@ impl Core {
/// Get the current focused window if it exists
#[must_use]
#[inline]
pub const fn focused_window(&self) -> Option<window::Id> {
self.focused_window
pub fn focused_window(&self) -> Option<window::Id> {
self.focused_window.last().copied()
}
/// Get the current focus chain of windows
#[must_use]
#[inline]
pub fn focus_chain(&self) -> &[window::Id] {
&self.focused_window
}
/// Whether the application should use a dark theme, according to the system