grabs: Snap Window Edges to Close Output Edges
This commit is contained in:
parent
2678cf41b2
commit
2553810621
12 changed files with 148 additions and 13 deletions
|
|
@ -46,6 +46,8 @@ pub struct CosmicCompConfig {
|
||||||
pub focus_follows_cursor_delay: u64,
|
pub focus_follows_cursor_delay: u64,
|
||||||
/// Let X11 applications scale themselves
|
/// Let X11 applications scale themselves
|
||||||
pub descale_xwayland: bool,
|
pub descale_xwayland: bool,
|
||||||
|
/// The threshold before windows snap themselves to output edges
|
||||||
|
pub edge_snap_threshold: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CosmicCompConfig {
|
impl Default for CosmicCompConfig {
|
||||||
|
|
@ -76,6 +78,7 @@ impl Default for CosmicCompConfig {
|
||||||
cursor_follows_focus: false,
|
cursor_follows_focus: false,
|
||||||
focus_follows_cursor_delay: 250,
|
focus_follows_cursor_delay: 250,
|
||||||
descale_xwayland: false,
|
descale_xwayland: false,
|
||||||
|
edge_snap_threshold: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -846,6 +846,12 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
|
||||||
state.common.config.cosmic_conf.focus_follows_cursor_delay = new;
|
state.common.config.cosmic_conf.focus_follows_cursor_delay = new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"edge_snap_threshold" => {
|
||||||
|
let new = get_config::<u32>(&config, "edge_snap_threshold");
|
||||||
|
if new != state.common.config.cosmic_conf.edge_snap_threshold {
|
||||||
|
state.common.config.cosmic_conf.edge_snap_threshold = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -760,6 +760,11 @@ impl State {
|
||||||
&seat_clone,
|
&seat_clone,
|
||||||
serial,
|
serial,
|
||||||
edge,
|
edge,
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.config
|
||||||
|
.cosmic_conf
|
||||||
|
.edge_snap_threshold,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
drop(shell);
|
drop(shell);
|
||||||
|
|
|
||||||
|
|
@ -1369,6 +1369,7 @@ impl PointerTarget<State> for CosmicStack {
|
||||||
Focus::ResizeRight => ResizeEdge::RIGHT,
|
Focus::ResizeRight => ResizeEdge::RIGHT,
|
||||||
Focus::Header => unreachable!(),
|
Focus::Header => unreachable!(),
|
||||||
},
|
},
|
||||||
|
state.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
if let Some((grab, focus)) = res {
|
if let Some((grab, focus)) = res {
|
||||||
|
|
|
||||||
|
|
@ -759,6 +759,7 @@ impl PointerTarget<State> for CosmicWindow {
|
||||||
Focus::ResizeRight => ResizeEdge::RIGHT,
|
Focus::ResizeRight => ResizeEdge::RIGHT,
|
||||||
Focus::Header => unreachable!(),
|
Focus::Header => unreachable!(),
|
||||||
},
|
},
|
||||||
|
state.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,12 @@ pub fn window_items(
|
||||||
let _ = handle.insert_idle(move |state| {
|
let _ = handle.insert_idle(move |state| {
|
||||||
let mut shell = state.common.shell.write().unwrap();
|
let mut shell = state.common.shell.write().unwrap();
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
let res = shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::TOP);
|
let res = shell.menu_resize_request(
|
||||||
|
&resize_clone,
|
||||||
|
&seat,
|
||||||
|
ResizeEdge::TOP,
|
||||||
|
state.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
|
);
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
if let Some(((target, loc), (grab, focus))) = res {
|
if let Some(((target, loc), (grab, focus))) = res {
|
||||||
|
|
@ -318,7 +323,12 @@ pub fn window_items(
|
||||||
let _ = handle.insert_idle(move |state| {
|
let _ = handle.insert_idle(move |state| {
|
||||||
let mut shell = state.common.shell.write().unwrap();
|
let mut shell = state.common.shell.write().unwrap();
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
let res = shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::LEFT);
|
let res = shell.menu_resize_request(
|
||||||
|
&resize_clone,
|
||||||
|
&seat,
|
||||||
|
ResizeEdge::LEFT,
|
||||||
|
state.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
|
);
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
if let Some(((target, loc), (grab, focus))) = res {
|
if let Some(((target, loc), (grab, focus))) = res {
|
||||||
|
|
@ -348,8 +358,12 @@ pub fn window_items(
|
||||||
let _ = handle.insert_idle(move |state| {
|
let _ = handle.insert_idle(move |state| {
|
||||||
let mut shell = state.common.shell.write().unwrap();
|
let mut shell = state.common.shell.write().unwrap();
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
let res =
|
let res = shell.menu_resize_request(
|
||||||
shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::RIGHT);
|
&resize_clone,
|
||||||
|
&seat,
|
||||||
|
ResizeEdge::RIGHT,
|
||||||
|
state.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
|
);
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
if let Some(((target, loc), (grab, focus))) = res {
|
if let Some(((target, loc), (grab, focus))) = res {
|
||||||
|
|
@ -379,8 +393,12 @@ pub fn window_items(
|
||||||
let _ = handle.insert_idle(move |state| {
|
let _ = handle.insert_idle(move |state| {
|
||||||
let mut shell = state.common.shell.write().unwrap();
|
let mut shell = state.common.shell.write().unwrap();
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
let res =
|
let res = shell.menu_resize_request(
|
||||||
shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::BOTTOM);
|
&resize_clone,
|
||||||
|
&seat,
|
||||||
|
ResizeEdge::BOTTOM,
|
||||||
|
state.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
|
);
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
if let Some(((target, loc), (grab, focus))) = res {
|
if let Some(((target, loc), (grab, focus))) = res {
|
||||||
|
|
|
||||||
|
|
@ -342,6 +342,7 @@ pub struct MoveGrab {
|
||||||
window_outputs: HashSet<Output>,
|
window_outputs: HashSet<Output>,
|
||||||
previous: ManagedLayer,
|
previous: ManagedLayer,
|
||||||
release: ReleaseMode,
|
release: ReleaseMode,
|
||||||
|
window_snap_threshold: f64,
|
||||||
// SAFETY: This is only used on drop which will always be on the main thread
|
// SAFETY: This is only used on drop which will always be on the main thread
|
||||||
evlh: NotSend<LoopHandle<'static, State>>,
|
evlh: NotSend<LoopHandle<'static, State>>,
|
||||||
}
|
}
|
||||||
|
|
@ -383,6 +384,40 @@ impl MoveGrab {
|
||||||
|
|
||||||
let mut window_geo = self.window.geometry();
|
let mut window_geo = self.window.geometry();
|
||||||
window_geo.loc += location.to_i32_round() + grab_state.window_offset;
|
window_geo.loc += location.to_i32_round() + grab_state.window_offset;
|
||||||
|
|
||||||
|
if matches!(self.previous, ManagedLayer::Floating | ManagedLayer::Sticky) {
|
||||||
|
let loc = (grab_state.window_offset.to_f64() + grab_state.location).as_local();
|
||||||
|
let size = window_geo.size.to_f64().as_local();
|
||||||
|
let output_geom = self
|
||||||
|
.cursor_output
|
||||||
|
.geometry()
|
||||||
|
.to_f64()
|
||||||
|
.to_local(&self.cursor_output);
|
||||||
|
let output_loc = output_geom.loc;
|
||||||
|
let output_size = output_geom.size;
|
||||||
|
|
||||||
|
grab_state.location.x = if (loc.x - output_loc.x).abs() < self.window_snap_threshold
|
||||||
|
{
|
||||||
|
output_loc.x - grab_state.window_offset.x as f64
|
||||||
|
} else if ((loc.x + size.w) - (output_loc.x + output_size.w)).abs()
|
||||||
|
< self.window_snap_threshold
|
||||||
|
{
|
||||||
|
output_loc.x + output_size.w - grab_state.window_offset.x as f64 - size.w
|
||||||
|
} else {
|
||||||
|
grab_state.location.x
|
||||||
|
};
|
||||||
|
grab_state.location.y = if (loc.y - output_loc.y).abs() < self.window_snap_threshold
|
||||||
|
{
|
||||||
|
output_loc.y - grab_state.window_offset.y as f64
|
||||||
|
} else if ((loc.y + size.h) - (output_loc.y + output_size.h)).abs()
|
||||||
|
< self.window_snap_threshold
|
||||||
|
{
|
||||||
|
output_loc.y + output_size.h - grab_state.window_offset.y as f64 - size.h
|
||||||
|
} else {
|
||||||
|
grab_state.location.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for output in shell.outputs() {
|
for output in shell.outputs() {
|
||||||
if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) {
|
if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) {
|
||||||
if self.window_outputs.insert(output.clone()) {
|
if self.window_outputs.insert(output.clone()) {
|
||||||
|
|
@ -681,6 +716,7 @@ impl MoveGrab {
|
||||||
initial_window_location: Point<i32, Global>,
|
initial_window_location: Point<i32, Global>,
|
||||||
cursor_output: Output,
|
cursor_output: Output,
|
||||||
indicator_thickness: u8,
|
indicator_thickness: u8,
|
||||||
|
window_snap_threshold: f64,
|
||||||
previous_layer: ManagedLayer,
|
previous_layer: ManagedLayer,
|
||||||
release: ReleaseMode,
|
release: ReleaseMode,
|
||||||
evlh: LoopHandle<'static, State>,
|
evlh: LoopHandle<'static, State>,
|
||||||
|
|
@ -720,10 +756,11 @@ impl MoveGrab {
|
||||||
window,
|
window,
|
||||||
start_data,
|
start_data,
|
||||||
seat: seat.clone(),
|
seat: seat.clone(),
|
||||||
window_outputs: outputs,
|
|
||||||
cursor_output,
|
cursor_output,
|
||||||
|
window_outputs: outputs,
|
||||||
previous: previous_layer,
|
previous: previous_layer,
|
||||||
release,
|
release,
|
||||||
|
window_snap_threshold,
|
||||||
evlh: NotSend(evlh),
|
evlh: NotSend(evlh),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ pub struct ResizeSurfaceGrab {
|
||||||
window: CosmicMapped,
|
window: CosmicMapped,
|
||||||
edges: ResizeEdge,
|
edges: ResizeEdge,
|
||||||
output: Output,
|
output: Output,
|
||||||
|
edge_snap_threshold: u32,
|
||||||
|
initial_window_location: Point<i32, Local>,
|
||||||
initial_window_size: Size<i32, Logical>,
|
initial_window_size: Size<i32, Logical>,
|
||||||
last_window_size: Size<i32, Logical>,
|
last_window_size: Size<i32, Logical>,
|
||||||
release: ReleaseMode,
|
release: ReleaseMode,
|
||||||
|
|
@ -91,6 +93,27 @@ impl ResizeSurfaceGrab {
|
||||||
}
|
}
|
||||||
|
|
||||||
new_window_width = (self.initial_window_size.w as f64 + dx) as i32;
|
new_window_width = (self.initial_window_size.w as f64 + dx) as i32;
|
||||||
|
|
||||||
|
// If the resizing vertical edge is close to our output's edge in the same direction, snap to it.
|
||||||
|
let output_geom = self.output.geometry().to_local(&self.output);
|
||||||
|
if self.edges.intersects(ResizeEdge::LEFT) {
|
||||||
|
if ((self.initial_window_location.x - dx as i32 - output_geom.loc.x).abs() as u32)
|
||||||
|
< self.edge_snap_threshold
|
||||||
|
{
|
||||||
|
new_window_width = self.initial_window_size.w - output_geom.loc.x
|
||||||
|
+ self.initial_window_location.x;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((self.initial_window_location.x + self.initial_window_size.w + dx as i32
|
||||||
|
- output_geom.loc.x
|
||||||
|
- output_geom.size.w)
|
||||||
|
.abs() as u32)
|
||||||
|
< self.edge_snap_threshold
|
||||||
|
{
|
||||||
|
new_window_width =
|
||||||
|
output_geom.loc.x - self.initial_window_location.x + output_geom.size.w;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.edges.intersects(top_bottom) {
|
if self.edges.intersects(top_bottom) {
|
||||||
|
|
@ -99,6 +122,27 @@ impl ResizeSurfaceGrab {
|
||||||
}
|
}
|
||||||
|
|
||||||
new_window_height = (self.initial_window_size.h as f64 + dy) as i32;
|
new_window_height = (self.initial_window_size.h as f64 + dy) as i32;
|
||||||
|
|
||||||
|
// If the resizing horizontal edge is close to our output's edge in the same direction, snap to it.
|
||||||
|
let output_geom = self.output.geometry().to_local(&self.output);
|
||||||
|
if self.edges.intersects(ResizeEdge::TOP) {
|
||||||
|
if ((self.initial_window_location.y - dy as i32 - output_geom.loc.y).abs() as u32)
|
||||||
|
< self.edge_snap_threshold
|
||||||
|
{
|
||||||
|
new_window_height = self.initial_window_size.h - output_geom.loc.y
|
||||||
|
+ self.initial_window_location.y;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((self.initial_window_location.y + self.initial_window_size.h + dy as i32
|
||||||
|
- output_geom.loc.y
|
||||||
|
- output_geom.size.h)
|
||||||
|
.abs() as u32)
|
||||||
|
< self.edge_snap_threshold
|
||||||
|
{
|
||||||
|
new_window_height =
|
||||||
|
output_geom.loc.y - self.initial_window_location.y + output_geom.size.h;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (min_size, max_size) = (self.window.min_size(), self.window.max_size());
|
let (min_size, max_size) = (self.window.min_size(), self.window.max_size());
|
||||||
|
|
@ -375,6 +419,7 @@ impl ResizeSurfaceGrab {
|
||||||
mapped: CosmicMapped,
|
mapped: CosmicMapped,
|
||||||
edges: ResizeEdge,
|
edges: ResizeEdge,
|
||||||
output: Output,
|
output: Output,
|
||||||
|
edge_snap_threshold: u32,
|
||||||
initial_window_location: Point<i32, Local>,
|
initial_window_location: Point<i32, Local>,
|
||||||
initial_window_size: Size<i32, Logical>,
|
initial_window_size: Size<i32, Logical>,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
|
|
@ -414,9 +459,11 @@ impl ResizeSurfaceGrab {
|
||||||
window: mapped,
|
window: mapped,
|
||||||
edges,
|
edges,
|
||||||
output,
|
output,
|
||||||
|
initial_window_location,
|
||||||
initial_window_size,
|
initial_window_size,
|
||||||
last_window_size: initial_window_size,
|
last_window_size: initial_window_size,
|
||||||
release,
|
release,
|
||||||
|
edge_snap_threshold,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -888,6 +888,7 @@ impl FloatingLayout {
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
start_data: GrabStartData,
|
start_data: GrabStartData,
|
||||||
edges: ResizeEdge,
|
edges: ResizeEdge,
|
||||||
|
edge_snap_threshold: u32,
|
||||||
release: ReleaseMode,
|
release: ReleaseMode,
|
||||||
) -> Option<ResizeSurfaceGrab> {
|
) -> Option<ResizeSurfaceGrab> {
|
||||||
if seat.get_pointer().is_some() {
|
if seat.get_pointer().is_some() {
|
||||||
|
|
@ -900,6 +901,7 @@ impl FloatingLayout {
|
||||||
mapped.clone(),
|
mapped.clone(),
|
||||||
edges,
|
edges,
|
||||||
self.space.outputs().next().cloned().unwrap(),
|
self.space.outputs().next().cloned().unwrap(),
|
||||||
|
edge_snap_threshold,
|
||||||
location,
|
location,
|
||||||
size,
|
size,
|
||||||
seat,
|
seat,
|
||||||
|
|
|
||||||
|
|
@ -2896,6 +2896,7 @@ impl Shell {
|
||||||
initial_window_location,
|
initial_window_location,
|
||||||
cursor_output,
|
cursor_output,
|
||||||
active_hint,
|
active_hint,
|
||||||
|
config.cosmic_conf.edge_snap_threshold as f64,
|
||||||
layer,
|
layer,
|
||||||
release,
|
release,
|
||||||
evlh.clone(),
|
evlh.clone(),
|
||||||
|
|
@ -3150,6 +3151,7 @@ impl Shell {
|
||||||
mapped: &CosmicMapped,
|
mapped: &CosmicMapped,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
edge: ResizeEdge,
|
edge: ResizeEdge,
|
||||||
|
edge_snap_threshold: u32,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
(
|
(
|
||||||
Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
|
|
@ -3217,6 +3219,7 @@ impl Shell {
|
||||||
seat,
|
seat,
|
||||||
start_data.clone(),
|
start_data.clone(),
|
||||||
edge,
|
edge,
|
||||||
|
edge_snap_threshold,
|
||||||
ReleaseMode::Click,
|
ReleaseMode::Click,
|
||||||
) {
|
) {
|
||||||
grab.into()
|
grab.into()
|
||||||
|
|
@ -3392,6 +3395,7 @@ impl Shell {
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
serial: impl Into<Option<Serial>>,
|
serial: impl Into<Option<Serial>>,
|
||||||
edges: ResizeEdge,
|
edges: ResizeEdge,
|
||||||
|
edge_snap_threshold: u32,
|
||||||
client_initiated: bool,
|
client_initiated: bool,
|
||||||
) -> Option<(ResizeGrab, Focus)> {
|
) -> Option<(ResizeGrab, Focus)> {
|
||||||
let serial = serial.into();
|
let serial = serial.into();
|
||||||
|
|
@ -3419,6 +3423,7 @@ impl Shell {
|
||||||
seat,
|
seat,
|
||||||
start_data.clone(),
|
start_data.clone(),
|
||||||
edges,
|
edges,
|
||||||
|
edge_snap_threshold,
|
||||||
ReleaseMode::NoMouseButtons,
|
ReleaseMode::NoMouseButtons,
|
||||||
) {
|
) {
|
||||||
grab.into()
|
grab.into()
|
||||||
|
|
|
||||||
|
|
@ -197,9 +197,14 @@ impl XdgShellHandler for State {
|
||||||
) {
|
) {
|
||||||
let seat = Seat::from_resource(&seat).unwrap();
|
let seat = Seat::from_resource(&seat).unwrap();
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
if let Some((grab, focus)) =
|
if let Some((grab, focus)) = shell.resize_request(
|
||||||
shell.resize_request(surface.wl_surface(), &seat, serial, edges.into(), true)
|
surface.wl_surface(),
|
||||||
{
|
&seat,
|
||||||
|
serial,
|
||||||
|
edges.into(),
|
||||||
|
self.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
|
true,
|
||||||
|
) {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
if grab.is_touch_grab() {
|
if grab.is_touch_grab() {
|
||||||
seat.get_touch().unwrap().set_grab(self, grab, serial)
|
seat.get_touch().unwrap().set_grab(self, grab, serial)
|
||||||
|
|
|
||||||
|
|
@ -556,9 +556,14 @@ impl XwmHandler for State {
|
||||||
if let Some(wl_surface) = window.wl_surface() {
|
if let Some(wl_surface) = window.wl_surface() {
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
if let Some((grab, focus)) =
|
if let Some((grab, focus)) = shell.resize_request(
|
||||||
shell.resize_request(&wl_surface, &seat, None, resize_edge.into(), true)
|
&wl_surface,
|
||||||
{
|
&seat,
|
||||||
|
None,
|
||||||
|
resize_edge.into(),
|
||||||
|
self.common.config.cosmic_conf.edge_snap_threshold,
|
||||||
|
true,
|
||||||
|
) {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
if grab.is_touch_grab() {
|
if grab.is_touch_grab() {
|
||||||
seat.get_touch()
|
seat.get_touch()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue