stack: Add interactive resize

This commit is contained in:
Victoria Brekenfeld 2024-02-29 13:12:57 +01:00 committed by Victoria Brekenfeld
parent afa7ad6d6a
commit 3c3a5f2ccf

View file

@ -1,11 +1,17 @@
use super::CosmicSurface; use super::{surface::RESIZE_BORDER, CosmicSurface};
use crate::{ use crate::{
backend::render::cursor::{CursorShape, CursorState},
shell::{ shell::{
focus::FocusDirection, grabs::ReleaseMode, layout::tiling::NodeDesc, Direction, Shell, focus::FocusDirection,
grabs::{ReleaseMode, ResizeEdge},
layout::tiling::NodeDesc,
Direction, Shell,
}, },
state::State, state::State,
utils::iced::{IcedElement, Program}, utils::{
utils::prelude::*, iced::{IcedElement, Program},
prelude::*,
},
wayland::handlers::screencopy::ScreencopySessions, wayland::handlers::screencopy::ScreencopySessions,
}; };
use calloop::LoopHandle; use calloop::LoopHandle;
@ -34,7 +40,7 @@ use smithay::{
input::{ input::{
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
pointer::{ pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent,
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent,
PointerTarget, RelativeMotionEvent, PointerTarget, RelativeMotionEvent,
@ -47,6 +53,7 @@ use smithay::{
wayland::seat::WaylandFocus, wayland::seat::WaylandFocus,
}; };
use std::{ use std::{
cell::RefCell,
fmt, fmt,
hash::Hash, hash::Hash,
sync::{ sync::{
@ -118,6 +125,14 @@ pub enum Focus {
None, None,
Header, Header,
Window, Window,
ResizeTop,
ResizeLeft,
ResizeRight,
ResizeBottom,
ResizeTopRight,
ResizeTopLeft,
ResizeBottomRight,
ResizeBottomLeft,
} }
pub enum MoveResult { pub enum MoveResult {
@ -922,20 +937,33 @@ impl SpaceElement for CosmicStack {
self.0.with_program(|p| { self.0.with_program(|p| {
let mut bbox = let mut bbox =
SpaceElement::bbox(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]); SpaceElement::bbox(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]);
bbox.loc -= Point::from((RESIZE_BORDER, RESIZE_BORDER));
bbox.size += Size::from((RESIZE_BORDER * 2, RESIZE_BORDER * 2));
bbox.size.h += TAB_HEIGHT; bbox.size.h += TAB_HEIGHT;
bbox bbox
}) })
} }
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool { fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
let mut point = *point; let mut point = *point;
let offset = self.0.with_program(|p| { let geo = self.0.with_program(|p| {
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
.geometry()
.loc
}); });
if (point.y.round() as i32 - offset.y) < TAB_HEIGHT {
let point_i32 = point.to_i32_round::<i32>();
if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0)
|| (point_i32.y - geo.loc.y >= -RESIZE_BORDER && point_i32.y - geo.loc.y < 0)
|| (point_i32.x - geo.loc.x >= geo.size.w
&& point_i32.x - geo.loc.x < geo.size.w + RESIZE_BORDER)
|| (point_i32.y - geo.loc.y >= geo.size.h
&& point_i32.y - geo.loc.y < geo.size.h + TAB_HEIGHT + RESIZE_BORDER)
{
return true; return true;
} }
if (point_i32.y - geo.loc.y) < TAB_HEIGHT {
return true;
}
point.y -= TAB_HEIGHT as f64; point.y -= TAB_HEIGHT as f64;
self.0.with_program(|p| { self.0.with_program(|p| {
SpaceElement::is_in_input_region( SpaceElement::is_in_input_region(
@ -1091,7 +1119,6 @@ impl KeyboardTarget<State> for CosmicStack {
impl PointerTarget<State> for CosmicStack { impl PointerTarget<State> for CosmicStack {
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) { fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
let mut event = event.clone(); let mut event = event.clone();
event.location.y -= TAB_HEIGHT as f64;
if self.0.with_program(|p| { if self.0.with_program(|p| {
let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)];
if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() { if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() {
@ -1100,24 +1127,65 @@ impl PointerTarget<State> for CosmicStack {
} }
} }
if (event.location.y - active_window.geometry().loc.y as f64) < 0. { let geo = active_window.geometry();
let previous = p.swap_focus(Focus::Header); let loc = event.location.to_i32_round::<i32>();
if previous == Focus::Window { let (old_focus, shape) = if loc.y - geo.loc.y < 0 && loc.x - geo.loc.x < 0 {
PointerTarget::leave(active_window, seat, data, event.serial, event.time); (
} p.swap_focus(Focus::ResizeTopLeft),
true CursorShape::NorthWestResize,
)
} else if loc.y - geo.loc.y < 0 && loc.x - geo.loc.x >= geo.size.w {
(
p.swap_focus(Focus::ResizeTopRight),
CursorShape::NorthEastResize,
)
} else if loc.y - geo.loc.y < 0 {
(p.swap_focus(Focus::ResizeTop), CursorShape::NorthResize)
} else if loc.y - geo.loc.y >= TAB_HEIGHT + geo.size.h && loc.x - geo.loc.x < 0 {
(
p.swap_focus(Focus::ResizeBottomLeft),
CursorShape::SouthWestResize,
)
} else if loc.y - geo.loc.y >= TAB_HEIGHT + geo.size.h
&& loc.x - geo.loc.x >= geo.size.w
{
(
p.swap_focus(Focus::ResizeBottomRight),
CursorShape::SouthEastResize,
)
} else if loc.y - geo.loc.y >= TAB_HEIGHT + geo.size.h {
(p.swap_focus(Focus::ResizeBottom), CursorShape::SouthResize)
} else if loc.x - geo.loc.x < 0 {
(p.swap_focus(Focus::ResizeLeft), CursorShape::WestResize)
} else if loc.x - geo.loc.x >= geo.size.w {
(p.swap_focus(Focus::ResizeRight), CursorShape::EastResize)
} else if loc.y - geo.loc.y < TAB_HEIGHT {
(p.swap_focus(Focus::Header), CursorShape::Default)
} else { } else {
p.swap_focus(Focus::Window); p.swap_focus(Focus::Window);
event.location.y -= TAB_HEIGHT as f64;
*p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time)); *p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time));
let active = p.active.load(Ordering::SeqCst); let active = p.active.load(Ordering::SeqCst);
p.previous_pointer.store(active, Ordering::SeqCst); p.previous_pointer.store(active, Ordering::SeqCst);
PointerTarget::enter(active_window, seat, data, &event); PointerTarget::enter(active_window, seat, data, &event);
false return false;
};
if old_focus == Focus::Window {
PointerTarget::leave(active_window, seat, data, event.serial, event.time);
} }
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
cursor_state.set_shape(shape);
let cursor_status = seat
.user_data()
.get::<RefCell<CursorImageStatus>>()
.unwrap();
*cursor_status.borrow_mut() = CursorImageStatus::default_named();
shape == CursorShape::Default
}) { }) {
event.location.y += TAB_HEIGHT as f64;
event.location -= self.0.with_program(|p| { event.location -= self.0.with_program(|p| {
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]
.geometry() .geometry()
@ -1130,11 +1198,10 @@ impl PointerTarget<State> for CosmicStack {
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) { fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
let mut event = event.clone(); let mut event = event.clone();
event.location.y -= TAB_HEIGHT as f64;
let active = let active =
self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location); self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location);
if let Some((previous, next)) = self.0.with_program(|p| { let (previous, next) = self.0.with_program(|p| {
let active_window = &p.windows.lock().unwrap()[active]; let active_window = &p.windows.lock().unwrap()[active];
if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() { if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() {
for session in &*sessions.0.borrow() { for session in &*sessions.0.borrow() {
@ -1147,13 +1214,30 @@ impl PointerTarget<State> for CosmicStack {
} }
} }
if (event.location.y - active_window.geometry().loc.y as f64) < 0. { let geo = active_window.geometry();
let previous = p.swap_focus(Focus::Header); let loc = event.location.to_i32_round::<i32>();
if previous == Focus::Window { let (next, shape) = if loc.y - geo.loc.y < 0 && loc.x - geo.loc.x < 0 {
PointerTarget::leave(active_window, seat, data, event.serial, event.time); (Focus::ResizeTopLeft, CursorShape::NorthWestResize)
} } else if loc.y - geo.loc.y < 0 && loc.x - geo.loc.x >= geo.size.w {
Some((previous, Focus::Header)) (Focus::ResizeTopRight, CursorShape::NorthEastResize)
} else if loc.y - geo.loc.y < 0 {
(Focus::ResizeTop, CursorShape::NorthResize)
} else if loc.y - geo.loc.y >= TAB_HEIGHT + geo.size.h && loc.x - geo.loc.x < 0 {
(Focus::ResizeBottomLeft, CursorShape::SouthWestResize)
} else if loc.y - geo.loc.y >= TAB_HEIGHT + geo.size.h
&& loc.x - geo.loc.x >= geo.size.w
{
(Focus::ResizeBottomRight, CursorShape::SouthEastResize)
} else if loc.y - geo.loc.y >= TAB_HEIGHT + geo.size.h {
(Focus::ResizeBottom, CursorShape::SouthResize)
} else if loc.x - geo.loc.x < 0 {
(Focus::ResizeLeft, CursorShape::WestResize)
} else if loc.x - geo.loc.x >= geo.size.w {
(Focus::ResizeRight, CursorShape::EastResize)
} else if (loc.y - geo.loc.y) < TAB_HEIGHT {
(Focus::Header, CursorShape::Default)
} else { } else {
event.location.y -= TAB_HEIGHT as f64;
*p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time)); *p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time));
let previous = p.swap_focus(Focus::Window); let previous = p.swap_focus(Focus::Window);
@ -1163,51 +1247,37 @@ impl PointerTarget<State> for CosmicStack {
PointerTarget::motion(active_window, seat, data, &event); PointerTarget::motion(active_window, seat, data, &event);
} }
Some((previous, Focus::Window)) return (previous, Focus::Window);
};
let previous = p.swap_focus(next);
if previous == Focus::Window {
PointerTarget::leave(active_window, seat, data, event.serial, event.time);
} }
}) {
event.location.y += TAB_HEIGHT as f64; let cursor_state = seat.user_data().get::<CursorState>().unwrap();
let active_window_geo = self cursor_state.set_shape(shape);
.0 let cursor_status = seat
.with_program(|p| p.windows.lock().unwrap()[active].geometry()); .user_data()
event.location -= active_window_geo.loc.to_f64(); .get::<RefCell<CursorImageStatus>>()
match (previous, next) { .unwrap();
(Focus::Header, Focus::Header) => { *cursor_status.borrow_mut() = CursorImageStatus::default_named();
PointerTarget::motion(&self.0, seat, data, &event);
if event.location.y < 0.0 (previous, next)
|| event.location.x < 64.0 });
|| event.location.x > (active_window_geo.size.w as f64 - 64.0)
{ let active_window_geo = self
if let Some(dragged_out) = self .0
.0 .with_program(|p| p.windows.lock().unwrap()[active].geometry());
.with_program(|p| p.potential_drag.lock().unwrap().take()) event.location -= active_window_geo.loc.to_f64();
{
if let Some(surface) = self.0.with_program(|p| { match (previous, next) {
p.windows.lock().unwrap().get(dragged_out).cloned() (Focus::Header, Focus::Header) => {
}) { PointerTarget::motion(&self.0, seat, data, &event);
let seat = seat.clone(); if event.location.y < 0.0
surface.try_force_undecorated(false); || event.location.x < 64.0
surface.send_configure(); || event.location.x > (active_window_geo.size.w as f64 - 64.0)
if let Some(surface) = surface.wl_surface() { {
let _ =
data.common.event_loop_handle.insert_idle(move |state| {
Shell::move_request(
state,
&surface,
&seat,
None,
ReleaseMode::NoMouseButtons,
true,
)
});
}
}
}
}
}
(_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, &event),
(Focus::Header, _) => {
PointerTarget::leave(&self.0, seat, data, event.serial, event.time);
if let Some(dragged_out) = self if let Some(dragged_out) = self
.0 .0
.with_program(|p| p.potential_drag.lock().unwrap().take()) .with_program(|p| p.potential_drag.lock().unwrap().take())
@ -1234,8 +1304,37 @@ impl PointerTarget<State> for CosmicStack {
} }
} }
} }
_ => {}
} }
(_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, &event),
(Focus::Header, _) => {
PointerTarget::leave(&self.0, seat, data, event.serial, event.time);
if let Some(dragged_out) = self
.0
.with_program(|p| p.potential_drag.lock().unwrap().take())
{
if let Some(surface) = self
.0
.with_program(|p| p.windows.lock().unwrap().get(dragged_out).cloned())
{
let seat = seat.clone();
surface.try_force_undecorated(false);
surface.send_configure();
if let Some(surface) = surface.wl_surface() {
let _ = data.common.event_loop_handle.insert_idle(move |state| {
Shell::move_request(
state,
&surface,
&seat,
None,
ReleaseMode::NoMouseButtons,
true,
)
});
}
}
}
}
_ => {}
} }
} }
@ -1284,7 +1383,36 @@ impl PointerTarget<State> for CosmicStack {
self.0.force_redraw(); self.0.force_redraw();
} }
} }
_ => {} Focus::None => {}
x => {
let serial = event.serial;
let seat = seat.clone();
let Some(surface) = self.0.with_program(|p| {
let window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)];
window.wl_surface()
}) else {
return;
};
self.0.loop_handle().insert_idle(move |state| {
Shell::resize_request(
state,
&surface,
&seat,
serial,
match x {
Focus::ResizeTop => ResizeEdge::TOP,
Focus::ResizeTopLeft => ResizeEdge::TOP_LEFT,
Focus::ResizeTopRight => ResizeEdge::TOP_RIGHT,
Focus::ResizeBottom => ResizeEdge::BOTTOM,
Focus::ResizeBottomLeft => ResizeEdge::BOTTOM_LEFT,
Focus::ResizeBottomRight => ResizeEdge::BOTTOM_RIGHT,
Focus::ResizeLeft => ResizeEdge::LEFT,
Focus::ResizeRight => ResizeEdge::RIGHT,
Focus::Header | Focus::Window | Focus::None => unreachable!(),
},
)
});
}
} }
} }
@ -1349,6 +1477,8 @@ impl PointerTarget<State> for CosmicStack {
} }
} }
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
cursor_state.set_shape(CursorShape::Default);
p.swap_focus(Focus::None) p.swap_focus(Focus::None)
}); });