shell: xdg-popup positioning logic
This commit is contained in:
parent
c3c0a25a80
commit
be136306cc
6 changed files with 434 additions and 13 deletions
299
src/wayland/handlers/xdg_shell/mod.rs
Normal file
299
src/wayland/handlers/xdg_shell/mod.rs
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::utils::prelude::*;
|
||||
use smithay::{
|
||||
delegate_xdg_shell,
|
||||
desktop::{
|
||||
Kind, PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy,
|
||||
Window, WindowSurfaceType,
|
||||
},
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||
wayland_server::{
|
||||
protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||
DisplayHandle,
|
||||
},
|
||||
},
|
||||
wayland::{
|
||||
output::Output,
|
||||
seat::{PointerGrabStartData, Seat},
|
||||
shell::xdg::{
|
||||
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler,
|
||||
XdgShellState,
|
||||
},
|
||||
Serial,
|
||||
},
|
||||
};
|
||||
use std::cell::Cell;
|
||||
|
||||
pub mod popup;
|
||||
|
||||
pub type PopupGrabData = Cell<Option<PopupGrab>>;
|
||||
|
||||
impl XdgShellHandler for State {
|
||||
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||
&mut self.common.shell.xdg_shell_state
|
||||
}
|
||||
|
||||
fn new_toplevel(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
|
||||
|
||||
let seat = &self.common.last_active_seat;
|
||||
let window = Window::new(Kind::Xdg(surface));
|
||||
self.common.shell.toplevel_info_state.new_toplevel(&window);
|
||||
self.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.push((window, seat.clone()));
|
||||
// We will position the window after the first commit, when we know its size hints
|
||||
}
|
||||
|
||||
fn new_popup(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: PopupSurface,
|
||||
positioner: PositionerState,
|
||||
) {
|
||||
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
|
||||
|
||||
surface.with_pending_state(|state| {
|
||||
state.geometry = positioner.get_geometry();
|
||||
state.positioner = positioner;
|
||||
});
|
||||
|
||||
self.common.shell.unconstrain_popup(&surface, &positioner);
|
||||
|
||||
if surface.send_configure().is_ok() {
|
||||
self.common
|
||||
.shell
|
||||
.popups
|
||||
.track_popup(PopupKind::from(surface))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn ack_configure(&mut self, _dh: &DisplayHandle, surface: WlSurface, configure: Configure) {
|
||||
if let Configure::Toplevel(configure) = configure {
|
||||
// If we would re-position the window inside the grab we would get a weird jittery animation.
|
||||
// We only want to resize once the client has acknoledged & commited the new size,
|
||||
// so we need to carefully track the state through different handlers.
|
||||
if let Some(window) =
|
||||
self.common
|
||||
.shell
|
||||
.space_for_surface(&surface)
|
||||
.and_then(|workspace| {
|
||||
workspace
|
||||
.space
|
||||
.window_for_surface(&surface, WindowSurfaceType::TOPLEVEL)
|
||||
})
|
||||
{
|
||||
crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure(window, configure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grab(&mut self, dh: &DisplayHandle, surface: PopupSurface, seat: WlSeat, serial: Serial) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
let ret = self
|
||||
.common
|
||||
.shell
|
||||
.popups
|
||||
.grab_popup(dh, surface.into(), &seat, serial);
|
||||
|
||||
if let Ok(mut grab) = ret {
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
if keyboard.is_grabbed()
|
||||
&& !(keyboard.has_grab(serial)
|
||||
|| keyboard.has_grab(grab.previous_serial().unwrap_or(serial)))
|
||||
{
|
||||
grab.ungrab(dh, PopupUngrabStrategy::All);
|
||||
return;
|
||||
}
|
||||
self.common
|
||||
.set_focus(dh, grab.current_grab().as_ref(), &seat, Some(serial));
|
||||
keyboard.set_grab(PopupKeyboardGrab::new(&grab), serial);
|
||||
}
|
||||
|
||||
if let Some(pointer) = seat.get_pointer() {
|
||||
if pointer.is_grabbed()
|
||||
&& !(pointer.has_grab(serial)
|
||||
|| pointer
|
||||
.has_grab(grab.previous_serial().unwrap_or_else(|| grab.serial())))
|
||||
{
|
||||
grab.ungrab(dh, PopupUngrabStrategy::All);
|
||||
return;
|
||||
}
|
||||
pointer.set_grab(PopupPointerGrab::new(&grab), serial, 0);
|
||||
}
|
||||
|
||||
seat.user_data()
|
||||
.insert_if_missing(|| PopupGrabData::new(None));
|
||||
seat.user_data()
|
||||
.get::<PopupGrabData>()
|
||||
.unwrap()
|
||||
.set(Some(grab));
|
||||
}
|
||||
}
|
||||
|
||||
fn reposition_request(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: PopupSurface,
|
||||
positioner: PositionerState,
|
||||
token: u32,
|
||||
) {
|
||||
surface.with_pending_state(|state| {
|
||||
let geometry = positioner.get_geometry();
|
||||
state.geometry = geometry;
|
||||
state.positioner = positioner;
|
||||
});
|
||||
|
||||
self.common.shell.unconstrain_popup(&surface, &positioner);
|
||||
|
||||
surface.send_repositioned(token);
|
||||
}
|
||||
|
||||
fn move_request(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: ToplevelSurface,
|
||||
seat: WlSeat,
|
||||
serial: Serial,
|
||||
) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) {
|
||||
let workspace = self
|
||||
.common
|
||||
.shell
|
||||
.space_for_surface_mut(surface.wl_surface())
|
||||
.unwrap();
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
workspace.move_request(&window, &seat, serial, start_data);
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_request(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: ToplevelSurface,
|
||||
seat: WlSeat,
|
||||
serial: Serial,
|
||||
edges: xdg_toplevel::ResizeEdge,
|
||||
) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) {
|
||||
let workspace = self
|
||||
.common
|
||||
.shell
|
||||
.space_for_surface_mut(surface.wl_surface())
|
||||
.unwrap();
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
workspace.resize_request(&window, &seat, serial, start_data, edges);
|
||||
}
|
||||
}
|
||||
|
||||
fn maximize_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
let surface = surface.wl_surface();
|
||||
let seat = &self.common.last_active_seat;
|
||||
let output = active_output(seat, &self.common);
|
||||
|
||||
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(surface, WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap()
|
||||
.clone();
|
||||
workspace.maximize_request(&window, &output)
|
||||
}
|
||||
}
|
||||
|
||||
fn unmaximize_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Maximized);
|
||||
state.size = None;
|
||||
});
|
||||
surface.send_configure();
|
||||
}
|
||||
|
||||
fn fullscreen_request(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: ToplevelSurface,
|
||||
output: Option<WlOutput>,
|
||||
) {
|
||||
let output = output
|
||||
.as_ref()
|
||||
.and_then(Output::from_resource)
|
||||
.unwrap_or_else(|| {
|
||||
let seat = &self.common.last_active_seat;
|
||||
active_output(seat, &self.common)
|
||||
});
|
||||
|
||||
let surface = surface.wl_surface();
|
||||
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(surface, WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap()
|
||||
.clone();
|
||||
workspace.fullscreen_request(&window, &output)
|
||||
}
|
||||
}
|
||||
|
||||
fn unfullscreen_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
let surface = surface.wl_surface();
|
||||
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(surface, WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap()
|
||||
.clone();
|
||||
workspace.unfullscreen_request(&window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_grab_preconditions(
|
||||
seat: &Seat<State>,
|
||||
surface: &WlSurface,
|
||||
serial: Serial,
|
||||
) -> Option<PointerGrabStartData> {
|
||||
use smithay::reexports::wayland_server::Resource;
|
||||
|
||||
// TODO: touch resize.
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
// Check that this surface has a click grab.
|
||||
if !pointer.has_grab(serial) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let start_data = pointer.grab_start_data().unwrap();
|
||||
|
||||
// If the focus was for a different surface, ignore the request.
|
||||
if start_data.focus.is_none()
|
||||
|| !start_data
|
||||
.focus
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.0
|
||||
.id()
|
||||
.same_client_as(&surface.id())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(start_data)
|
||||
}
|
||||
|
||||
delegate_xdg_shell!(State);
|
||||
383
src/wayland/handlers/xdg_shell/popup.rs
Normal file
383
src/wayland/handlers/xdg_shell/popup.rs
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{shell::Shell, utils::prelude::*};
|
||||
use smithay::{
|
||||
desktop::{PopupKind, PopupManager, Space, Window, WindowSurfaceType},
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_positioner::{
|
||||
Anchor, ConstraintAdjustment, Gravity,
|
||||
},
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::{Logical, Point, Rectangle},
|
||||
wayland::{
|
||||
compositor::{get_role, with_states},
|
||||
shell::xdg::{
|
||||
PopupSurface, PositionerState, SurfaceCachedState, XdgPopupSurfaceRoleAttributes,
|
||||
XDG_POPUP_ROLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
use std::sync::Mutex;
|
||||
|
||||
impl Shell {
|
||||
pub fn unconstrain_popup(&self, surface: &PopupSurface, positioner: &PositionerState) {
|
||||
if let Some(parent) = get_popup_toplevel(&surface) {
|
||||
if let Some(workspace) = self.space_for_surface(&parent) {
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(&parent, WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap();
|
||||
unconstrain_popup(surface, positioner, &workspace.space, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_reactive_popups(&self, window: &Window) {
|
||||
if let Some(workspace) = self.space_for_surface(window.toplevel().wl_surface()) {
|
||||
update_reactive_popups(&workspace.space, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_reactive_popups(space: &Space, window: &Window) {
|
||||
for (popup, _) in PopupManager::popups_for_surface(window.toplevel().wl_surface()) {
|
||||
match popup {
|
||||
PopupKind::Xdg(surface) => {
|
||||
let positioner = with_states(&surface.wl_surface(), |states| {
|
||||
let attributes = states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
attributes.current.positioner.clone()
|
||||
});
|
||||
if positioner.reactive {
|
||||
unconstrain_popup(&surface, &positioner, space, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unconstrain_popup(
|
||||
surface: &PopupSurface,
|
||||
positioner: &PositionerState,
|
||||
space: &Space,
|
||||
window: &Window,
|
||||
) {
|
||||
let anchor_point = get_anchor_point(&positioner) + space.window_location(&window).unwrap();
|
||||
if let Some(output_rect) = space
|
||||
.outputs_for_window(window)
|
||||
.into_iter()
|
||||
.find(|o| o.geometry().contains(anchor_point))
|
||||
.map(|o| space.output_geometry(&o).unwrap())
|
||||
{
|
||||
let mut combined = output_rect
|
||||
.intersection(space.window_bbox(&window).unwrap_or_default())
|
||||
.unwrap_or_default();
|
||||
combined.loc -= space.window_location(&window).unwrap();
|
||||
let offset = check_constrained(&surface, positioner.get_geometry(), combined);
|
||||
|
||||
if offset.x != 0 || offset.y != 0 {
|
||||
if !unconstrain_flip(&surface, &positioner, combined) {
|
||||
if !unconstrain_slide(&surface, &positioner, combined) {
|
||||
unconstrain_resize(&surface, &positioner, combined);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unconstrain_flip(
|
||||
popup: &PopupSurface,
|
||||
positioner: &PositionerState,
|
||||
toplevel_box: Rectangle<i32, Logical>,
|
||||
) -> bool {
|
||||
let offset = check_constrained(popup, positioner.get_geometry(), toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut positioner = positioner.clone();
|
||||
|
||||
let flip_x = offset.x != 0
|
||||
&& positioner
|
||||
.constraint_adjustment
|
||||
.contains(ConstraintAdjustment::FlipX);
|
||||
let flip_y = offset.y != 0
|
||||
&& positioner
|
||||
.constraint_adjustment
|
||||
.contains(ConstraintAdjustment::FlipY);
|
||||
|
||||
if flip_x {
|
||||
positioner.anchor_edges = invert_anchor_x(positioner.anchor_edges);
|
||||
positioner.gravity = invert_gravity_x(positioner.gravity);
|
||||
}
|
||||
if flip_y {
|
||||
positioner.anchor_edges = invert_anchor_y(positioner.anchor_edges);
|
||||
positioner.gravity = invert_gravity_y(positioner.gravity);
|
||||
}
|
||||
|
||||
let offset = check_constrained(popup, positioner.get_geometry(), toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
// no longer constrained
|
||||
popup.with_pending_state(|state| {
|
||||
state.geometry = positioner.get_geometry();
|
||||
state.positioner = positioner;
|
||||
});
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn unconstrain_slide(
|
||||
popup: &PopupSurface,
|
||||
positioner: &PositionerState,
|
||||
toplevel_box: Rectangle<i32, Logical>,
|
||||
) -> bool {
|
||||
let offset = check_constrained(popup, positioner.get_geometry(), toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let slide_x = offset.x != 0
|
||||
&& positioner
|
||||
.constraint_adjustment
|
||||
.contains(ConstraintAdjustment::SlideX);
|
||||
let slide_y = offset.y != 0
|
||||
&& positioner
|
||||
.constraint_adjustment
|
||||
.contains(ConstraintAdjustment::SlideY);
|
||||
|
||||
let mut geometry = positioner.get_geometry();
|
||||
if slide_x {
|
||||
geometry.loc.x += offset.x;
|
||||
}
|
||||
if slide_y {
|
||||
geometry.loc.y += offset.y;
|
||||
}
|
||||
|
||||
let toplevel = get_popup_toplevel_coords(popup);
|
||||
if slide_x && toplevel.x < toplevel_box.loc.x {
|
||||
geometry.loc.x += toplevel_box.loc.x - toplevel.x;
|
||||
}
|
||||
if slide_y && toplevel.y < toplevel_box.loc.y {
|
||||
geometry.loc.y += toplevel_box.loc.y - toplevel.y;
|
||||
}
|
||||
|
||||
let offset = check_constrained(popup, geometry, toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
// no longer constrained
|
||||
popup.with_pending_state(|state| {
|
||||
state.geometry = geometry;
|
||||
});
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn unconstrain_resize(
|
||||
popup: &PopupSurface,
|
||||
positioner: &PositionerState,
|
||||
toplevel_box: Rectangle<i32, Logical>,
|
||||
) -> bool {
|
||||
let offset = check_constrained(popup, positioner.get_geometry(), toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let resize_x = offset.x != 0
|
||||
&& positioner
|
||||
.constraint_adjustment
|
||||
.contains(ConstraintAdjustment::ResizeX);
|
||||
let resize_y = offset.y != 0
|
||||
&& positioner
|
||||
.constraint_adjustment
|
||||
.contains(ConstraintAdjustment::ResizeY);
|
||||
|
||||
let mut geometry = positioner.get_geometry();
|
||||
if resize_x {
|
||||
geometry.size.w -= offset.x;
|
||||
}
|
||||
if resize_y {
|
||||
geometry.size.h -= offset.y;
|
||||
}
|
||||
|
||||
let offset = check_constrained(popup, geometry, toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
// no longer constrained
|
||||
popup.with_pending_state(|state| {
|
||||
state.geometry = geometry;
|
||||
});
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_constrained(
|
||||
popup: &PopupSurface,
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
toplevel_box: Rectangle<i32, Logical>,
|
||||
) -> Point<i32, Logical> {
|
||||
let relative_coords = Rectangle::from_loc_and_size(
|
||||
geometry.loc + get_popup_toplevel_coords(popup),
|
||||
geometry.size,
|
||||
);
|
||||
|
||||
let mut offset = (0, 0).into();
|
||||
if toplevel_box.contains_rect(relative_coords) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
if relative_coords.loc.x < toplevel_box.loc.x {
|
||||
offset.x = toplevel_box.loc.x - relative_coords.loc.x;
|
||||
} else if relative_coords.loc.x + relative_coords.size.w
|
||||
> toplevel_box.loc.x + toplevel_box.size.w
|
||||
{
|
||||
offset.x = toplevel_box.loc.x + toplevel_box.size.w
|
||||
- (relative_coords.loc.x + relative_coords.size.w);
|
||||
}
|
||||
|
||||
if relative_coords.loc.y < toplevel_box.loc.y {
|
||||
offset.y = toplevel_box.loc.y - relative_coords.loc.y;
|
||||
} else if relative_coords.loc.y + relative_coords.size.h
|
||||
> toplevel_box.loc.y + toplevel_box.size.h
|
||||
{
|
||||
offset.y = toplevel_box.loc.y + toplevel_box.size.h
|
||||
- (relative_coords.loc.y + relative_coords.size.h);
|
||||
}
|
||||
|
||||
offset
|
||||
}
|
||||
|
||||
fn get_anchor_point(positioner: &PositionerState) -> Point<i32, Logical> {
|
||||
let rect = positioner.anchor_rect;
|
||||
match positioner.anchor_edges {
|
||||
Anchor::Top => (rect.loc.x + (rect.size.w / 2), rect.loc.y),
|
||||
Anchor::Bottom => (rect.loc.x + (rect.size.w / 2), rect.loc.y + rect.size.h),
|
||||
Anchor::Left => (rect.loc.x, rect.loc.y + (rect.size.h / 2)),
|
||||
Anchor::Right => (rect.loc.x + rect.size.w, rect.loc.y + (rect.size.h / 2)),
|
||||
Anchor::TopLeft => (rect.loc.x, rect.loc.y),
|
||||
Anchor::TopRight => (rect.loc.x + rect.size.w, rect.loc.y),
|
||||
Anchor::BottomLeft => (rect.loc.x, rect.loc.y + rect.size.h),
|
||||
Anchor::BottomRight => (rect.loc.x + rect.size.w, rect.loc.y + rect.size.h),
|
||||
Anchor::None | _ => (
|
||||
rect.loc.x + (rect.size.w / 2),
|
||||
rect.loc.y + (rect.size.h / 2),
|
||||
),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn get_popup_toplevel(popup: &PopupSurface) -> Option<WlSurface> {
|
||||
let mut parent = popup.get_parent_surface()?;
|
||||
while get_role(&parent) == Some(XDG_POPUP_ROLE) {
|
||||
parent = with_states(&parent, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.parent
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
});
|
||||
}
|
||||
Some(parent)
|
||||
}
|
||||
|
||||
fn get_popup_toplevel_coords(popup: &PopupSurface) -> Point<i32, Logical> {
|
||||
let mut parent = match popup.get_parent_surface() {
|
||||
Some(parent) => parent,
|
||||
None => return (0, 0).into(),
|
||||
};
|
||||
|
||||
let mut offset = (0, 0).into();
|
||||
while get_role(&parent) == Some(XDG_POPUP_ROLE) {
|
||||
offset += with_states(&parent, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.current
|
||||
.geometry
|
||||
.loc
|
||||
});
|
||||
parent = with_states(&parent, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.parent
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
});
|
||||
}
|
||||
offset += with_states(&parent, |states| {
|
||||
states
|
||||
.cached_state
|
||||
.current::<SurfaceCachedState>()
|
||||
.geometry
|
||||
.map(|x| x.loc)
|
||||
.unwrap_or_else(|| (0, 0).into())
|
||||
});
|
||||
|
||||
offset
|
||||
}
|
||||
|
||||
fn invert_anchor_x(anchor: Anchor) -> Anchor {
|
||||
match anchor {
|
||||
Anchor::Left => Anchor::Right,
|
||||
Anchor::Right => Anchor::Left,
|
||||
Anchor::TopLeft => Anchor::TopRight,
|
||||
Anchor::TopRight => Anchor::TopLeft,
|
||||
Anchor::BottomLeft => Anchor::BottomRight,
|
||||
Anchor::BottomRight => Anchor::BottomLeft,
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
fn invert_anchor_y(anchor: Anchor) -> Anchor {
|
||||
match anchor {
|
||||
Anchor::Top => Anchor::Bottom,
|
||||
Anchor::Bottom => Anchor::Top,
|
||||
Anchor::TopLeft => Anchor::BottomLeft,
|
||||
Anchor::TopRight => Anchor::BottomRight,
|
||||
Anchor::BottomLeft => Anchor::TopLeft,
|
||||
Anchor::BottomRight => Anchor::TopRight,
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
fn invert_gravity_x(gravity: Gravity) -> Gravity {
|
||||
match gravity {
|
||||
Gravity::Left => Gravity::Right,
|
||||
Gravity::Right => Gravity::Left,
|
||||
Gravity::TopLeft => Gravity::TopRight,
|
||||
Gravity::TopRight => Gravity::TopLeft,
|
||||
Gravity::BottomLeft => Gravity::BottomRight,
|
||||
Gravity::BottomRight => Gravity::BottomLeft,
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
fn invert_gravity_y(gravity: Gravity) -> Gravity {
|
||||
match gravity {
|
||||
Gravity::Top => Gravity::Bottom,
|
||||
Gravity::Bottom => Gravity::Top,
|
||||
Gravity::TopLeft => Gravity::BottomLeft,
|
||||
Gravity::TopRight => Gravity::BottomRight,
|
||||
Gravity::BottomLeft => Gravity::TopLeft,
|
||||
Gravity::BottomRight => Gravity::TopRight,
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue