grab: Refactor MenuGrab to be useful for zoom ui
This commit is contained in:
parent
e0530d2723
commit
6fd1a48e60
2 changed files with 425 additions and 162 deletions
|
|
@ -1,6 +1,9 @@
|
|||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
use std::{
|
||||
fmt,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use calloop::LoopHandle;
|
||||
|
|
@ -14,7 +17,7 @@ use cosmic::{
|
|||
};
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::ButtonState,
|
||||
input::{ButtonState, TouchSlot},
|
||||
renderer::{
|
||||
element::{memory::MemoryRenderBufferRenderElement, AsRenderElements},
|
||||
ImportMem, Renderer,
|
||||
|
|
@ -26,26 +29,29 @@ use smithay::{
|
|||
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
|
||||
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
|
||||
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,
|
||||
GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle,
|
||||
PointerTarget, RelativeMotionEvent,
|
||||
GrabStartData as PointerGrabStartData, MotionEvent as PointerMotionEvent, PointerGrab,
|
||||
PointerInnerHandle, PointerTarget, RelativeMotionEvent,
|
||||
},
|
||||
touch::{
|
||||
DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent,
|
||||
TouchGrab, TouchInnerHandle, TouchTarget, UpEvent,
|
||||
},
|
||||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
utils::{Logical, Point, Rectangle, Size},
|
||||
utils::{Logical, Point, Rectangle, Serial, Size},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
shell::focus::target::PointerFocusTarget,
|
||||
shell::SeatExt,
|
||||
shell::{focus::target::PointerFocusTarget, SeatExt},
|
||||
state::State,
|
||||
utils::{
|
||||
iced::{IcedElement, Program},
|
||||
prelude::{Global, OutputExt, PointGlobalExt, PointLocalExt, SizeExt},
|
||||
prelude::*,
|
||||
},
|
||||
};
|
||||
|
||||
use super::ResizeEdge;
|
||||
use super::{GrabStartData, ResizeEdge};
|
||||
|
||||
mod default;
|
||||
mod item;
|
||||
|
|
@ -53,6 +59,7 @@ pub use self::default::*;
|
|||
|
||||
pub struct MenuGrabState {
|
||||
elements: Arc<Mutex<Vec<Element>>>,
|
||||
screen_space_relative: Option<Output>,
|
||||
}
|
||||
pub type SeatMenuGrabState = Mutex<Option<MenuGrabState>>;
|
||||
|
||||
|
|
@ -82,6 +89,10 @@ impl MenuGrabState {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn is_in_screen_space(&self) -> bool {
|
||||
self.screen_space_relative.is_some()
|
||||
}
|
||||
|
||||
pub fn set_theme(&self, theme: cosmic::Theme) {
|
||||
for element in &*self.elements.lock().unwrap() {
|
||||
element.iced.set_theme(theme.clone())
|
||||
|
|
@ -106,6 +117,35 @@ pub enum Item {
|
|||
},
|
||||
}
|
||||
|
||||
impl fmt::Debug for Item {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Separator => write!(f, "Separator"),
|
||||
Self::Submenu { title, items } => f
|
||||
.debug_struct("Submenu")
|
||||
.field("title", title)
|
||||
.field("items", items)
|
||||
.finish(),
|
||||
Self::Entry {
|
||||
title,
|
||||
shortcut,
|
||||
on_press: _,
|
||||
toggled,
|
||||
submenu,
|
||||
disabled,
|
||||
} => f
|
||||
.debug_struct("Entry")
|
||||
.field("title", title)
|
||||
.field("shortcut", shortcut)
|
||||
.field("on_press", &"...")
|
||||
.field("toggled", toggled)
|
||||
.field("submenu", submenu)
|
||||
.field("disabled", disabled)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn new<S: Into<String>, F: Fn(&LoopHandle<'_, State>) + Send + Sync + 'static>(
|
||||
title: S,
|
||||
|
|
@ -162,6 +202,7 @@ impl Item {
|
|||
}
|
||||
|
||||
/// Menu that comes up when right-clicking an application header bar
|
||||
#[derive(Debug)]
|
||||
pub struct ContextMenu {
|
||||
items: Vec<Item>,
|
||||
selected: AtomicBool,
|
||||
|
|
@ -176,6 +217,10 @@ impl ContextMenu {
|
|||
row_width: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_row_width(&self, width: f32) {
|
||||
*self.row_width.lock().unwrap() = Some(width);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -202,6 +247,7 @@ impl Program for ContextMenu {
|
|||
&mut self,
|
||||
message: Self::Message,
|
||||
loop_handle: &LoopHandle<'static, crate::state::State>,
|
||||
last_seat: Option<&(Seat<State>, Serial)>,
|
||||
) -> Task<Self::Message> {
|
||||
match message {
|
||||
Message::ItemPressed(idx) => {
|
||||
|
|
@ -209,124 +255,118 @@ impl Program for ContextMenu {
|
|||
(on_press)(loop_handle);
|
||||
self.selected.store(true, Ordering::SeqCst);
|
||||
}
|
||||
// TODO: If Submenu, then also expand on "Pressed" for touch events.
|
||||
// But right now we don't have any touch responsive menus with submenus
|
||||
}
|
||||
Message::ItemEntered(idx, bounds) => {
|
||||
if let Some(Item::Submenu { items, .. }) = self.items.get_mut(idx) {
|
||||
let items = items.clone();
|
||||
let _ = loop_handle.insert_idle(move |state| {
|
||||
let seat = state
|
||||
.common
|
||||
.shell
|
||||
.read()
|
||||
.unwrap()
|
||||
.seats
|
||||
.last_active()
|
||||
.clone();
|
||||
let grab_state = seat
|
||||
.user_data()
|
||||
.get::<SeatMenuGrabState>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
if let Some((seat, _)) = last_seat.cloned() {
|
||||
let items = items.clone();
|
||||
let _ = loop_handle.insert_idle(move |state| {
|
||||
let grab_state = seat
|
||||
.user_data()
|
||||
.get::<SeatMenuGrabState>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
if let Some(grab_state) = &*grab_state {
|
||||
let mut elements = grab_state.elements.lock().unwrap();
|
||||
if let Some(grab_state) = &*grab_state {
|
||||
let mut elements = grab_state.elements.lock().unwrap();
|
||||
|
||||
let position = elements.last().unwrap().position;
|
||||
let element = IcedElement::new(
|
||||
ContextMenu::new(items),
|
||||
Size::default(),
|
||||
state.common.event_loop_handle.clone(),
|
||||
state.common.theme.clone(),
|
||||
);
|
||||
let position = elements.last().unwrap().position;
|
||||
let element = IcedElement::new(
|
||||
ContextMenu::new(items),
|
||||
Size::default(),
|
||||
state.common.event_loop_handle.clone(),
|
||||
state.common.theme.clone(),
|
||||
);
|
||||
|
||||
let min_size = element.minimum_size();
|
||||
element.with_program(|p| {
|
||||
*p.row_width.lock().unwrap() = Some(min_size.w as f32);
|
||||
});
|
||||
element.resize(min_size);
|
||||
let min_size = element.minimum_size();
|
||||
element.with_program(|p| {
|
||||
*p.row_width.lock().unwrap() = Some(min_size.w as f32);
|
||||
});
|
||||
element.resize(min_size);
|
||||
|
||||
let output = seat.active_output();
|
||||
let position = [
|
||||
// to the right -> down
|
||||
Rectangle::new(
|
||||
position
|
||||
+ Point::from((
|
||||
bounds.width.ceil() as i32,
|
||||
bounds.y.ceil() as i32,
|
||||
)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
// to the right -> up
|
||||
Rectangle::new(
|
||||
position
|
||||
+ Point::from((
|
||||
bounds.width.ceil() as i32,
|
||||
bounds.y.ceil() as i32 + bounds.height.ceil() as i32
|
||||
- min_size.h,
|
||||
)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
// to the left -> down
|
||||
Rectangle::new(
|
||||
position + Point::from((-min_size.w, bounds.y.ceil() as i32)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
// to the left -> up
|
||||
Rectangle::new(
|
||||
position
|
||||
+ Point::from((
|
||||
-min_size.w,
|
||||
bounds.y.ceil() as i32 + bounds.height.ceil() as i32
|
||||
- min_size.h,
|
||||
)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.rev() // preference of max_by_key is backwards
|
||||
.max_by_key(|rect| {
|
||||
output
|
||||
.geometry()
|
||||
.intersection(**rect)
|
||||
.map(|rect| rect.size.w * rect.size.h)
|
||||
})
|
||||
.unwrap()
|
||||
.loc;
|
||||
element.output_enter(&output, element.bbox());
|
||||
let output = seat.active_output();
|
||||
let position = [
|
||||
// to the right -> down
|
||||
Rectangle::new(
|
||||
position
|
||||
+ Point::from((
|
||||
bounds.width.ceil() as i32,
|
||||
bounds.y.ceil() as i32,
|
||||
)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
// to the right -> up
|
||||
Rectangle::new(
|
||||
position
|
||||
+ Point::from((
|
||||
bounds.width.ceil() as i32,
|
||||
bounds.y.ceil() as i32
|
||||
+ bounds.height.ceil() as i32
|
||||
- min_size.h,
|
||||
)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
// to the left -> down
|
||||
Rectangle::new(
|
||||
position
|
||||
+ Point::from((-min_size.w, bounds.y.ceil() as i32)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
// to the left -> up
|
||||
Rectangle::new(
|
||||
position
|
||||
+ Point::from((
|
||||
-min_size.w,
|
||||
bounds.y.ceil() as i32
|
||||
+ bounds.height.ceil() as i32
|
||||
- min_size.h,
|
||||
)),
|
||||
min_size.as_global(),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.rev() // preference of max_by_key is backwards
|
||||
.max_by_key(|rect| {
|
||||
output
|
||||
.geometry()
|
||||
.intersection(**rect)
|
||||
.map(|rect| rect.size.w * rect.size.h)
|
||||
})
|
||||
.unwrap()
|
||||
.loc;
|
||||
element.output_enter(&output, element.bbox());
|
||||
|
||||
elements.push(Element {
|
||||
iced: element,
|
||||
position,
|
||||
pointer_entered: false,
|
||||
})
|
||||
}
|
||||
});
|
||||
elements.push(Element {
|
||||
iced: element,
|
||||
position,
|
||||
pointer_entered: false,
|
||||
touch_entered: None,
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::ItemLeft(idx, _) => {
|
||||
if let Some(Item::Submenu { .. }) = self.items.get_mut(idx) {
|
||||
let _ = loop_handle.insert_idle(|state| {
|
||||
let seat = state
|
||||
.common
|
||||
.shell
|
||||
.read()
|
||||
.unwrap()
|
||||
.seats
|
||||
.last_active()
|
||||
.clone();
|
||||
let grab_state = seat
|
||||
.user_data()
|
||||
.get::<SeatMenuGrabState>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
if let Some((seat, _)) = last_seat.cloned() {
|
||||
let _ = loop_handle.insert_idle(move |_| {
|
||||
let grab_state = seat
|
||||
.user_data()
|
||||
.get::<SeatMenuGrabState>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
if let Some(grab_state) = &*grab_state {
|
||||
let mut elements = grab_state.elements.lock().unwrap();
|
||||
elements.pop();
|
||||
}
|
||||
});
|
||||
if let Some(grab_state) = &*grab_state {
|
||||
let mut elements = grab_state.elements.lock().unwrap();
|
||||
elements.pop();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -457,12 +497,14 @@ pub struct Element {
|
|||
iced: IcedElement<ContextMenu>,
|
||||
position: Point<i32, Global>,
|
||||
pointer_entered: bool,
|
||||
touch_entered: Option<TouchSlot>,
|
||||
}
|
||||
|
||||
pub struct MenuGrab {
|
||||
elements: Arc<Mutex<Vec<Element>>>,
|
||||
start_data: PointerGrabStartData<State>,
|
||||
start_data: GrabStartData,
|
||||
seat: Seat<State>,
|
||||
screen_space_relative: Option<Output>,
|
||||
}
|
||||
|
||||
impl PointerGrab<State> for MenuGrab {
|
||||
|
|
@ -471,21 +513,36 @@ impl PointerGrab<State> for MenuGrab {
|
|||
state: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &MotionEvent,
|
||||
event: &PointerMotionEvent,
|
||||
) {
|
||||
{
|
||||
let mut guard = self.elements.lock().unwrap();
|
||||
let elements = &mut *guard;
|
||||
let event_location = if let Some(output) = self.screen_space_relative.as_ref() {
|
||||
if let Some(zoom_state) = state.common.shell.read().unwrap().zoom_state() {
|
||||
event
|
||||
.location
|
||||
.as_global()
|
||||
.to_zoomed(output, zoom_state.level)
|
||||
.to_global(output)
|
||||
.as_logical()
|
||||
} else {
|
||||
event.location
|
||||
}
|
||||
} else {
|
||||
event.location
|
||||
};
|
||||
|
||||
if let Some(i) = elements.iter().position(|elem| {
|
||||
let mut bbox = elem.iced.bbox();
|
||||
bbox.loc = elem.position.as_logical();
|
||||
|
||||
bbox.contains(event.location.to_i32_round())
|
||||
bbox.contains(event_location.to_i32_round())
|
||||
}) {
|
||||
let element = &mut elements[i];
|
||||
|
||||
let new_event = MotionEvent {
|
||||
location: event.location - element.position.as_logical().to_f64(),
|
||||
let new_event = PointerMotionEvent {
|
||||
location: event_location - element.position.as_logical().to_f64(),
|
||||
serial: event.serial,
|
||||
time: event.time,
|
||||
};
|
||||
|
|
@ -493,7 +550,7 @@ impl PointerGrab<State> for MenuGrab {
|
|||
PointerTarget::enter(&element.iced, &self.seat, state, &new_event);
|
||||
element.pointer_entered = true;
|
||||
} else {
|
||||
element.iced.motion(&self.seat, state, &new_event);
|
||||
PointerTarget::motion(&element.iced, &self.seat, state, &new_event);
|
||||
}
|
||||
} else {
|
||||
elements.iter_mut().for_each(|element| {
|
||||
|
|
@ -545,7 +602,7 @@ impl PointerGrab<State> for MenuGrab {
|
|||
let elements = self.elements.lock().unwrap();
|
||||
let mut selected = false;
|
||||
for element in elements.iter().filter(|elem| elem.pointer_entered) {
|
||||
element.iced.button(&self.seat, state, event);
|
||||
PointerTarget::button(&element.iced, &self.seat, state, event);
|
||||
selected = true;
|
||||
}
|
||||
selected
|
||||
|
|
@ -644,18 +701,224 @@ impl PointerGrab<State> for MenuGrab {
|
|||
}
|
||||
|
||||
fn start_data(&self) -> &PointerGrabStartData<State> {
|
||||
&self.start_data
|
||||
match &self.start_data {
|
||||
GrabStartData::Pointer(start_data) => start_data,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unset(&mut self, _data: &mut State) {}
|
||||
}
|
||||
|
||||
impl TouchGrab<State> for MenuGrab {
|
||||
fn down(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
{
|
||||
let mut guard = self.elements.lock().unwrap();
|
||||
let elements = &mut *guard;
|
||||
let event_location = if let Some(output) = self.screen_space_relative.as_ref() {
|
||||
if let Some(zoom_state) = data.common.shell.read().unwrap().zoom_state() {
|
||||
event
|
||||
.location
|
||||
.as_global()
|
||||
.to_zoomed(output, zoom_state.level)
|
||||
.to_global(output)
|
||||
.as_logical()
|
||||
} else {
|
||||
event.location
|
||||
}
|
||||
} else {
|
||||
event.location
|
||||
};
|
||||
|
||||
if let Some(i) = elements.iter().position(|elem| {
|
||||
let mut bbox = elem.iced.bbox();
|
||||
bbox.loc = elem.position.as_logical();
|
||||
|
||||
bbox.contains(event_location.to_i32_round())
|
||||
}) {
|
||||
let element = &mut elements[i];
|
||||
|
||||
let new_event = DownEvent {
|
||||
slot: event.slot,
|
||||
location: event_location - element.position.as_logical().to_f64(),
|
||||
serial: event.serial,
|
||||
time: event.time,
|
||||
};
|
||||
if element.touch_entered.is_none() {
|
||||
TouchTarget::down(&element.iced, &self.seat, data, &new_event, seq);
|
||||
element.touch_entered = Some(event.slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
handle.down(data, None, event, seq);
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
{
|
||||
let elements = self.elements.lock().unwrap();
|
||||
for element in elements.iter().filter(|elem| {
|
||||
elem.touch_entered
|
||||
.as_ref()
|
||||
.is_some_and(|slot| *slot == event.slot)
|
||||
}) {
|
||||
TouchTarget::up(&element.iced, &self.seat, data, event, seq);
|
||||
}
|
||||
}
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
fn motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
{
|
||||
let elements = self.elements.lock().unwrap();
|
||||
for element in elements.iter().filter(|elem| {
|
||||
elem.touch_entered
|
||||
.as_ref()
|
||||
.is_some_and(|slot| *slot == event.slot)
|
||||
}) {
|
||||
TouchTarget::motion(&element.iced, &self.seat, data, event, seq);
|
||||
}
|
||||
}
|
||||
handle.motion(data, None, event, seq);
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
handle.frame(data, seq);
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
{
|
||||
let mut elements = self.elements.lock().unwrap();
|
||||
for element in elements.iter_mut() {
|
||||
let _ = element.touch_entered.take();
|
||||
}
|
||||
}
|
||||
handle.cancel(data, seq);
|
||||
}
|
||||
|
||||
fn shape(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &smithay::input::touch::ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.shape(data, event, seq);
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &smithay::input::touch::OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.orientation(data, event, seq);
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||
match &self.start_data {
|
||||
GrabStartData::Touch(start_data) => start_data,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unset(&mut self, _data: &mut State) {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct MenuAlignment {
|
||||
pub x: AxisAlignment,
|
||||
pub y: AxisAlignment,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AxisAlignment {
|
||||
Corner,
|
||||
Centered,
|
||||
}
|
||||
|
||||
impl MenuAlignment {
|
||||
pub const CORNER: Self = MenuAlignment {
|
||||
x: AxisAlignment::Corner,
|
||||
y: AxisAlignment::Corner,
|
||||
};
|
||||
pub const CENTERED: Self = MenuAlignment {
|
||||
x: AxisAlignment::Centered,
|
||||
y: AxisAlignment::Centered,
|
||||
};
|
||||
pub const HORIZONTALLY_CENTERED: Self = MenuAlignment {
|
||||
x: AxisAlignment::Centered,
|
||||
y: AxisAlignment::Corner,
|
||||
};
|
||||
pub const VERTICALLY_CENTERED: Self = MenuAlignment {
|
||||
x: AxisAlignment::Corner,
|
||||
y: AxisAlignment::Centered,
|
||||
};
|
||||
|
||||
fn rectangles(
|
||||
&self,
|
||||
position: Point<i32, Global>,
|
||||
size: Size<i32, Global>,
|
||||
) -> Vec<Rectangle<i32, Global>> {
|
||||
match (self.x, self.y) {
|
||||
(AxisAlignment::Corner, AxisAlignment::Corner) => vec![
|
||||
Rectangle::new(position, size), // normal
|
||||
Rectangle::new(position - Point::from((size.w, 0)), size), // flipped left
|
||||
Rectangle::new(position - Point::from((0, size.h)), size), // flipped up
|
||||
Rectangle::new(position - size.to_point(), size), // flipped left & up
|
||||
],
|
||||
(AxisAlignment::Centered, AxisAlignment::Corner) => {
|
||||
let x = position.x - ((size.w as f64 / 2.).round() as i32);
|
||||
vec![
|
||||
Rectangle::new(Point::from((x, position.y)), size), // below
|
||||
Rectangle::new(Point::from((x, position.y - size.h)), size), // above
|
||||
]
|
||||
}
|
||||
(AxisAlignment::Corner, AxisAlignment::Centered) => {
|
||||
let y = position.y - ((size.h as f64 / 2.).round() as i32);
|
||||
vec![
|
||||
Rectangle::new(Point::from((position.x, y)), size), // left
|
||||
Rectangle::new(Point::from((position.x - size.w, y)), size), // right
|
||||
]
|
||||
}
|
||||
(AxisAlignment::Centered, AxisAlignment::Centered) => {
|
||||
vec![Rectangle::new(
|
||||
position - size.to_f64().downscale(2.).to_i32_round().to_point(),
|
||||
size,
|
||||
)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MenuGrab {
|
||||
pub fn new(
|
||||
start_data: PointerGrabStartData<State>,
|
||||
start_data: GrabStartData,
|
||||
seat: &Seat<State>,
|
||||
items: impl Iterator<Item = Item>,
|
||||
position: Point<i32, Global>,
|
||||
alignment: MenuAlignment,
|
||||
screen_space_relative: bool,
|
||||
handle: LoopHandle<'static, crate::state::State>,
|
||||
theme: cosmic::Theme,
|
||||
) -> MenuGrab {
|
||||
|
|
@ -668,31 +931,18 @@ impl MenuGrab {
|
|||
element.resize(min_size);
|
||||
|
||||
let output = seat.active_output();
|
||||
let position = [
|
||||
Rectangle::new(position, min_size.as_global()), // normal
|
||||
Rectangle::new(
|
||||
position - Point::from((min_size.w, 0)),
|
||||
min_size.as_global(),
|
||||
), // flipped left
|
||||
Rectangle::new(
|
||||
position - Point::from((0, min_size.h)),
|
||||
min_size.as_global(),
|
||||
), // flipped up
|
||||
Rectangle::new(
|
||||
position - Point::from((min_size.w, min_size.h)),
|
||||
min_size.as_global(),
|
||||
), // flipped left & up
|
||||
]
|
||||
.iter()
|
||||
.rev() // preference of max_by_key is backwards
|
||||
.max_by_key(|rect| {
|
||||
output
|
||||
.geometry()
|
||||
.intersection(**rect)
|
||||
.map(|rect| rect.size.w * rect.size.h)
|
||||
})
|
||||
.unwrap()
|
||||
.loc;
|
||||
let position = alignment
|
||||
.rectangles(position, min_size.as_global())
|
||||
.iter()
|
||||
.rev() // preference of max_by_key is backwards
|
||||
.max_by_key(|rect| {
|
||||
output
|
||||
.geometry()
|
||||
.intersection(**rect)
|
||||
.map(|rect| rect.size.w * rect.size.h)
|
||||
})
|
||||
.unwrap()
|
||||
.loc;
|
||||
|
||||
element.output_enter(&output, element.bbox());
|
||||
|
||||
|
|
@ -700,10 +950,14 @@ impl MenuGrab {
|
|||
iced: element,
|
||||
position,
|
||||
pointer_entered: false,
|
||||
touch_entered: None,
|
||||
}]));
|
||||
|
||||
let screen_space_relative = screen_space_relative.then_some(output);
|
||||
|
||||
let grab_state = MenuGrabState {
|
||||
elements: elements.clone(),
|
||||
screen_space_relative: screen_space_relative.clone(),
|
||||
};
|
||||
|
||||
*seat
|
||||
|
|
@ -717,6 +971,14 @@ impl MenuGrab {
|
|||
elements,
|
||||
start_data,
|
||||
seat: seat.clone(),
|
||||
screen_space_relative,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_touch_grab(&self) -> bool {
|
||||
match self.start_data {
|
||||
GrabStartData::Touch(_) => true,
|
||||
GrabStartData::Pointer(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use calloop::LoopHandle;
|
||||
use focus::target::WindowGroup;
|
||||
use grabs::SeatMoveGrabState;
|
||||
use grabs::{MenuAlignment, SeatMoveGrabState};
|
||||
use indexmap::IndexMap;
|
||||
use layout::TilingExceptions;
|
||||
use std::{
|
||||
|
|
@ -2900,9 +2900,9 @@ impl Shell {
|
|||
) -> Option<(MenuGrab, Focus)> {
|
||||
let serial = serial.into();
|
||||
let Some(GrabStartData::Pointer(start_data)) =
|
||||
check_grab_preconditions(&seat, surface, serial, true)
|
||||
check_grab_preconditions(&seat, serial, Some(surface))
|
||||
else {
|
||||
return None;
|
||||
return None; // TODO: an application can send a menu request for a touch event
|
||||
};
|
||||
|
||||
let mapped = self.element_for_surface(surface).cloned()?;
|
||||
|
|
@ -2966,7 +2966,7 @@ impl Shell {
|
|||
};
|
||||
|
||||
let grab = MenuGrab::new(
|
||||
start_data,
|
||||
GrabStartData::Pointer(start_data),
|
||||
seat,
|
||||
if target_stack || !is_stacked {
|
||||
Box::new(window_items(
|
||||
|
|
@ -2987,6 +2987,8 @@ impl Shell {
|
|||
as Box<dyn Iterator<Item = Item>>
|
||||
},
|
||||
global_position,
|
||||
MenuAlignment::CORNER,
|
||||
false,
|
||||
evlh.clone(),
|
||||
self.theme.clone(),
|
||||
);
|
||||
|
|
@ -3008,7 +3010,8 @@ impl Shell {
|
|||
) -> Option<(MoveGrab, Focus)> {
|
||||
let serial = serial.into();
|
||||
|
||||
let mut start_data = check_grab_preconditions(&seat, surface, serial, client_initiated)?;
|
||||
let mut start_data =
|
||||
check_grab_preconditions(&seat, serial, client_initiated.then_some(surface))?;
|
||||
let old_mapped = self.element_for_surface(surface).cloned()?;
|
||||
if old_mapped.is_minimized() {
|
||||
return None;
|
||||
|
|
@ -3453,13 +3456,11 @@ impl Shell {
|
|||
),
|
||||
(ResizeGrab, Focus),
|
||||
)> {
|
||||
let active_window = mapped.active_window();
|
||||
let surface = active_window.wl_surface()?;
|
||||
if mapped.is_fullscreen(true) || mapped.is_maximized(true) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut start_data = check_grab_preconditions(&seat, &surface, None, false)?;
|
||||
let mut start_data = check_grab_preconditions(&seat, None, None)?;
|
||||
|
||||
let (floating_layer, geometry) = if let Some(set) = self
|
||||
.workspaces
|
||||
|
|
@ -3693,7 +3694,8 @@ impl Shell {
|
|||
client_initiated: bool,
|
||||
) -> Option<(ResizeGrab, Focus)> {
|
||||
let serial = serial.into();
|
||||
let start_data = check_grab_preconditions(&seat, surface, serial, client_initiated)?;
|
||||
let start_data =
|
||||
check_grab_preconditions(&seat, serial, client_initiated.then_some(surface))?;
|
||||
let mapped = self.element_for_surface(surface).cloned()?;
|
||||
if mapped.is_fullscreen(true) || mapped.is_maximized(true) {
|
||||
return None;
|
||||
|
|
@ -4119,9 +4121,8 @@ fn workspace_set_idx(
|
|||
|
||||
pub fn check_grab_preconditions(
|
||||
seat: &Seat<State>,
|
||||
surface: &WlSurface,
|
||||
serial: Option<Serial>,
|
||||
client_initiated: bool,
|
||||
client_initiated: Option<&WlSurface>,
|
||||
) -> Option<GrabStartData> {
|
||||
use smithay::reexports::wayland_server::Resource;
|
||||
|
||||
|
|
@ -4141,7 +4142,7 @@ pub fn check_grab_preconditions(
|
|||
}))
|
||||
};
|
||||
|
||||
if client_initiated {
|
||||
if let Some(surface) = client_initiated {
|
||||
// Check that this surface has a click or touch down grab.
|
||||
if !match serial {
|
||||
Some(serial) => pointer.has_grab(serial) || touch.has_grab(serial),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue