Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Snap Window Edges to Close Output Edges #1189

Merged
merged 4 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cosmic-comp-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct CosmicCompConfig {
pub focus_follows_cursor_delay: u64,
/// Let X11 applications scale themselves
pub descale_xwayland: bool,
/// The threshold before windows snap themselves to output edges
pub edge_snap_threshold: u32,
}

impl Default for CosmicCompConfig {
Expand Down Expand Up @@ -76,6 +78,7 @@ impl Default for CosmicCompConfig {
cursor_follows_focus: false,
focus_follows_cursor_delay: 250,
descale_xwayland: false,
edge_snap_threshold: 0,
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
"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;
}
}
_ => {}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,11 @@ impl State {
&seat_clone,
serial,
edge,
state
.common
.config
.cosmic_conf
.edge_snap_threshold,
false,
);
drop(shell);
Expand Down
1 change: 1 addition & 0 deletions src/shell/element/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,7 @@ impl PointerTarget<State> for CosmicStack {
Focus::ResizeRight => ResizeEdge::RIGHT,
Focus::Header => unreachable!(),
},
state.common.config.cosmic_conf.edge_snap_threshold,
false,
);
if let Some((grab, focus)) = res {
Expand Down
1 change: 1 addition & 0 deletions src/shell/element/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,7 @@ impl PointerTarget<State> for CosmicWindow {
Focus::ResizeRight => ResizeEdge::RIGHT,
Focus::Header => unreachable!(),
},
state.common.config.cosmic_conf.edge_snap_threshold,
false,
);

Expand Down
30 changes: 24 additions & 6 deletions src/shell/grabs/menu/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,12 @@ pub fn window_items(
let _ = handle.insert_idle(move |state| {
let mut shell = state.common.shell.write().unwrap();
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);
if let Some(((target, loc), (grab, focus))) = res {
Expand Down Expand Up @@ -318,7 +323,12 @@ pub fn window_items(
let _ = handle.insert_idle(move |state| {
let mut shell = state.common.shell.write().unwrap();
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);
if let Some(((target, loc), (grab, focus))) = res {
Expand Down Expand Up @@ -348,8 +358,12 @@ pub fn window_items(
let _ = handle.insert_idle(move |state| {
let mut shell = state.common.shell.write().unwrap();
let seat = shell.seats.last_active().clone();
let res =
shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::RIGHT);
let res = shell.menu_resize_request(
&resize_clone,
&seat,
ResizeEdge::RIGHT,
state.common.config.cosmic_conf.edge_snap_threshold,
);

std::mem::drop(shell);
if let Some(((target, loc), (grab, focus))) = res {
Expand Down Expand Up @@ -379,8 +393,12 @@ pub fn window_items(
let _ = handle.insert_idle(move |state| {
let mut shell = state.common.shell.write().unwrap();
let seat = shell.seats.last_active().clone();
let res =
shell.menu_resize_request(&resize_clone, &seat, ResizeEdge::BOTTOM);
let res = shell.menu_resize_request(
&resize_clone,
&seat,
ResizeEdge::BOTTOM,
state.common.config.cosmic_conf.edge_snap_threshold,
);

std::mem::drop(shell);
if let Some(((target, loc), (grab, focus))) = res {
Expand Down
39 changes: 38 additions & 1 deletion src/shell/grabs/moving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ pub struct MoveGrab {
window_outputs: HashSet<Output>,
previous: ManagedLayer,
release: ReleaseMode,
window_snap_threshold: f64,
// SAFETY: This is only used on drop which will always be on the main thread
evlh: NotSend<LoopHandle<'static, State>>,
}
Expand Down Expand Up @@ -383,6 +384,40 @@ impl MoveGrab {

let mut window_geo = self.window.geometry();
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() {
if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) {
if self.window_outputs.insert(output.clone()) {
Expand Down Expand Up @@ -681,6 +716,7 @@ impl MoveGrab {
initial_window_location: Point<i32, Global>,
cursor_output: Output,
indicator_thickness: u8,
window_snap_threshold: f64,
previous_layer: ManagedLayer,
release: ReleaseMode,
evlh: LoopHandle<'static, State>,
Expand Down Expand Up @@ -720,10 +756,11 @@ impl MoveGrab {
window,
start_data,
seat: seat.clone(),
window_outputs: outputs,
cursor_output,
window_outputs: outputs,
previous: previous_layer,
release,
window_snap_threshold,
evlh: NotSend(evlh),
}
}
Expand Down
47 changes: 47 additions & 0 deletions src/shell/layout/floating/grabs/resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub struct ResizeSurfaceGrab {
window: CosmicMapped,
edges: ResizeEdge,
output: Output,
edge_snap_threshold: u32,
initial_window_location: Point<i32, Local>,
initial_window_size: Size<i32, Logical>,
last_window_size: Size<i32, Logical>,
release: ReleaseMode,
Expand Down Expand Up @@ -91,6 +93,27 @@ impl ResizeSurfaceGrab {
}

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) {
Expand All @@ -99,6 +122,27 @@ impl ResizeSurfaceGrab {
}

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());
Expand Down Expand Up @@ -375,6 +419,7 @@ impl ResizeSurfaceGrab {
mapped: CosmicMapped,
edges: ResizeEdge,
output: Output,
edge_snap_threshold: u32,
initial_window_location: Point<i32, Local>,
initial_window_size: Size<i32, Logical>,
seat: &Seat<State>,
Expand Down Expand Up @@ -414,9 +459,11 @@ impl ResizeSurfaceGrab {
window: mapped,
edges,
output,
initial_window_location,
initial_window_size,
last_window_size: initial_window_size,
release,
edge_snap_threshold,
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/shell/layout/floating/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ impl FloatingLayout {
seat: &Seat<State>,
start_data: GrabStartData,
edges: ResizeEdge,
edge_snap_threshold: u32,
release: ReleaseMode,
) -> Option<ResizeSurfaceGrab> {
if seat.get_pointer().is_some() {
Expand All @@ -900,6 +901,7 @@ impl FloatingLayout {
mapped.clone(),
edges,
self.space.outputs().next().cloned().unwrap(),
edge_snap_threshold,
location,
size,
seat,
Expand Down
5 changes: 5 additions & 0 deletions src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2896,6 +2896,7 @@ impl Shell {
initial_window_location,
cursor_output,
active_hint,
config.cosmic_conf.edge_snap_threshold as f64,
layer,
release,
evlh.clone(),
Expand Down Expand Up @@ -3150,6 +3151,7 @@ impl Shell {
mapped: &CosmicMapped,
seat: &Seat<State>,
edge: ResizeEdge,
edge_snap_threshold: u32,
) -> Option<(
(
Option<(PointerFocusTarget, Point<f64, Logical>)>,
Expand Down Expand Up @@ -3217,6 +3219,7 @@ impl Shell {
seat,
start_data.clone(),
edge,
edge_snap_threshold,
ReleaseMode::Click,
) {
grab.into()
Expand Down Expand Up @@ -3392,6 +3395,7 @@ impl Shell {
seat: &Seat<State>,
serial: impl Into<Option<Serial>>,
edges: ResizeEdge,
edge_snap_threshold: u32,
client_initiated: bool,
) -> Option<(ResizeGrab, Focus)> {
let serial = serial.into();
Expand Down Expand Up @@ -3419,6 +3423,7 @@ impl Shell {
seat,
start_data.clone(),
edges,
edge_snap_threshold,
ReleaseMode::NoMouseButtons,
) {
grab.into()
Expand Down
11 changes: 8 additions & 3 deletions src/wayland/handlers/xdg_shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,14 @@ impl XdgShellHandler for State {
) {
let seat = Seat::from_resource(&seat).unwrap();
let mut shell = self.common.shell.write().unwrap();
if let Some((grab, focus)) =
shell.resize_request(surface.wl_surface(), &seat, serial, edges.into(), true)
{
if let Some((grab, focus)) = shell.resize_request(
surface.wl_surface(),
&seat,
serial,
edges.into(),
self.common.config.cosmic_conf.edge_snap_threshold,
true,
) {
std::mem::drop(shell);
if grab.is_touch_grab() {
seat.get_touch().unwrap().set_grab(self, grab, serial)
Expand Down
11 changes: 8 additions & 3 deletions src/xwayland.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,9 +556,14 @@ impl XwmHandler for State {
if let Some(wl_surface) = window.wl_surface() {
let mut shell = self.common.shell.write().unwrap();
let seat = shell.seats.last_active().clone();
if let Some((grab, focus)) =
shell.resize_request(&wl_surface, &seat, None, resize_edge.into(), true)
{
if let Some((grab, focus)) = shell.resize_request(
&wl_surface,
&seat,
None,
resize_edge.into(),
self.common.config.cosmic_conf.edge_snap_threshold,
true,
) {
std::mem::drop(shell);
if grab.is_touch_grab() {
seat.get_touch()
Expand Down
Loading