2022-10-26 15:26:07 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2023-07-18 12:21:16 +02:00
|
|
|
backend::render::{
|
2024-09-27 23:41:58 +02:00
|
|
|
cursor::CursorState, element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, Usage,
|
2023-07-18 12:21:16 +02:00
|
|
|
},
|
2022-10-26 15:26:07 +02:00
|
|
|
shell::{
|
2023-07-24 19:31:31 +02:00
|
|
|
element::{
|
|
|
|
|
stack_hover::{stack_hover, StackHover},
|
|
|
|
|
CosmicMappedRenderElement,
|
|
|
|
|
},
|
2022-10-26 15:26:07 +02:00
|
|
|
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
|
2024-03-18 00:12:58 -05:00
|
|
|
layout::floating::TiledCorners,
|
|
|
|
|
CosmicMapped, CosmicSurface, Direction, ManagedLayer,
|
2022-10-26 15:26:07 +02:00
|
|
|
},
|
|
|
|
|
utils::prelude::*,
|
2025-09-25 11:47:53 -04:00
|
|
|
wayland::protocols::toplevel_info::{toplevel_enter_output, toplevel_enter_workspace},
|
2022-10-26 15:26:07 +02:00
|
|
|
};
|
|
|
|
|
|
2023-12-11 16:11:07 +00:00
|
|
|
use calloop::LoopHandle;
|
2023-10-10 13:55:34 -04:00
|
|
|
use cosmic::theme::CosmicTheme;
|
2022-10-26 15:26:07 +02:00
|
|
|
use smithay::{
|
2023-12-07 19:49:53 +00:00
|
|
|
backend::{
|
|
|
|
|
input::ButtonState,
|
|
|
|
|
renderer::{
|
|
|
|
|
element::{utils::RescaleRenderElement, AsRenderElements, RenderElement},
|
|
|
|
|
ImportAll, ImportMem, Renderer,
|
|
|
|
|
},
|
2023-07-18 12:21:31 +02:00
|
|
|
},
|
2024-10-10 23:18:04 +02:00
|
|
|
desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType},
|
2022-10-26 15:26:07 +02:00
|
|
|
input::{
|
|
|
|
|
pointer::{
|
2024-09-09 16:21:27 +02:00
|
|
|
AxisFrame, ButtonEvent, CursorIcon, GestureHoldBeginEvent, GestureHoldEndEvent,
|
2023-09-05 10:55:23 -07:00
|
|
|
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
|
|
|
|
|
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,
|
|
|
|
|
GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle,
|
|
|
|
|
RelativeMotionEvent,
|
2022-10-26 15:26:07 +02:00
|
|
|
},
|
2024-04-04 19:17:03 -07:00
|
|
|
touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle},
|
2022-10-26 15:26:07 +02:00
|
|
|
Seat,
|
|
|
|
|
},
|
|
|
|
|
output::Output,
|
2024-04-24 09:34:46 -07:00
|
|
|
utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial, SERIAL_COUNTER},
|
2023-07-18 12:21:31 +02:00
|
|
|
};
|
2024-06-07 19:26:23 +02:00
|
|
|
use std::{
|
|
|
|
|
collections::HashSet,
|
|
|
|
|
sync::{atomic::Ordering, Mutex},
|
|
|
|
|
time::Instant,
|
|
|
|
|
};
|
2022-10-26 15:26:07 +02:00
|
|
|
|
2024-04-04 19:17:03 -07:00
|
|
|
use super::{GrabStartData, ReleaseMode};
|
2023-12-07 19:49:53 +00:00
|
|
|
|
2024-06-07 19:26:23 +02:00
|
|
|
pub type SeatMoveGrabState = Mutex<Option<MoveGrabState>>;
|
2022-10-26 15:26:07 +02:00
|
|
|
|
2023-07-18 12:21:31 +02:00
|
|
|
const RESCALE_ANIMATION_DURATION: f64 = 150.0;
|
|
|
|
|
|
2022-10-26 15:26:07 +02:00
|
|
|
pub struct MoveGrabState {
|
|
|
|
|
window: CosmicMapped,
|
2023-03-06 18:50:59 +01:00
|
|
|
window_offset: Point<i32, Logical>,
|
2023-03-09 18:27:11 +01:00
|
|
|
indicator_thickness: u8,
|
2023-07-18 12:21:31 +02:00
|
|
|
start: Instant,
|
2023-12-20 20:29:44 +00:00
|
|
|
previous: ManagedLayer,
|
2024-03-18 00:12:58 -05:00
|
|
|
snapping_zone: Option<SnappingZone>,
|
2023-07-24 19:31:31 +02:00
|
|
|
stacking_indicator: Option<(StackHover, Point<i32, Logical>)>,
|
2024-04-04 19:17:03 -07:00
|
|
|
location: Point<f64, Logical>,
|
2024-04-11 13:47:37 -07:00
|
|
|
cursor_output: Output,
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MoveGrabState {
|
2023-10-07 19:15:44 -07:00
|
|
|
#[profiling::function]
|
2024-04-11 13:47:37 -07:00
|
|
|
pub fn render<I, R>(&self, renderer: &mut R, output: &Output, theme: &CosmicTheme) -> Vec<I>
|
2022-10-26 15:26:07 +02:00
|
|
|
where
|
2023-01-16 15:12:25 +01:00
|
|
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
2025-03-11 19:14:49 +01:00
|
|
|
R::TextureId: Send + Clone + 'static,
|
2022-11-22 10:28:30 +01:00
|
|
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
2022-10-26 15:26:07 +02:00
|
|
|
I: From<CosmicMappedRenderElement<R>>,
|
|
|
|
|
{
|
2023-12-20 20:29:44 +00:00
|
|
|
let scale = if self.previous == ManagedLayer::Tiling {
|
2023-07-25 16:46:08 +02:00
|
|
|
0.6 + ((1.0
|
2023-07-18 12:21:31 +02:00
|
|
|
- (Instant::now().duration_since(self.start).as_millis() as f64
|
|
|
|
|
/ RESCALE_ANIMATION_DURATION)
|
|
|
|
|
.min(1.0))
|
2023-07-25 16:46:08 +02:00
|
|
|
* 0.4)
|
|
|
|
|
} else {
|
|
|
|
|
1.0
|
|
|
|
|
};
|
2024-04-11 13:47:37 -07:00
|
|
|
let alpha = if &self.cursor_output == output {
|
2023-10-11 19:16:51 +02:00
|
|
|
1.0
|
|
|
|
|
} else {
|
|
|
|
|
0.4
|
|
|
|
|
};
|
2023-07-18 12:21:31 +02:00
|
|
|
|
2022-10-26 15:26:07 +02:00
|
|
|
let mut window_geo = self.window.geometry();
|
2024-04-04 19:17:03 -07:00
|
|
|
window_geo.loc += self.location.to_i32_round() + self.window_offset;
|
2025-10-16 13:50:32 +02:00
|
|
|
if output
|
2023-10-25 19:24:51 +02:00
|
|
|
.geometry()
|
|
|
|
|
.as_logical()
|
2025-10-16 13:50:32 +02:00
|
|
|
.intersection(window_geo).is_none()
|
2023-10-25 19:24:51 +02:00
|
|
|
{
|
2022-10-26 15:26:07 +02:00
|
|
|
return Vec::new();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-31 18:17:37 +02:00
|
|
|
let output_scale: Scale<f64> = output.current_scale().fractional_scale().into();
|
2023-07-18 12:21:31 +02:00
|
|
|
let scaling_offset =
|
|
|
|
|
self.window_offset - self.window_offset.to_f64().upscale(scale).to_i32_round();
|
2024-04-04 19:17:03 -07:00
|
|
|
let render_location = self.location.to_i32_round() - output.geometry().loc.as_logical()
|
2023-10-25 19:24:51 +02:00
|
|
|
+ self.window_offset
|
|
|
|
|
- scaling_offset;
|
2023-03-06 18:50:59 +01:00
|
|
|
|
2023-10-10 13:55:34 -04:00
|
|
|
let active_window_hint = crate::theme::active_window_hint(theme);
|
2025-09-24 15:11:16 -04:00
|
|
|
let radius = self
|
2025-10-03 12:28:57 -04:00
|
|
|
.element()
|
|
|
|
|
.corner_radius(window_geo.size, self.indicator_thickness);
|
2025-09-24 15:11:16 -04:00
|
|
|
|
2023-07-13 17:19:29 +02:00
|
|
|
let focus_element = if self.indicator_thickness > 0 {
|
|
|
|
|
Some(
|
2023-05-30 13:20:46 +02:00
|
|
|
CosmicMappedRenderElement::from(IndicatorShader::focus_element(
|
2023-03-09 18:27:11 +01:00
|
|
|
renderer,
|
2024-06-07 19:15:16 +02:00
|
|
|
Key::Window(Usage::MoveGrabIndicator, self.window.key()),
|
2024-12-26 18:18:35 -08:00
|
|
|
Rectangle::new(
|
2023-07-18 12:21:31 +02:00
|
|
|
render_location,
|
|
|
|
|
self.window
|
|
|
|
|
.geometry()
|
|
|
|
|
.size
|
|
|
|
|
.to_f64()
|
|
|
|
|
.upscale(scale)
|
|
|
|
|
.to_i32_round(),
|
2023-10-25 19:24:51 +02:00
|
|
|
)
|
|
|
|
|
.as_local(),
|
2023-03-09 18:27:11 +01:00
|
|
|
self.indicator_thickness,
|
2025-09-24 15:11:16 -04:00
|
|
|
radius,
|
2023-10-11 19:16:51 +02:00
|
|
|
alpha,
|
2023-10-10 13:55:34 -04:00
|
|
|
[
|
|
|
|
|
active_window_hint.red,
|
|
|
|
|
active_window_hint.green,
|
|
|
|
|
active_window_hint.blue,
|
|
|
|
|
],
|
2025-10-16 13:50:32 +02:00
|
|
|
)),
|
2023-07-13 17:19:29 +02:00
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-18 00:12:58 -05:00
|
|
|
let non_exclusive_geometry = {
|
2025-10-16 13:50:32 +02:00
|
|
|
let layers = layer_map_for_output(output);
|
2024-03-18 00:12:58 -05:00
|
|
|
layers.non_exclusive_zone()
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-19 20:38:14 -05:00
|
|
|
let gaps = (theme.gaps.0 as i32, theme.gaps.1 as i32);
|
2024-11-05 20:58:49 -06:00
|
|
|
let thickness = self.indicator_thickness.max(1);
|
2024-04-19 20:38:14 -05:00
|
|
|
|
2024-03-18 00:12:58 -05:00
|
|
|
let snapping_indicator = match &self.snapping_zone {
|
2024-04-11 13:47:37 -07:00
|
|
|
Some(t) if &self.cursor_output == output => {
|
2024-03-21 11:39:16 -05:00
|
|
|
let base_color = theme.palette.neutral_9;
|
2024-04-19 20:38:14 -05:00
|
|
|
let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps);
|
2024-03-21 11:39:16 -05:00
|
|
|
vec![
|
|
|
|
|
CosmicMappedRenderElement::from(IndicatorShader::element(
|
|
|
|
|
renderer,
|
2024-06-07 19:15:16 +02:00
|
|
|
Key::Window(Usage::SnappingIndicator, self.window.key()),
|
2024-03-21 11:39:16 -05:00
|
|
|
overlay_geometry,
|
2024-11-05 20:58:49 -06:00
|
|
|
thickness,
|
2025-09-24 17:50:23 -04:00
|
|
|
[
|
|
|
|
|
theme.radius_s()[0] as u8,
|
|
|
|
|
theme.radius_s()[1] as u8,
|
|
|
|
|
theme.radius_s()[2] as u8,
|
|
|
|
|
theme.radius_s()[3] as u8,
|
|
|
|
|
],
|
2024-03-21 11:39:16 -05:00
|
|
|
1.0,
|
|
|
|
|
[
|
|
|
|
|
active_window_hint.red,
|
|
|
|
|
active_window_hint.green,
|
|
|
|
|
active_window_hint.blue,
|
|
|
|
|
],
|
2025-10-16 13:50:32 +02:00
|
|
|
)),
|
2024-03-21 11:39:16 -05:00
|
|
|
CosmicMappedRenderElement::from(BackdropShader::element(
|
|
|
|
|
renderer,
|
2024-06-07 19:15:16 +02:00
|
|
|
Key::Window(Usage::SnappingIndicator, self.window.key()),
|
2024-04-19 20:38:14 -05:00
|
|
|
t.overlay_geometry(non_exclusive_geometry, gaps),
|
2024-03-21 11:39:16 -05:00
|
|
|
theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization
|
|
|
|
|
0.4,
|
|
|
|
|
[base_color.red, base_color.green, base_color.blue],
|
2025-10-16 13:50:32 +02:00
|
|
|
)),
|
2024-03-21 11:39:16 -05:00
|
|
|
]
|
|
|
|
|
}
|
2024-04-10 19:43:18 -07:00
|
|
|
_ => vec![],
|
2024-03-18 00:12:58 -05:00
|
|
|
};
|
|
|
|
|
|
2024-09-27 23:41:58 +02:00
|
|
|
let w_elements = self
|
2023-07-18 12:21:31 +02:00
|
|
|
.window
|
2024-09-27 23:41:58 +02:00
|
|
|
.render_elements::<R, CosmicMappedRenderElement<R>>(
|
|
|
|
|
renderer,
|
|
|
|
|
(render_location - self.window.geometry().loc)
|
|
|
|
|
.to_physical_precise_round(output_scale),
|
|
|
|
|
output_scale,
|
|
|
|
|
alpha,
|
2025-07-22 16:39:33 +02:00
|
|
|
Some(false),
|
2024-09-27 23:41:58 +02:00
|
|
|
);
|
|
|
|
|
let p_elements = self
|
|
|
|
|
.window
|
|
|
|
|
.popup_render_elements::<R, CosmicMappedRenderElement<R>>(
|
2023-07-18 12:21:31 +02:00
|
|
|
renderer,
|
|
|
|
|
(render_location - self.window.geometry().loc)
|
|
|
|
|
.to_physical_precise_round(output_scale),
|
|
|
|
|
output_scale,
|
2023-10-11 19:16:51 +02:00
|
|
|
alpha,
|
2023-07-18 12:21:31 +02:00
|
|
|
);
|
2023-07-13 17:19:29 +02:00
|
|
|
|
2023-07-24 19:31:31 +02:00
|
|
|
self.stacking_indicator
|
|
|
|
|
.iter()
|
|
|
|
|
.flat_map(|(indicator, location)| {
|
|
|
|
|
indicator.render_elements(
|
|
|
|
|
renderer,
|
2023-07-26 16:13:48 +02:00
|
|
|
location.to_physical_precise_round(output_scale),
|
2023-07-24 19:31:31 +02:00
|
|
|
output_scale,
|
|
|
|
|
1.0,
|
|
|
|
|
)
|
|
|
|
|
})
|
2024-07-09 15:21:16 -07:00
|
|
|
.chain(p_elements)
|
2023-07-13 17:19:29 +02:00
|
|
|
.chain(focus_element)
|
2024-07-09 15:21:16 -07:00
|
|
|
.chain(w_elements.into_iter().map(|elem| match elem {
|
2023-07-18 12:21:31 +02:00
|
|
|
CosmicMappedRenderElement::Stack(stack) => {
|
|
|
|
|
CosmicMappedRenderElement::GrabbedStack(
|
|
|
|
|
RescaleRenderElement::from_element(
|
|
|
|
|
stack,
|
|
|
|
|
render_location.to_physical_precise_round(
|
|
|
|
|
output.current_scale().fractional_scale(),
|
|
|
|
|
),
|
|
|
|
|
scale,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
CosmicMappedRenderElement::Window(window) => {
|
|
|
|
|
CosmicMappedRenderElement::GrabbedWindow(
|
|
|
|
|
RescaleRenderElement::from_element(
|
|
|
|
|
window,
|
|
|
|
|
render_location.to_physical_precise_round(
|
|
|
|
|
output.current_scale().fractional_scale(),
|
|
|
|
|
),
|
|
|
|
|
scale,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
x => x,
|
|
|
|
|
}))
|
2024-03-18 00:12:58 -05:00
|
|
|
.chain(snapping_indicator)
|
2023-07-18 12:21:31 +02:00
|
|
|
.map(I::from)
|
2023-07-13 17:19:29 +02:00
|
|
|
.collect()
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
2023-02-13 17:56:13 +01:00
|
|
|
|
2024-03-21 12:53:52 +01:00
|
|
|
pub fn element(&self) -> CosmicMapped {
|
|
|
|
|
self.window.clone()
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-31 13:57:37 +02:00
|
|
|
pub fn window(&self) -> CosmicSurface {
|
|
|
|
|
self.window.active_window()
|
|
|
|
|
}
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-11 16:11:07 +00:00
|
|
|
struct NotSend<T>(pub T);
|
|
|
|
|
unsafe impl<T> Send for NotSend<T> {}
|
|
|
|
|
|
2024-03-18 00:12:58 -05:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub enum SnappingZone {
|
2024-03-21 11:39:16 -05:00
|
|
|
Maximize,
|
|
|
|
|
Top,
|
|
|
|
|
TopLeft,
|
2024-03-18 00:12:58 -05:00
|
|
|
Left,
|
2024-03-21 11:39:16 -05:00
|
|
|
BottomLeft,
|
2024-03-18 00:12:58 -05:00
|
|
|
Bottom,
|
2024-03-21 11:39:16 -05:00
|
|
|
BottomRight,
|
|
|
|
|
Right,
|
|
|
|
|
TopRight,
|
2024-03-18 00:12:58 -05:00
|
|
|
}
|
|
|
|
|
|
2024-03-21 11:39:16 -05:00
|
|
|
const SNAP_RANGE: i32 = 32;
|
2024-04-09 17:50:52 -05:00
|
|
|
const SNAP_RANGE_MAXIMIZE: i32 = 22;
|
|
|
|
|
const SNAP_RANGE_TOP: i32 = 16;
|
2024-03-18 00:12:58 -05:00
|
|
|
|
|
|
|
|
impl SnappingZone {
|
|
|
|
|
pub fn contains(
|
|
|
|
|
&self,
|
2024-03-21 11:39:16 -05:00
|
|
|
point: Point<i32, Local>,
|
|
|
|
|
output_geometry: Rectangle<i32, Local>,
|
2024-03-18 00:12:58 -05:00
|
|
|
) -> bool {
|
|
|
|
|
if !output_geometry.contains(point) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-04-09 17:50:52 -05:00
|
|
|
let top_zone_32 = point.y < output_geometry.loc.y + SNAP_RANGE_MAXIMIZE;
|
|
|
|
|
let top_zone_56 = point.y < output_geometry.loc.y + SNAP_RANGE_MAXIMIZE + SNAP_RANGE_TOP;
|
2024-03-21 11:39:16 -05:00
|
|
|
let left_zone = point.x < output_geometry.loc.x + SNAP_RANGE;
|
|
|
|
|
let right_zone = point.x > output_geometry.loc.x + output_geometry.size.w - SNAP_RANGE;
|
|
|
|
|
let bottom_zone = point.y > output_geometry.loc.y + output_geometry.size.h - SNAP_RANGE;
|
|
|
|
|
let left_6th = point.x < output_geometry.loc.x + (output_geometry.size.w / 6);
|
|
|
|
|
let right_6th = point.x > output_geometry.loc.x + (output_geometry.size.w * 5 / 6);
|
|
|
|
|
let top_4th = point.y < output_geometry.loc.y + (output_geometry.size.h / 4);
|
|
|
|
|
let bottom_4th = point.y > output_geometry.loc.y + (output_geometry.size.h * 3 / 4);
|
2024-03-18 00:12:58 -05:00
|
|
|
match self {
|
2024-03-21 11:39:16 -05:00
|
|
|
SnappingZone::Maximize => top_zone_32 && !left_6th && !right_6th,
|
|
|
|
|
SnappingZone::Top => top_zone_56 && !top_zone_32 && !left_6th && !right_6th,
|
|
|
|
|
SnappingZone::TopLeft => (top_zone_56 && left_6th) || (left_zone && top_4th),
|
|
|
|
|
SnappingZone::Left => left_zone && !top_4th && !bottom_4th,
|
|
|
|
|
SnappingZone::BottomLeft => (bottom_zone && left_6th) || (left_zone && bottom_4th),
|
|
|
|
|
SnappingZone::Bottom => bottom_zone && !left_6th && !right_6th,
|
|
|
|
|
SnappingZone::BottomRight => (bottom_zone && right_6th) || (right_zone && bottom_4th),
|
|
|
|
|
SnappingZone::Right => right_zone && !top_4th && !bottom_4th,
|
|
|
|
|
SnappingZone::TopRight => (top_zone_56 && right_6th) || (right_zone && top_4th),
|
2024-03-18 00:12:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn overlay_geometry(
|
|
|
|
|
&self,
|
|
|
|
|
non_exclusive_geometry: Rectangle<i32, Logical>,
|
2024-04-19 20:38:14 -05:00
|
|
|
gaps: (i32, i32),
|
2024-03-18 00:12:58 -05:00
|
|
|
) -> Rectangle<i32, Local> {
|
|
|
|
|
match self {
|
2024-03-21 11:39:16 -05:00
|
|
|
SnappingZone::Maximize => non_exclusive_geometry.as_local(),
|
2024-04-19 20:38:14 -05:00
|
|
|
SnappingZone::Top => TiledCorners::Top.relative_geometry(non_exclusive_geometry, gaps),
|
2024-03-21 11:39:16 -05:00
|
|
|
SnappingZone::TopLeft => {
|
2024-04-19 20:38:14 -05:00
|
|
|
TiledCorners::TopLeft.relative_geometry(non_exclusive_geometry, gaps)
|
|
|
|
|
}
|
|
|
|
|
SnappingZone::Left => {
|
|
|
|
|
TiledCorners::Left.relative_geometry(non_exclusive_geometry, gaps)
|
2024-03-21 11:39:16 -05:00
|
|
|
}
|
|
|
|
|
SnappingZone::BottomLeft => {
|
2024-04-19 20:38:14 -05:00
|
|
|
TiledCorners::BottomLeft.relative_geometry(non_exclusive_geometry, gaps)
|
|
|
|
|
}
|
|
|
|
|
SnappingZone::Bottom => {
|
|
|
|
|
TiledCorners::Bottom.relative_geometry(non_exclusive_geometry, gaps)
|
2024-03-21 11:39:16 -05:00
|
|
|
}
|
|
|
|
|
SnappingZone::BottomRight => {
|
2024-04-19 20:38:14 -05:00
|
|
|
TiledCorners::BottomRight.relative_geometry(non_exclusive_geometry, gaps)
|
|
|
|
|
}
|
|
|
|
|
SnappingZone::Right => {
|
|
|
|
|
TiledCorners::Right.relative_geometry(non_exclusive_geometry, gaps)
|
2024-03-21 11:39:16 -05:00
|
|
|
}
|
|
|
|
|
SnappingZone::TopRight => {
|
2024-04-19 20:38:14 -05:00
|
|
|
TiledCorners::TopRight.relative_geometry(non_exclusive_geometry, gaps)
|
2024-03-21 11:39:16 -05:00
|
|
|
}
|
2024-03-18 00:12:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-17 21:11:23 +02:00
|
|
|
pub struct MoveGrab {
|
2022-10-26 15:26:07 +02:00
|
|
|
window: CosmicMapped,
|
2024-04-04 19:17:03 -07:00
|
|
|
start_data: GrabStartData,
|
2022-10-26 15:26:07 +02:00
|
|
|
seat: Seat<State>,
|
2023-07-31 17:25:09 +02:00
|
|
|
cursor_output: Output,
|
|
|
|
|
window_outputs: HashSet<Output>,
|
2023-12-20 20:29:44 +00:00
|
|
|
previous: ManagedLayer,
|
2023-12-07 19:49:53 +00:00
|
|
|
release: ReleaseMode,
|
2025-02-14 22:17:17 +11:00
|
|
|
edge_snap_threshold: f64,
|
2023-12-11 16:11:07 +00:00
|
|
|
// SAFETY: This is only used on drop which will always be on the main thread
|
|
|
|
|
evlh: NotSend<LoopHandle<'static, State>>,
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-04-04 19:17:03 -07:00
|
|
|
impl MoveGrab {
|
|
|
|
|
fn update_location(&mut self, state: &mut State, location: Point<f64, Logical>) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = state.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
|
2024-12-26 18:18:35 -08:00
|
|
|
let Some(current_output) = shell
|
|
|
|
|
.outputs()
|
|
|
|
|
.find(|output| {
|
|
|
|
|
output
|
|
|
|
|
.geometry()
|
|
|
|
|
.as_logical()
|
|
|
|
|
.overlaps_or_touches(Rectangle::new(location.to_i32_floor(), (0, 0).into()))
|
|
|
|
|
})
|
|
|
|
|
.cloned()
|
2023-07-31 17:25:09 +02:00
|
|
|
else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
if self.cursor_output != current_output {
|
2024-04-10 15:49:08 +02:00
|
|
|
shell
|
2023-07-31 17:25:09 +02:00
|
|
|
.workspaces
|
|
|
|
|
.active_mut(&self.cursor_output)
|
2025-01-06 19:23:06 +01:00
|
|
|
.unwrap()
|
2023-07-31 17:25:09 +02:00
|
|
|
.tiling_layer
|
2023-10-25 19:40:26 +02:00
|
|
|
.cleanup_drag();
|
2023-07-31 17:25:09 +02:00
|
|
|
self.cursor_output = current_output.clone();
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 19:31:31 +02:00
|
|
|
let mut borrow = self
|
2023-03-06 18:50:59 +01:00
|
|
|
.seat
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<SeatMoveGrabState>()
|
2024-06-07 19:26:23 +02:00
|
|
|
.map(|s| s.lock().unwrap());
|
2023-07-24 19:31:31 +02:00
|
|
|
if let Some(grab_state) = borrow.as_mut().and_then(|s| s.as_mut()) {
|
2024-04-04 19:17:03 -07:00
|
|
|
grab_state.location = location;
|
2024-04-11 13:47:37 -07:00
|
|
|
grab_state.cursor_output = self.cursor_output.clone();
|
2024-04-04 19:17:03 -07:00
|
|
|
|
2023-03-06 18:50:59 +01:00
|
|
|
let mut window_geo = self.window.geometry();
|
2024-04-04 19:17:03 -07:00
|
|
|
window_geo.loc += location.to_i32_round() + grab_state.window_offset;
|
2025-02-14 21:58:09 +11:00
|
|
|
|
|
|
|
|
if matches!(self.previous, ManagedLayer::Floating | ManagedLayer::Sticky) {
|
2025-07-18 21:02:09 +10:00
|
|
|
let loc = grab_state.window_offset.to_f64() + grab_state.location;
|
|
|
|
|
let size = window_geo.size.to_f64();
|
|
|
|
|
let output_geom = self.cursor_output.geometry().to_f64().as_logical();
|
2025-02-14 21:58:09 +11:00
|
|
|
let output_loc = output_geom.loc;
|
|
|
|
|
let output_size = output_geom.size;
|
|
|
|
|
|
2025-02-14 22:17:17 +11:00
|
|
|
grab_state.location.x = if (loc.x - output_loc.x).abs() < self.edge_snap_threshold {
|
2025-02-14 21:58:09 +11:00
|
|
|
output_loc.x - grab_state.window_offset.x as f64
|
|
|
|
|
} else if ((loc.x + size.w) - (output_loc.x + output_size.w)).abs()
|
2025-02-14 22:17:17 +11:00
|
|
|
< self.edge_snap_threshold
|
2025-02-14 21:58:09 +11:00
|
|
|
{
|
|
|
|
|
output_loc.x + output_size.w - grab_state.window_offset.x as f64 - size.w
|
|
|
|
|
} else {
|
|
|
|
|
grab_state.location.x
|
|
|
|
|
};
|
2025-02-14 22:17:17 +11:00
|
|
|
grab_state.location.y = if (loc.y - output_loc.y).abs() < self.edge_snap_threshold {
|
2025-02-14 21:58:09 +11:00
|
|
|
output_loc.y - grab_state.window_offset.y as f64
|
|
|
|
|
} else if ((loc.y + size.h) - (output_loc.y + output_size.h)).abs()
|
2025-02-14 22:17:17 +11:00
|
|
|
< self.edge_snap_threshold
|
2025-02-14 21:58:09 +11:00
|
|
|
{
|
|
|
|
|
output_loc.y + output_size.h - grab_state.window_offset.y as f64 - size.h
|
|
|
|
|
} else {
|
|
|
|
|
grab_state.location.y
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-10 15:49:08 +02:00
|
|
|
for output in shell.outputs() {
|
2023-10-25 19:24:51 +02:00
|
|
|
if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) {
|
2023-07-31 17:25:09 +02:00
|
|
|
if self.window_outputs.insert(output.clone()) {
|
2023-03-06 18:50:59 +01:00
|
|
|
self.window.output_enter(output, overlap);
|
2023-07-24 19:31:31 +02:00
|
|
|
if let Some(indicator) =
|
|
|
|
|
grab_state.stacking_indicator.as_ref().map(|x| &x.0)
|
|
|
|
|
{
|
|
|
|
|
indicator.output_enter(output, overlap);
|
|
|
|
|
}
|
2023-03-06 18:50:59 +01:00
|
|
|
}
|
2025-10-16 13:50:32 +02:00
|
|
|
} else if self.window_outputs.remove(output) {
|
2023-03-06 18:50:59 +01:00
|
|
|
self.window.output_leave(output);
|
2023-07-24 19:31:31 +02:00
|
|
|
if let Some(indicator) = grab_state.stacking_indicator.as_ref().map(|x| &x.0) {
|
|
|
|
|
indicator.output_leave(output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
let indicator_location =
|
2025-10-16 13:50:32 +02:00
|
|
|
shell.stacking_indicator(¤t_output, self.previous);
|
2024-01-08 21:37:06 +00:00
|
|
|
if indicator_location.is_some() != grab_state.stacking_indicator.is_some() {
|
|
|
|
|
grab_state.stacking_indicator = indicator_location.map(|geo| {
|
|
|
|
|
let element = stack_hover(
|
|
|
|
|
state.common.event_loop_handle.clone(),
|
|
|
|
|
geo.size.as_logical(),
|
|
|
|
|
state.common.theme.clone(),
|
|
|
|
|
);
|
|
|
|
|
for output in &self.window_outputs {
|
|
|
|
|
element.output_enter(
|
|
|
|
|
output,
|
2024-12-26 18:18:35 -08:00
|
|
|
Rectangle::from_size(output.geometry().size.as_logical()),
|
2023-10-25 19:24:51 +02:00
|
|
|
);
|
2024-01-08 21:37:06 +00:00
|
|
|
}
|
|
|
|
|
(element, geo.loc.as_logical())
|
|
|
|
|
});
|
2023-03-06 18:50:59 +01:00
|
|
|
}
|
2024-03-18 00:12:58 -05:00
|
|
|
|
|
|
|
|
// Check for overlapping with zones
|
|
|
|
|
if grab_state.previous == ManagedLayer::Floating {
|
2024-03-21 11:39:16 -05:00
|
|
|
let output_geometry = current_output.geometry().to_local(¤t_output);
|
2024-08-29 14:11:28 +01:00
|
|
|
grab_state.snapping_zone = [
|
2024-03-21 11:39:16 -05:00
|
|
|
SnappingZone::Maximize,
|
|
|
|
|
SnappingZone::Top,
|
|
|
|
|
SnappingZone::TopLeft,
|
2024-03-18 00:12:58 -05:00
|
|
|
SnappingZone::Left,
|
2024-03-21 11:39:16 -05:00
|
|
|
SnappingZone::BottomLeft,
|
2024-03-18 00:12:58 -05:00
|
|
|
SnappingZone::Bottom,
|
2024-03-21 11:39:16 -05:00
|
|
|
SnappingZone::BottomRight,
|
|
|
|
|
SnappingZone::Right,
|
|
|
|
|
SnappingZone::TopRight,
|
2024-03-18 00:12:58 -05:00
|
|
|
]
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|&x| {
|
|
|
|
|
x.contains(
|
2024-04-04 19:17:03 -07:00
|
|
|
location
|
2024-03-21 11:39:16 -05:00
|
|
|
.as_global()
|
|
|
|
|
.to_local(¤t_output)
|
|
|
|
|
.to_i32_floor(),
|
|
|
|
|
output_geometry,
|
2024-03-18 00:12:58 -05:00
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.cloned();
|
|
|
|
|
}
|
2023-03-06 18:50:59 +01:00
|
|
|
}
|
|
|
|
|
drop(borrow);
|
2024-04-04 19:17:03 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PointerGrab<State> for MoveGrab {
|
|
|
|
|
fn motion(
|
|
|
|
|
&mut self,
|
|
|
|
|
state: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
2024-06-18 19:23:16 -07:00
|
|
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
2024-04-04 19:17:03 -07:00
|
|
|
event: &MotionEvent,
|
|
|
|
|
) {
|
|
|
|
|
self.update_location(state, event.location);
|
2023-03-06 18:50:59 +01:00
|
|
|
|
2022-10-26 15:26:07 +02:00
|
|
|
// While the grab is active, no client has pointer focus
|
|
|
|
|
handle.motion(state, None, event);
|
|
|
|
|
if !self.window.alive() {
|
2024-04-16 12:05:22 -07:00
|
|
|
handle.unset_grab(self, state, event.serial, event.time, true);
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 23:19:36 +01:00
|
|
|
fn relative_motion(
|
|
|
|
|
&mut self,
|
|
|
|
|
state: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
2024-06-18 19:23:16 -07:00
|
|
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
2023-01-30 23:19:36 +01:00
|
|
|
event: &RelativeMotionEvent,
|
|
|
|
|
) {
|
|
|
|
|
// While the grab is active, no client has pointer focus
|
|
|
|
|
handle.relative_motion(state, None, event);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-26 15:26:07 +02:00
|
|
|
fn button(
|
|
|
|
|
&mut self,
|
|
|
|
|
state: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &ButtonEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.button(state, event);
|
2023-12-07 19:49:53 +00:00
|
|
|
match self.release {
|
|
|
|
|
ReleaseMode::NoMouseButtons => {
|
|
|
|
|
if handle.current_pressed().is_empty() {
|
2024-04-16 12:05:22 -07:00
|
|
|
handle.unset_grab(self, state, event.serial, event.time, true);
|
2023-12-07 19:49:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ReleaseMode::Click => {
|
|
|
|
|
if event.state == ButtonState::Pressed {
|
2024-04-16 12:05:22 -07:00
|
|
|
handle.unset_grab(self, state, event.serial, event.time, true);
|
2023-12-07 19:49:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn axis(
|
|
|
|
|
&mut self,
|
|
|
|
|
state: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
details: AxisFrame,
|
|
|
|
|
) {
|
|
|
|
|
handle.axis(state, details);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-13 20:52:10 -07:00
|
|
|
fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {
|
|
|
|
|
handle.frame(data)
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 10:55:23 -07:00
|
|
|
fn gesture_swipe_begin(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GestureSwipeBeginEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_swipe_begin(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_swipe_update(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GestureSwipeUpdateEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_swipe_update(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_swipe_end(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GestureSwipeEndEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_swipe_end(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_pinch_begin(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GesturePinchBeginEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_pinch_begin(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_pinch_update(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GesturePinchUpdateEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_pinch_update(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_pinch_end(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GesturePinchEndEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_pinch_end(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_hold_begin(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GestureHoldBeginEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_hold_begin(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gesture_hold_end(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut PointerInnerHandle<'_, State>,
|
|
|
|
|
event: &GestureHoldEndEvent,
|
|
|
|
|
) {
|
|
|
|
|
handle.gesture_hold_end(data, event)
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-26 15:26:07 +02:00
|
|
|
fn start_data(&self) -> &PointerGrabStartData<State> {
|
2024-04-04 19:17:03 -07:00
|
|
|
match &self.start_data {
|
|
|
|
|
GrabStartData::Pointer(start_data) => start_data,
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-12 09:48:29 -07:00
|
|
|
|
|
|
|
|
fn unset(&mut self, _data: &mut State) {}
|
2024-04-04 19:17:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TouchGrab<State> for MoveGrab {
|
|
|
|
|
fn down(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut TouchInnerHandle<'_, State>,
|
2024-06-18 19:23:16 -07:00
|
|
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
2024-04-04 19:17:03 -07:00
|
|
|
event: &touch::DownEvent,
|
|
|
|
|
seq: Serial,
|
|
|
|
|
) {
|
|
|
|
|
handle.down(data, None, event, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn up(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut TouchInnerHandle<'_, State>,
|
|
|
|
|
event: &touch::UpEvent,
|
|
|
|
|
seq: Serial,
|
|
|
|
|
) {
|
|
|
|
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
2024-04-16 12:05:22 -07:00
|
|
|
handle.unset_grab(self, data);
|
2024-04-04 19:17:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handle.up(data, event, seq);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn motion(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut TouchInnerHandle<'_, State>,
|
2024-06-18 19:23:16 -07:00
|
|
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
2024-04-04 19:17:03 -07:00
|
|
|
event: &touch::MotionEvent,
|
|
|
|
|
seq: Serial,
|
|
|
|
|
) {
|
|
|
|
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
|
|
|
|
self.update_location(data, event.location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
2024-04-16 12:05:22 -07:00
|
|
|
handle.unset_grab(self, data);
|
2024-04-04 19:17:03 -07:00
|
|
|
}
|
|
|
|
|
|
2024-05-13 14:16:21 -07:00
|
|
|
fn shape(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut TouchInnerHandle<'_, State>,
|
|
|
|
|
event: &touch::ShapeEvent,
|
|
|
|
|
seq: Serial,
|
|
|
|
|
) {
|
|
|
|
|
handle.shape(data, event, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn orientation(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut State,
|
|
|
|
|
handle: &mut TouchInnerHandle<'_, State>,
|
|
|
|
|
event: &touch::OrientationEvent,
|
|
|
|
|
seq: Serial,
|
|
|
|
|
) {
|
|
|
|
|
handle.orientation(data, event, seq)
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-04 19:17:03 -07:00
|
|
|
fn start_data(&self) -> &TouchGrabStartData<State> {
|
|
|
|
|
match &self.start_data {
|
|
|
|
|
GrabStartData::Touch(start_data) => start_data,
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
2024-04-12 09:48:29 -07:00
|
|
|
|
|
|
|
|
fn unset(&mut self, _data: &mut State) {}
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-17 21:11:23 +02:00
|
|
|
impl MoveGrab {
|
2022-10-26 15:26:07 +02:00
|
|
|
pub fn new(
|
2024-04-04 19:17:03 -07:00
|
|
|
start_data: GrabStartData,
|
2022-10-26 15:26:07 +02:00
|
|
|
window: CosmicMapped,
|
|
|
|
|
seat: &Seat<State>,
|
2023-10-25 19:24:51 +02:00
|
|
|
initial_window_location: Point<i32, Global>,
|
2024-04-11 13:47:37 -07:00
|
|
|
cursor_output: Output,
|
2023-03-09 18:27:11 +01:00
|
|
|
indicator_thickness: u8,
|
2025-02-14 22:17:17 +11:00
|
|
|
edge_snap_threshold: f64,
|
2023-12-20 20:29:44 +00:00
|
|
|
previous_layer: ManagedLayer,
|
2023-12-07 19:49:53 +00:00
|
|
|
release: ReleaseMode,
|
2023-12-11 16:11:07 +00:00
|
|
|
evlh: LoopHandle<'static, State>,
|
2023-07-17 21:11:23 +02:00
|
|
|
) -> MoveGrab {
|
2023-03-06 18:50:59 +01:00
|
|
|
let mut outputs = HashSet::new();
|
2024-04-11 13:47:37 -07:00
|
|
|
outputs.insert(cursor_output.clone());
|
|
|
|
|
window.output_enter(&cursor_output, window.geometry()); // not accurate but...
|
2023-11-08 22:59:46 +01:00
|
|
|
window.moved_since_mapped.store(true, Ordering::SeqCst);
|
2023-03-06 18:50:59 +01:00
|
|
|
|
2022-10-26 15:26:07 +02:00
|
|
|
let grab_state = MoveGrabState {
|
|
|
|
|
window: window.clone(),
|
2024-04-04 19:17:03 -07:00
|
|
|
window_offset: (initial_window_location
|
|
|
|
|
- start_data.location().as_global().to_i32_round())
|
|
|
|
|
.as_logical(),
|
2023-03-09 18:27:11 +01:00
|
|
|
indicator_thickness,
|
2023-07-18 12:21:31 +02:00
|
|
|
start: Instant::now(),
|
2023-07-24 19:31:31 +02:00
|
|
|
stacking_indicator: None,
|
2024-03-18 00:12:58 -05:00
|
|
|
snapping_zone: None,
|
2025-10-16 13:50:32 +02:00
|
|
|
previous: previous_layer,
|
2024-04-04 19:17:03 -07:00
|
|
|
location: start_data.location(),
|
2024-04-11 13:47:37 -07:00
|
|
|
cursor_output: cursor_output.clone(),
|
2022-10-26 15:26:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
*seat
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<SeatMoveGrabState>()
|
|
|
|
|
.unwrap()
|
2024-06-07 19:26:23 +02:00
|
|
|
.lock()
|
|
|
|
|
.unwrap() = Some(grab_state);
|
2022-10-26 15:26:07 +02:00
|
|
|
|
2023-07-18 12:21:16 +02:00
|
|
|
{
|
|
|
|
|
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
2024-10-05 20:17:44 +02:00
|
|
|
cursor_state.lock().unwrap().set_shape(CursorIcon::Grabbing);
|
2023-07-18 12:21:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-17 21:11:23 +02:00
|
|
|
MoveGrab {
|
2022-10-26 15:26:07 +02:00
|
|
|
window,
|
|
|
|
|
start_data,
|
|
|
|
|
seat: seat.clone(),
|
2024-04-11 13:47:37 -07:00
|
|
|
cursor_output,
|
2025-02-14 21:58:09 +11:00
|
|
|
window_outputs: outputs,
|
2023-12-20 20:29:44 +00:00
|
|
|
previous: previous_layer,
|
2023-12-07 19:49:53 +00:00
|
|
|
release,
|
2025-02-14 22:17:17 +11:00
|
|
|
edge_snap_threshold,
|
2023-12-11 16:11:07 +00:00
|
|
|
evlh: NotSend(evlh),
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-17 21:11:23 +02:00
|
|
|
pub fn is_tiling_grab(&self) -> bool {
|
2023-12-20 20:29:44 +00:00
|
|
|
self.previous == ManagedLayer::Tiling
|
2023-07-17 21:11:23 +02:00
|
|
|
}
|
2024-04-10 15:49:08 +02:00
|
|
|
|
|
|
|
|
pub fn is_touch_grab(&self) -> bool {
|
|
|
|
|
match self.start_data {
|
|
|
|
|
GrabStartData::Touch(_) => true,
|
|
|
|
|
GrabStartData::Pointer(_) => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-11 16:11:07 +00:00
|
|
|
}
|
2023-07-17 21:11:23 +02:00
|
|
|
|
2023-12-11 16:11:07 +00:00
|
|
|
impl Drop for MoveGrab {
|
|
|
|
|
fn drop(&mut self) {
|
2022-10-26 15:26:07 +02:00
|
|
|
// No more buttons are pressed, release the grab.
|
2024-04-11 13:47:37 -07:00
|
|
|
let output = self.cursor_output.clone();
|
2023-12-11 16:11:07 +00:00
|
|
|
let seat = self.seat.clone();
|
|
|
|
|
let window_outputs = self.window_outputs.drain().collect::<HashSet<_>>();
|
2025-10-16 13:50:32 +02:00
|
|
|
let previous = self.previous;
|
2023-12-11 16:11:07 +00:00
|
|
|
let window = self.window.clone();
|
2024-04-05 16:43:07 -07:00
|
|
|
let is_touch_grab = matches!(self.start_data, GrabStartData::Touch(_));
|
2023-12-11 16:11:07 +00:00
|
|
|
|
|
|
|
|
let _ = self.evlh.0.insert_idle(move |state| {
|
|
|
|
|
let position: Option<(CosmicMapped, Point<i32, Global>)> = if let Some(grab_state) =
|
|
|
|
|
seat.user_data()
|
|
|
|
|
.get::<SeatMoveGrabState>()
|
2024-06-07 19:26:23 +02:00
|
|
|
.and_then(|s| s.lock().unwrap().take())
|
2023-12-11 16:11:07 +00:00
|
|
|
{
|
|
|
|
|
if grab_state.window.alive() {
|
2024-04-05 16:43:07 -07:00
|
|
|
let window_location =
|
|
|
|
|
(grab_state.location.to_i32_round() + grab_state.window_offset).as_global();
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = state.common.shell.write();
|
2023-12-11 16:11:07 +00:00
|
|
|
|
2025-01-06 19:23:06 +01:00
|
|
|
let workspace_handle = shell.active_space(&output).unwrap().handle;
|
2023-12-11 16:11:07 +00:00
|
|
|
for old_output in window_outputs.iter().filter(|o| *o != &output) {
|
|
|
|
|
grab_state.window.output_leave(old_output);
|
|
|
|
|
}
|
2024-04-10 15:49:08 +02:00
|
|
|
|
2023-12-11 16:11:07 +00:00
|
|
|
for (window, _) in grab_state.window.windows() {
|
2024-04-10 15:49:08 +02:00
|
|
|
toplevel_enter_output(&window, &output);
|
2023-12-20 20:29:44 +00:00
|
|
|
if previous != ManagedLayer::Sticky {
|
2024-04-10 15:49:08 +02:00
|
|
|
toplevel_enter_workspace(&window, &workspace_handle);
|
2023-12-20 20:29:44 +00:00
|
|
|
}
|
2023-12-11 16:11:07 +00:00
|
|
|
}
|
2022-10-26 15:26:07 +02:00
|
|
|
|
2023-12-20 20:29:44 +00:00
|
|
|
match previous {
|
2024-01-26 18:47:59 +00:00
|
|
|
ManagedLayer::Sticky => {
|
2024-12-26 18:18:35 -08:00
|
|
|
grab_state.window.set_geometry(Rectangle::new(
|
2024-01-26 18:47:59 +00:00
|
|
|
window_location,
|
|
|
|
|
grab_state.window.geometry().size.as_global(),
|
|
|
|
|
));
|
2024-04-10 15:49:08 +02:00
|
|
|
let set = shell.workspaces.sets.get_mut(&output).unwrap();
|
2024-01-26 18:47:59 +00:00
|
|
|
let (window, location) = set
|
|
|
|
|
.sticky_layer
|
|
|
|
|
.drop_window(grab_state.window, window_location.to_local(&output));
|
|
|
|
|
|
|
|
|
|
Some((window, location.to_global(&output)))
|
|
|
|
|
}
|
2025-01-06 19:23:06 +01:00
|
|
|
ManagedLayer::Tiling
|
|
|
|
|
if shell.active_space(&output).unwrap().tiling_enabled =>
|
|
|
|
|
{
|
2024-04-10 15:49:08 +02:00
|
|
|
let (window, location) = shell
|
2023-12-20 20:29:44 +00:00
|
|
|
.active_space_mut(&output)
|
2025-01-06 19:23:06 +01:00
|
|
|
.unwrap()
|
2023-12-20 20:29:44 +00:00
|
|
|
.tiling_layer
|
|
|
|
|
.drop_window(grab_state.window);
|
|
|
|
|
Some((window, location.to_global(&output)))
|
|
|
|
|
}
|
2024-01-26 18:47:59 +00:00
|
|
|
_ => {
|
2024-12-26 18:18:35 -08:00
|
|
|
grab_state.window.set_geometry(Rectangle::new(
|
2023-12-20 20:29:44 +00:00
|
|
|
window_location,
|
|
|
|
|
grab_state.window.geometry().size.as_global(),
|
|
|
|
|
));
|
2024-04-10 15:49:08 +02:00
|
|
|
let theme = shell.theme.clone();
|
2025-01-06 19:23:06 +01:00
|
|
|
let workspace = shell.active_space_mut(&output).unwrap();
|
2024-01-08 21:37:06 +00:00
|
|
|
let (window, location) = workspace.floating_layer.drop_window(
|
2023-12-20 20:29:44 +00:00
|
|
|
grab_state.window,
|
2024-01-08 21:37:06 +00:00
|
|
|
window_location.to_local(&workspace.output),
|
2023-12-20 20:29:44 +00:00
|
|
|
);
|
2024-03-18 00:12:58 -05:00
|
|
|
|
2025-06-25 17:54:27 +02:00
|
|
|
if matches!(previous, ManagedLayer::Floating) {
|
2024-03-18 00:12:58 -05:00
|
|
|
if let Some(sz) = grab_state.snapping_zone {
|
2024-03-21 11:39:16 -05:00
|
|
|
if sz == SnappingZone::Maximize {
|
2025-06-25 17:54:27 +02:00
|
|
|
shell.maximize_toggle(
|
|
|
|
|
&window,
|
|
|
|
|
&seat,
|
|
|
|
|
&state.common.event_loop_handle,
|
|
|
|
|
);
|
2024-03-18 00:12:58 -05:00
|
|
|
} else {
|
2024-03-21 11:39:16 -05:00
|
|
|
let directions = match sz {
|
|
|
|
|
SnappingZone::Maximize => vec![],
|
|
|
|
|
SnappingZone::Top => vec![Direction::Up],
|
|
|
|
|
SnappingZone::TopLeft => {
|
|
|
|
|
vec![Direction::Up, Direction::Left]
|
|
|
|
|
}
|
|
|
|
|
SnappingZone::Left => vec![Direction::Left],
|
|
|
|
|
SnappingZone::BottomLeft => {
|
|
|
|
|
vec![Direction::Down, Direction::Left]
|
|
|
|
|
}
|
|
|
|
|
SnappingZone::Bottom => vec![Direction::Down],
|
|
|
|
|
SnappingZone::BottomRight => {
|
|
|
|
|
vec![Direction::Down, Direction::Right]
|
|
|
|
|
}
|
|
|
|
|
SnappingZone::Right => vec![Direction::Right],
|
|
|
|
|
SnappingZone::TopRight => {
|
|
|
|
|
vec![Direction::Up, Direction::Right]
|
|
|
|
|
}
|
2024-03-18 00:12:58 -05:00
|
|
|
};
|
2024-03-21 11:39:16 -05:00
|
|
|
for direction in directions {
|
|
|
|
|
workspace.floating_layer.move_element(
|
|
|
|
|
direction,
|
|
|
|
|
&seat,
|
|
|
|
|
ManagedLayer::Floating,
|
|
|
|
|
&theme,
|
|
|
|
|
&window,
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-03-18 00:12:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-08 21:37:06 +00:00
|
|
|
Some((window, location.to_global(&output)))
|
2023-12-20 20:29:44 +00:00
|
|
|
}
|
2023-12-11 16:11:07 +00:00
|
|
|
}
|
2023-10-25 19:24:51 +02:00
|
|
|
} else {
|
2023-12-11 16:11:07 +00:00
|
|
|
None
|
2023-07-17 21:11:23 +02:00
|
|
|
}
|
2023-04-19 11:42:46 +02:00
|
|
|
} else {
|
|
|
|
|
None
|
2023-12-11 16:11:07 +00:00
|
|
|
};
|
2023-07-18 12:21:16 +02:00
|
|
|
|
2023-12-11 16:11:07 +00:00
|
|
|
{
|
|
|
|
|
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
2024-09-09 16:21:27 +02:00
|
|
|
cursor_state.lock().unwrap().unset_shape();
|
2023-12-11 16:11:07 +00:00
|
|
|
}
|
2023-07-18 12:21:16 +02:00
|
|
|
|
2023-12-11 16:11:07 +00:00
|
|
|
if let Some((mapped, position)) = position {
|
|
|
|
|
let serial = SERIAL_COUNTER.next_serial();
|
2024-04-05 16:43:07 -07:00
|
|
|
if !is_touch_grab {
|
|
|
|
|
let pointer = seat.get_pointer().unwrap();
|
|
|
|
|
let current_location = pointer.current_location();
|
|
|
|
|
|
2024-11-14 13:19:47 -08:00
|
|
|
if let Some((target, offset)) = mapped.focus_under(
|
|
|
|
|
current_location - position.as_logical().to_f64(),
|
|
|
|
|
WindowSurfaceType::ALL,
|
|
|
|
|
) {
|
2024-04-05 16:43:07 -07:00
|
|
|
pointer.motion(
|
|
|
|
|
state,
|
|
|
|
|
Some((
|
|
|
|
|
target,
|
2024-04-03 16:02:27 +02:00
|
|
|
position.as_logical().to_f64() - window.geometry().loc.to_f64()
|
|
|
|
|
+ offset,
|
2024-04-05 16:43:07 -07:00
|
|
|
)),
|
|
|
|
|
&MotionEvent {
|
|
|
|
|
location: pointer.current_location(),
|
|
|
|
|
serial,
|
2024-08-21 13:49:49 -07:00
|
|
|
time: state.common.clock.now().as_millis(),
|
2024-04-05 16:43:07 -07:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-03-25 21:48:34 +01:00
|
|
|
}
|
2024-04-05 13:53:35 +02:00
|
|
|
Shell::set_focus(
|
2023-12-11 16:11:07 +00:00
|
|
|
state,
|
|
|
|
|
Some(&KeyboardFocusTarget::from(mapped)),
|
|
|
|
|
&seat,
|
|
|
|
|
Some(serial),
|
2024-09-04 11:13:59 -05:00
|
|
|
false,
|
2023-12-11 16:11:07 +00:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-10-26 15:26:07 +02:00
|
|
|
}
|
|
|
|
|
}
|