From 8e3108c032f72c72f6743b39dad6fd83ee45124a Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sun, 17 Oct 2021 00:44:24 +0000 Subject: [PATCH] action: Add 'SnapToEdge' Implements snap-style edge snapping that works between multiple monitors. Signed-off-by: Joshua Ashton --- include/labwc.h | 1 + src/action.c | 2 + src/view.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/include/labwc.h b/include/labwc.h index 03d13330..07e6cb91 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -337,6 +337,7 @@ void view_for_each_surface(struct view *view, void view_for_each_popup_surface(struct view *view, wlr_surface_iterator_func_t iterator, void *data); void view_move_to_edge(struct view *view, const char *direction); +void view_snap_to_edge(struct view *view, const char *direction); void view_update_title(struct view *view); void view_update_app_id(struct view *view); diff --git a/src/action.c b/src/action.c index ce735af6..b6b679e2 100644 --- a/src/action.c +++ b/src/action.c @@ -42,6 +42,8 @@ action(struct server *server, const char *action, const char *command) wl_display_terminate(server->wl_display); } else if (!strcasecmp(action, "MoveToEdge")) { view_move_to_edge(desktop_focused_view(server), command); + } else if (!strcasecmp(action, "SnapToEdge")) { + view_snap_to_edge(desktop_focused_view(server), command); } else if (!strcasecmp(action, "NextWindow")) { server->cycle_view = desktop_cycle_view(server, server->cycle_view); diff --git a/src/view.c b/src/view.c index 35c6e6bd..a35f5b2e 100644 --- a/src/view.c +++ b/src/view.c @@ -232,6 +232,110 @@ view_move_to_edge(struct view *view, const char *direction) view_move(view, x, y); } +enum view_edge { + VIEW_EDGE_INVALID, + + VIEW_EDGE_LEFT, + VIEW_EDGE_RIGHT, + VIEW_EDGE_UP, + VIEW_EDGE_DOWN, +}; + +static enum view_edge +view_edge_invert(enum view_edge edge) +{ + switch (edge) { + case VIEW_EDGE_LEFT: return VIEW_EDGE_RIGHT; + case VIEW_EDGE_RIGHT: return VIEW_EDGE_LEFT; + case VIEW_EDGE_UP: return VIEW_EDGE_DOWN; + case VIEW_EDGE_DOWN: return VIEW_EDGE_UP; + case VIEW_EDGE_INVALID: + default: + return VIEW_EDGE_INVALID; + } +} + +static enum view_edge +view_edge_parse(const char *direction) +{ + if (!strcasecmp(direction, "left")) { + return VIEW_EDGE_LEFT; + } else if (!strcasecmp(direction, "up")) { + return VIEW_EDGE_UP; + } else if (!strcasecmp(direction, "right")) { + return VIEW_EDGE_RIGHT; + } else if (!strcasecmp(direction, "down")) { + return VIEW_EDGE_DOWN; + } else { + return VIEW_EDGE_INVALID; + } +} + +static struct wlr_box +view_get_edge_snap_box(struct view *view, struct output *output, enum view_edge edge) +{ + struct border border = view_border(view); + struct wlr_box usable = output_usable_area_in_layout_coords(output); + + int x_offset = edge == VIEW_EDGE_RIGHT ? usable.width / 2 : 0; + int y_offset = edge == VIEW_EDGE_DOWN ? usable.height / 2 : 0; + int base_width = (edge == VIEW_EDGE_LEFT || edge == VIEW_EDGE_RIGHT) ? usable.width / 2 : usable.width; + int base_height = (edge == VIEW_EDGE_UP || edge == VIEW_EDGE_DOWN) ? usable.height / 2 : usable.height; + struct wlr_box dst = { + .x = x_offset + usable.x + border.left + rc.gap, + .y = y_offset + usable.y + border.top + rc.gap, + .width = base_width - border.left - border.right - 2 * rc.gap, + .height = base_height - border.top - border.bottom - 2 * rc.gap, + }; + + return dst; +} + +void +view_snap_to_edge(struct view *view, const char *direction) +{ + if (!view) { + wlr_log(WLR_ERROR, "no view"); + return; + } + struct output *output = view_output(view); + if (!output) { + wlr_log(WLR_ERROR, "no output"); + return; + } + enum view_edge edge = view_edge_parse(direction); + if (edge == VIEW_EDGE_INVALID) { + wlr_log(WLR_ERROR, "invalid edge"); + return; + } + + struct wlr_box dst = view_get_edge_snap_box(view, output, edge); + + if (view->x == dst.x && view->y == dst.y && view->w == dst.width && view->h == dst.height) { + /* Move over to the next screen if this is already snapped. */ + struct wlr_box usable = output_usable_area_in_layout_coords(output); + switch (edge) { + case VIEW_EDGE_LEFT: dst.x -= (usable.width / 2) + 1; break; + case VIEW_EDGE_RIGHT: dst.x += (usable.width / 2) + 1; break; + case VIEW_EDGE_UP: dst.y -= (usable.height / 2) + 1; break; + case VIEW_EDGE_DOWN: dst.y += (usable.height / 2) + 1; break; + default: break; + } + + struct output *new_output = + output_from_wlr_output(view->server, + wlr_output_layout_output_at(view->server->output_layout, dst.x, dst.y)); + + if (new_output == output || !new_output) { + return; + } + + dst = view_get_edge_snap_box(view, new_output, view_edge_invert(edge)); + } + + view_move_resize(view, dst); +} + void view_update_title(struct view *view) {