fix(wayland): share a grabbing popup's modifiers with its toplevel
This commit is contained in:
parent
5716b9a691
commit
6f1b3a7170
5 changed files with 90 additions and 1 deletions
|
|
@ -1328,6 +1328,17 @@ async fn run_instance<P>(
|
|||
continue;
|
||||
}
|
||||
}
|
||||
// to avoid race with the reported modifier state from the popup of
|
||||
// this window, don't empty the modifiers, on focus loss
|
||||
#[cfg(wayland_platform)]
|
||||
if let winit::event::WindowEvent::ModifiersChanged(modifiers) =
|
||||
&event
|
||||
&& modifiers.state().is_empty()
|
||||
&& platform_specific_handler.has_popup(id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match event {
|
||||
winit::event::WindowEvent::SurfaceResized(_) => {
|
||||
window.raw.request_redraw();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ impl PlatformSpecific {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(wayland_platform)]
|
||||
pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool {
|
||||
self.wayland.has_popup(toplevel)
|
||||
}
|
||||
|
||||
pub(crate) fn retain_subsurfaces<F: Fn(window::Id) -> bool>(
|
||||
&mut self,
|
||||
keep: F,
|
||||
|
|
|
|||
|
|
@ -353,6 +353,31 @@ impl KeyboardHandler for SctkState {
|
|||
surface: surface.clone(),
|
||||
});
|
||||
}
|
||||
// A popup holds the seat's keyboard on behalf of its toplevel. Report
|
||||
// the modifiers to the toplevel as well.
|
||||
let toplevel = self.popmgr.popups().find_map(|p| {
|
||||
(p.popup.wl_surface() == &surface)
|
||||
.then(|| p.data.toplevel.clone())
|
||||
});
|
||||
if let Some(toplevel) = toplevel {
|
||||
let id = self
|
||||
.windows
|
||||
.iter()
|
||||
.find(|w| w.wl_surface(&self.connection) == toplevel)
|
||||
.map(|w| w.id)
|
||||
.or_else(|| {
|
||||
self.layer_surfaces
|
||||
.iter()
|
||||
.find(|l| *l.surface.wl_surface() == toplevel)
|
||||
.map(|l| l.id)
|
||||
});
|
||||
if let Some(toplevel) = id {
|
||||
self.sctk_events.push(SctkEvent::PopupModifiers {
|
||||
toplevel,
|
||||
modifiers,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use iced_graphics::{Compositor, compositor};
|
|||
use iced_runtime::core::{Vector, window};
|
||||
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
|
||||
use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle};
|
||||
use sctk_event::SctkEvent;
|
||||
use sctk_event::{PopupEventVariant, SctkEvent};
|
||||
use std::sync::OnceLock;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
||||
|
|
@ -103,6 +103,7 @@ pub(crate) struct WaylandSpecific {
|
|||
surface_ids: HashMap<ObjectId, SurfaceIdWrapper>,
|
||||
subsurface_state: Option<SubsurfaceState>,
|
||||
surface_subsurfaces: HashMap<window::Id, Vec<SubsurfaceInstance>>,
|
||||
popup_toplevels: HashMap<ObjectId, window::Id>,
|
||||
}
|
||||
|
||||
impl PlatformSpecific {
|
||||
|
|
@ -173,6 +174,11 @@ impl WaylandSpecific {
|
|||
self.conn.as_ref()
|
||||
}
|
||||
|
||||
/// Whether a popup opened from `toplevel` is alive.
|
||||
pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool {
|
||||
self.popup_toplevels.values().any(|id| *id == toplevel)
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_event<'a, 'b, P>(
|
||||
&mut self,
|
||||
e: SctkEvent,
|
||||
|
|
@ -201,8 +207,33 @@ impl WaylandSpecific {
|
|||
modifiers,
|
||||
subsurface_state,
|
||||
surface_subsurfaces,
|
||||
popup_toplevels,
|
||||
} = self;
|
||||
|
||||
if let SctkEvent::PopupEvent {
|
||||
variant,
|
||||
parent_id,
|
||||
parent_window,
|
||||
id,
|
||||
..
|
||||
} = &e
|
||||
{
|
||||
match variant {
|
||||
PopupEventVariant::Created(..) => {
|
||||
// A nested popup's parent is itself a popup so inhreit its toplevel
|
||||
let toplevel = popup_toplevels
|
||||
.get(&parent_id.id())
|
||||
.copied()
|
||||
.unwrap_or(*parent_window);
|
||||
_ = popup_toplevels.insert(id.id(), toplevel);
|
||||
}
|
||||
PopupEventVariant::Done => {
|
||||
_ = popup_toplevels.remove(&id.id());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
match e {
|
||||
sctk_event => {
|
||||
let Some(sender) = sender.as_ref() else {
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ pub enum SctkEvent {
|
|||
SessionUnlocked,
|
||||
SurfaceScaleFactorChanged(f64, WlSurface, window::Id),
|
||||
Winit(WindowId, WindowEvent),
|
||||
PopupModifiers {
|
||||
toplevel: window::Id,
|
||||
modifiers: cctk::sctk::seat::keyboard::Modifiers,
|
||||
},
|
||||
Subcompositor(SubsurfaceState),
|
||||
ShortcutsInhibited(bool),
|
||||
}
|
||||
|
|
@ -1400,6 +1404,19 @@ impl SctkEvent {
|
|||
),
|
||||
)),
|
||||
SctkEvent::Winit(_, _) => {}
|
||||
SctkEvent::PopupModifiers {
|
||||
toplevel,
|
||||
modifiers,
|
||||
} => {
|
||||
events.push((
|
||||
Some(toplevel),
|
||||
iced_runtime::core::Event::Keyboard(
|
||||
keyboard::Event::ModifiersChanged(modifiers_to_native(
|
||||
modifiers,
|
||||
)),
|
||||
),
|
||||
));
|
||||
}
|
||||
SctkEvent::SurfaceScaleFactorChanged(scale, _, id) => {
|
||||
if let Some(w) = window_manager.get_mut(id) {
|
||||
w.state.update_scale_factor(scale);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue