Merge pull request #1389 from pop-os/subwindow-modifiers

Track and use modifiers per sub-window, fixes #1152
This commit is contained in:
Jeremy Soller 2025-11-21 14:18:45 -07:00 committed by GitHub
commit 83bdc4d073
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -661,6 +661,20 @@ impl PartialEq for WatcherWrapper {
} }
} }
struct Window {
kind: WindowKind,
modifiers: Modifiers,
}
impl Window {
fn new(kind: WindowKind) -> Self {
Self {
kind,
modifiers: Modifiers::empty(),
}
}
}
// The [`App`] stores application-specific state. // The [`App`] stores application-specific state.
pub struct App { pub struct App {
core: Core, core: Core,
@ -709,7 +723,7 @@ pub struct App {
Debouncer<RecommendedWatcher, RecommendedCache>, Debouncer<RecommendedWatcher, RecommendedCache>,
FxHashSet<PathBuf>, FxHashSet<PathBuf>,
)>, )>,
windows: FxHashMap<window::Id, WindowKind>, windows: FxHashMap<window::Id, Window>,
nav_dnd_hover: Option<(Location, Instant)>, nav_dnd_hover: Option<(Location, Instant)>,
tab_dnd_hover: Option<(Entity, Instant)>, tab_dnd_hover: Option<(Entity, Instant)>,
nav_drag_id: DragId, nav_drag_id: DragId,
@ -935,9 +949,9 @@ impl App {
dialog.set_accept_label(fl!("extract-here")); dialog.set_accept_label(fl!("extract-here"));
self.windows.insert( self.windows.insert(
dialog.window_id(), dialog.window_id(),
WindowKind::FileDialog(Some( Window::new(WindowKind::FileDialog(Some(
paths.iter().map(|x| x.as_ref().to_path_buf()).collect(), paths.iter().map(|x| x.as_ref().to_path_buf()).collect(),
)), ))),
); );
self.file_dialog_opt = Some(dialog); self.file_dialog_opt = Some(dialog);
Task::batch([set_title_task, dialog_task]) Task::batch([set_title_task, dialog_task])
@ -1178,8 +1192,8 @@ impl App {
} }
fn remove_window(&mut self, id: &window::Id) { fn remove_window(&mut self, id: &window::Id) {
if let Some(window_kind) = self.windows.remove(id) { if let Some(window) = self.windows.remove(id) {
match window_kind { match window.kind {
WindowKind::ContextMenu(entity, _) => { WindowKind::ContextMenu(entity, _) => {
// Close context menu // Close context menu
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) { if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
@ -2704,7 +2718,8 @@ impl Application for App {
} }
let (id, command) = window::open(settings); let (id, command) = window::open(settings);
self.windows.insert(id, WindowKind::DesktopViewOptions); self.windows
.insert(id, Window::new(WindowKind::DesktopViewOptions));
return command.map(|_id| cosmic::action::none()); return command.map(|_id| cosmic::action::none());
} }
Message::DesktopDialogs(show) => { Message::DesktopDialogs(show) => {
@ -2732,14 +2747,14 @@ impl Application for App {
let (id, command) = window::open(settings); let (id, command) = window::open(settings);
self.windows self.windows
.insert(id, WindowKind::Dialogs(widget::Id::unique())); .insert(id, Window::new(WindowKind::Dialogs(widget::Id::unique())));
return command.map(|_id| cosmic::Action::None); return command.map(|_id| cosmic::Action::None);
} }
let tasks = self let tasks = self
.windows .windows
.iter() .iter()
.filter(|(_, kind)| matches!(*kind, WindowKind::Dialogs(_))) .filter(|(_, window)| matches!(window.kind, WindowKind::Dialogs(_)))
.map(|(id, _)| window::close(*id)); .map(|(id, _)| window::close(*id));
return Task::batch(tasks); return Task::batch(tasks);
} }
@ -2929,9 +2944,10 @@ impl Application for App {
DialogResult::Open(selected_paths) => { DialogResult::Open(selected_paths) => {
let mut archive_paths = None; let mut archive_paths = None;
if let Some(file_dialog) = &self.file_dialog_opt { if let Some(file_dialog) = &self.file_dialog_opt {
let window = self.windows.remove(&file_dialog.window_id()); if let Some(window) = self.windows.remove(&file_dialog.window_id()) {
if let Some(WindowKind::FileDialog(paths)) = window { if let WindowKind::FileDialog(paths) = window.kind {
archive_paths = paths; archive_paths = paths;
}
} }
} }
if let Some(archive_paths) = archive_paths { if let Some(archive_paths) = archive_paths {
@ -3021,6 +3037,9 @@ impl Application for App {
if self.core.main_window_id() == Some(window_id) || in_surface_ids { if self.core.main_window_id() == Some(window_id) || in_surface_ids {
self.modifiers = modifiers; self.modifiers = modifiers;
} }
if let Some(window) = self.windows.get_mut(&window_id) {
window.modifiers = modifiers;
}
} }
Message::MounterItems(mounter_key, mounter_items) => { Message::MounterItems(mounter_key, mounter_items) => {
// Check for unmounted folders // Check for unmounted folders
@ -3612,10 +3631,10 @@ impl Application for App {
let (id, command) = window::open(settings); let (id, command) = window::open(settings);
self.windows.insert( self.windows.insert(
id, id,
WindowKind::Preview( Window::new(WindowKind::Preview(
entity_opt, entity_opt,
PreviewKind::Location(Location::Path(path)), PreviewKind::Location(Location::Path(path)),
), )),
); );
commands.push(command.map(|_id| cosmic::action::none())); commands.push(command.map(|_id| cosmic::action::none()));
} }
@ -3912,7 +3931,10 @@ impl Application for App {
let window_id = WindowId::unique(); let window_id = WindowId::unique();
self.windows.insert( self.windows.insert(
window_id, window_id,
WindowKind::ContextMenu(entity, widget::Id::unique()), Window::new(WindowKind::ContextMenu(
entity,
widget::Id::unique(),
)),
); );
commands.push(self.update(Message::Surface( commands.push(self.update(Message::Surface(
cosmic::surface::action::app_popup( cosmic::surface::action::app_popup(
@ -3952,8 +3974,8 @@ impl Application for App {
} else { } else {
// Destroy previous popup // Destroy previous popup
let mut window_ids = Vec::new(); let mut window_ids = Vec::new();
for (window_id, window_kind) in &self.windows { for (window_id, window) in &self.windows {
if let WindowKind::ContextMenu(e, _) = window_kind { if let WindowKind::ContextMenu(e, _) = &window.kind {
if *e == entity { if *e == entity {
window_ids.push(*window_id); window_ids.push(*window_id);
} }
@ -4565,7 +4587,8 @@ impl Application for App {
widget::Id::unique(), widget::Id::unique(),
Some(surface_id), Some(surface_id),
); );
self.windows.insert(surface_id, WindowKind::Desktop(entity)); self.windows
.insert(surface_id, Window::new(WindowKind::Desktop(entity)));
return Task::batch([ return Task::batch([
command, command,
get_layer_surface(SctkLayerSurfaceSettings { get_layer_surface(SctkLayerSurfaceSettings {
@ -4666,7 +4689,7 @@ impl Application for App {
#[cfg(all(feature = "wayland", feature = "desktop-applet"))] #[cfg(all(feature = "wayland", feature = "desktop-applet"))]
Message::Focused(id) => { Message::Focused(id) => {
if let Some(w) = self.windows.get(&id) { if let Some(w) = self.windows.get(&id) {
match w { match &w.kind {
WindowKind::Desktop(entity) => self.tab_model.activate(*entity), WindowKind::Desktop(entity) => self.tab_model.activate(*entity),
_ => {} _ => {}
}; };
@ -5696,66 +5719,66 @@ impl Application for App {
fn view_window(&self, id: WindowId) -> Element<'_, Self::Message> { fn view_window(&self, id: WindowId) -> Element<'_, Self::Message> {
let content = match self.windows.get(&id) { let content = match self.windows.get(&id) {
Some(WindowKind::ContextMenu(entity, id)) => { Some(window) => match &window.kind {
match self.tab_model.data::<Tab>(*entity) { WindowKind::ContextMenu(entity, id) => match self.tab_model.data::<Tab>(*entity) {
Some(tab) => { Some(tab) => {
return widget::autosize::autosize( return widget::autosize::autosize(
menu::context_menu(tab, &self.key_binds, &self.modifiers) menu::context_menu(tab, &self.key_binds, &window.modifiers)
.map(|x| Message::TabMessage(Some(*entity), x)), .map(|x| Message::TabMessage(Some(*entity), x)),
id.clone(), id.clone(),
) )
.into(); .into();
} }
None => widget::text("Unknown tab ID").into(), None => widget::text("Unknown tab ID").into(),
} },
} WindowKind::Desktop(entity) => {
Some(WindowKind::Desktop(entity)) => { let mut tab_column = widget::column::with_capacity(3);
let mut tab_column = widget::column::with_capacity(3);
let tab_view = match self.tab_model.data::<Tab>(*entity) { let tab_view = match self.tab_model.data::<Tab>(*entity) {
Some(tab) => tab Some(tab) => tab
.view(&self.key_binds, &self.modifiers) .view(&self.key_binds, &window.modifiers)
.map(move |message| Message::TabMessage(Some(*entity), message)), .map(move |message| Message::TabMessage(Some(*entity), message)),
None => widget::vertical_space().into(), None => widget::vertical_space().into(),
}; };
tab_column = tab_column.push(tab_view); tab_column = tab_column.push(tab_view);
// The toaster is added on top of an empty element to ensure that it does not override context menus // The toaster is added on top of an empty element to ensure that it does not override context menus
tab_column = tab_column =
tab_column.push(widget::toaster(&self.toasts, widget::horizontal_space())); tab_column.push(widget::toaster(&self.toasts, widget::horizontal_space()));
return if let Some(margin) = self.margin.get(&id) { return if let Some(margin) = self.margin.get(&id) {
if margin.0 >= 0. || margin.2 >= 0. { if margin.0 >= 0. || margin.2 >= 0. {
tab_column = widget::column::with_children([ tab_column = widget::column::with_children([
vertical_space().height(margin.0).into(), vertical_space().height(margin.0).into(),
tab_column.into(), tab_column.into(),
vertical_space().height(margin.2).into(), vertical_space().height(margin.2).into(),
]); ]);
} }
if margin.1 >= 0. || margin.3 >= 0. { if margin.1 >= 0. || margin.3 >= 0. {
Element::from(widget::row::with_children([ Element::from(widget::row::with_children([
horizontal_space().width(margin.1).into(), horizontal_space().width(margin.1).into(),
tab_column.into(), tab_column.into(),
horizontal_space().width(margin.3).into(), horizontal_space().width(margin.3).into(),
])) ]))
} else {
tab_column.into()
}
} else { } else {
tab_column.into() tab_column.into()
} };
} else { }
tab_column.into() WindowKind::DesktopViewOptions => self.desktop_view_options(),
}; WindowKind::Dialogs(id) => match self.dialog() {
} Some(element) => return widget::autosize::autosize(element, id.clone()).into(),
Some(WindowKind::DesktopViewOptions) => self.desktop_view_options(), None => widget::horizontal_space().into(),
Some(WindowKind::Dialogs(id)) => match self.dialog() { },
Some(element) => return widget::autosize::autosize(element, id.clone()).into(), WindowKind::Preview(entity_opt, kind) => self
None => widget::horizontal_space().into(), .preview(entity_opt, kind, false)
}, .map(|x| Message::TabMessage(*entity_opt, x)),
Some(WindowKind::Preview(entity_opt, kind)) => self WindowKind::FileDialog(..) => match &self.file_dialog_opt {
.preview(entity_opt, kind, false) Some(dialog) => return dialog.view(id),
.map(|x| Message::TabMessage(*entity_opt, x)), None => widget::text("Unknown window ID").into(),
Some(WindowKind::FileDialog(..)) => match &self.file_dialog_opt { },
Some(dialog) => return dialog.view(id),
None => widget::text("Unknown window ID").into(),
}, },
None => { None => {
//TODO: distinct views per monitor in desktop mode //TODO: distinct views per monitor in desktop mode