*<action name="ToggleSnapToEdge" direction="value" />*++
*<action name="SnapToEdge" direction="value" />*
Resize window to fill half the output in the given direction. Supports
- directions "left", "up", "right", "down" and "center".
+ directions "left", "up", "right", "down", "up-left", "up-right", "down-left",
+ "down-right" and "center".
ToggleSnapToEdge additionally toggles the active window between
tiled to the given direction and its untiled position.
The "left" , "right", "left-occupied" and
"right-occupied" directions will not wrap.
- *tiled* [up|right|down|left|center|any]
+ *tiled* [up|right|down|left|top-left|top-right|down-left|down-right|center|any]
Whether the client is tiled (snapped) along the the
indicated screen edge.
edges of an output. Edge snapping causes a window to occupy half of its output,
extending outward from the snapped edge.
-*<snapping><range>*
- If an interactive move ends with the cursor a maximum distance *range*,
- (in pixels) from the edge of an output, the move will trigger a
- SnapToEdge action for that edge. A *range* of 0 disables snapping via
- interactive moves. Default is 10.
+*<snapping><range>*++
+*<snapping><cornerRange>*
+ If an interactive move ends with the cursor within *<range>* pixels of an
+ output edge, the window is snapped to the edge. If it's also within
+ *<cornerRange>* pixels of an output corner, the window is snapped to the
+ corner instead. A *<range>* of 0 disables snapping.
+ Default is 10 for *<range>* and 50 for *<cornerRange>*.
*<snapping><overlay><enabled>* [yes|no]
Show an overlay when snapping to a window to an edge. Default is yes.
<snapping>
<!-- Set range to 0 to disable window snapping completely -->
<range>10</range>
+ <cornerRange>50</cornerRange>
<overlay enabled="yes">
<delay inner="500" outer="500" />
</overlay>
#ifndef LABWC_DIRECTION_H
#define LABWC_DIRECTION_H
+#include <wlr/types/wlr_output_layout.h>
#include "view.h"
-enum wlr_direction direction_from_view_edge(enum view_edge edge);
+bool direction_from_view_edge(enum view_edge edge, enum wlr_direction *direction);
enum wlr_direction direction_get_opposite(enum wlr_direction direction);
#endif /* LABWC_DIRECTION_H */
/* window snapping */
int snap_edge_range;
+ int snap_edge_corner_range;
bool snap_overlay_enabled;
int snap_overlay_delay_inner;
int snap_overlay_delay_outer;
void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges);
void interactive_finish(struct view *view);
void interactive_cancel(struct view *view);
-enum view_edge edge_from_cursor(struct seat *seat, struct output **dest_output);
+
+/**
+ * Returns the edge to snap a window to.
+ * For example, if the output-relative cursor position (x,y) fulfills
+ * x <= (<snapping><cornerRange>) and y <= (<snapping><range>),
+ * then edge1=VIEW_EDGE_UP and edge2=VIEW_EDGE_LEFT.
+ * The value of (edge1|edge2) can be passed to view_snap_to_edge().
+ */
+bool edge_from_cursor(struct seat *seat, struct output **dest_output,
+ enum view_edge *edge1, enum view_edge *edge2);
void handle_tearing_new_object(struct wl_listener *listener, void *data);
VIEW_EDGE_DOWN = (1 << 3),
VIEW_EDGE_CENTER = (1 << 4),
VIEW_EDGE_ANY = (1 << 5),
+ VIEW_EDGE_UPLEFT = (VIEW_EDGE_UP | VIEW_EDGE_LEFT),
+ VIEW_EDGE_UPRIGHT = (VIEW_EDGE_UP | VIEW_EDGE_RIGHT),
+ VIEW_EDGE_DOWNLEFT = (VIEW_EDGE_DOWN | VIEW_EDGE_LEFT),
+ VIEW_EDGE_DOWNRIGHT = (VIEW_EDGE_DOWN | VIEW_EDGE_RIGHT),
};
enum view_wants_focus {
*/
void view_offer_focus(struct view *view);
+struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output,
+ enum view_edge edge);
+
void mappable_connect(struct mappable *mappable, struct wlr_surface *surface,
wl_notify_func_t notify_map, wl_notify_func_t notify_unmap);
void mappable_disconnect(struct mappable *mappable);
#include <wlr/types/wlr_output_layout.h>
#include "view.h"
-enum wlr_direction
-direction_from_view_edge(enum view_edge edge)
+bool
+direction_from_view_edge(enum view_edge edge, enum wlr_direction *direction)
{
switch (edge) {
case VIEW_EDGE_LEFT:
- return WLR_DIRECTION_LEFT;
+ *direction = WLR_DIRECTION_LEFT;
+ return true;
case VIEW_EDGE_RIGHT:
- return WLR_DIRECTION_RIGHT;
+ *direction = WLR_DIRECTION_RIGHT;
+ return true;
case VIEW_EDGE_UP:
- return WLR_DIRECTION_UP;
+ *direction = WLR_DIRECTION_UP;
+ return true;
case VIEW_EDGE_DOWN:
- return WLR_DIRECTION_DOWN;
- case VIEW_EDGE_CENTER:
- case VIEW_EDGE_INVALID:
+ *direction = WLR_DIRECTION_DOWN;
+ return true;
default:
- return WLR_DIRECTION_UP;
+ return false;
}
}
rc.unmaximize_threshold = atoi(content);
} else if (!strcasecmp(nodename, "range.snapping")) {
rc.snap_edge_range = atoi(content);
+ } else if (!strcasecmp(nodename, "cornerRange.snapping")) {
+ rc.snap_edge_corner_range = atoi(content);
} else if (!strcasecmp(nodename, "enabled.overlay.snapping")) {
set_bool(content, &rc.snap_overlay_enabled);
} else if (!strcasecmp(nodename, "inner.delay.overlay.snapping")) {
rc.unmaximize_threshold = 150;
rc.snap_edge_range = 10;
+ rc.snap_edge_corner_range = 50;
rc.snap_overlay_enabled = true;
rc.snap_overlay_delay_inner = 500;
rc.snap_overlay_delay_outer = 500;
}
}
-enum view_edge
-edge_from_cursor(struct seat *seat, struct output **dest_output)
+bool
+edge_from_cursor(struct seat *seat, struct output **dest_output,
+ enum view_edge *edge1, enum view_edge *edge2)
{
+ *dest_output = NULL;
+ *edge1 = VIEW_EDGE_INVALID;
+ *edge2 = VIEW_EDGE_INVALID;
+
if (!view_is_floating(seat->server->grabbed_view)) {
- return VIEW_EDGE_INVALID;
+ return false;
}
- int snap_range = rc.snap_edge_range;
- if (!snap_range) {
- return VIEW_EDGE_INVALID;
+ if (rc.snap_edge_range == 0) {
+ return false;
}
struct output *output = output_nearest_to_cursor(seat->server);
if (!output_is_usable(output)) {
wlr_log(WLR_ERROR, "output at cursor is unusable");
- return VIEW_EDGE_INVALID;
+ return false;
}
*dest_output = output;
output->wlr_output, &cursor_x, &cursor_y);
struct wlr_box *area = &output->usable_area;
- if (cursor_x <= area->x + snap_range) {
- return VIEW_EDGE_LEFT;
- } else if (cursor_x >= area->x + area->width - snap_range) {
- return VIEW_EDGE_RIGHT;
- } else if (cursor_y <= area->y + snap_range) {
- return VIEW_EDGE_UP;
- } else if (cursor_y >= area->y + area->height - snap_range) {
- return VIEW_EDGE_DOWN;
+
+ int top = cursor_y - area->y;
+ int bottom = area->y + area->height - cursor_y;
+ int left = cursor_x - area->x;
+ int right = area->x + area->width - cursor_x;
+
+ if (top < rc.snap_edge_range) {
+ *edge1 = VIEW_EDGE_UP;
+ } else if (bottom < rc.snap_edge_range) {
+ *edge1 = VIEW_EDGE_DOWN;
+ } else if (left < rc.snap_edge_range) {
+ *edge1 = VIEW_EDGE_LEFT;
+ } else if (right < rc.snap_edge_range) {
+ *edge1 = VIEW_EDGE_RIGHT;
} else {
- /* Not close to any edge */
- return VIEW_EDGE_INVALID;
+ return false;
}
+
+ if (*edge1 == VIEW_EDGE_UP || *edge1 == VIEW_EDGE_DOWN) {
+ if (left < rc.snap_edge_corner_range) {
+ *edge2 = VIEW_EDGE_LEFT;
+ } else if (right < rc.snap_edge_corner_range) {
+ *edge2 = VIEW_EDGE_RIGHT;
+ }
+ } else if (*edge1 == VIEW_EDGE_LEFT || *edge1 == VIEW_EDGE_RIGHT) {
+ if (top < rc.snap_edge_corner_range) {
+ *edge2 = VIEW_EDGE_UP;
+ } else if (bottom < rc.snap_edge_corner_range) {
+ *edge2 = VIEW_EDGE_DOWN;
+ }
+ }
+
+ return true;
}
/* Returns true if view was snapped to any edge */
snap_to_edge(struct view *view)
{
struct output *output;
- enum view_edge edge = edge_from_cursor(&view->server->seat, &output);
- if (edge == VIEW_EDGE_INVALID) {
+ enum view_edge edge1, edge2;
+ if (!edge_from_cursor(&view->server->seat, &output, &edge1, &edge2)) {
return false;
}
+ enum view_edge edge = edge1 | edge2;
view_set_output(view, output);
/*
return NULL;
}
+ enum wlr_direction direction;
+ if (!direction_from_view_edge(edge, &direction)) {
+ return NULL;
+ }
+
struct wlr_box box = output_usable_area_in_layout_coords(output);
int lx = box.x + box.width / 2;
int ly = box.y + box.height / 2;
struct wlr_output *new_output = NULL;
struct wlr_output *current_output = output->wlr_output;
struct wlr_output_layout *layout = output->server->output_layout;
- enum wlr_direction direction = direction_from_view_edge(edge);
new_output = wlr_output_layout_adjacent_output(layout, direction,
current_output, lx, ly);
#include "overlay.h"
#include <assert.h>
#include <wlr/types/wlr_scene.h>
+#include "common/direction.h"
#include "common/lab-scene-rect.h"
#include "labwc.h"
#include "output.h"
return 0;
}
-static enum wlr_direction
-get_wlr_direction(enum view_edge edge)
-{
- switch (edge) {
- case VIEW_EDGE_LEFT:
- return WLR_DIRECTION_LEFT;
- case VIEW_EDGE_RIGHT:
- return WLR_DIRECTION_RIGHT;
- case VIEW_EDGE_UP:
- case VIEW_EDGE_CENTER:
- return WLR_DIRECTION_UP;
- case VIEW_EDGE_DOWN:
- return WLR_DIRECTION_DOWN;
- default:
- /* not reached */
- assert(false);
- return 0;
- }
-}
-
static bool
edge_has_adjacent_output_from_cursor(struct seat *seat, struct output *output,
enum view_edge edge)
{
+ enum wlr_direction dir;
+ if (!direction_from_view_edge(edge, &dir)) {
+ return false;
+ }
return wlr_output_layout_adjacent_output(
- seat->server->output_layout, get_wlr_direction(edge),
+ seat->server->output_layout, dir,
output->wlr_output, seat->cursor->x, seat->cursor->y);
}
static void
-show_edge_overlay(struct seat *seat, enum view_edge edge,
+show_edge_overlay(struct seat *seat, enum view_edge edge1, enum view_edge edge2,
struct output *output)
{
if (!rc.snap_overlay_enabled) {
return;
}
+ uint32_t edge = edge1 | edge2;
if (seat->overlay.active.edge == edge
&& seat->overlay.active.output == output) {
return;
seat->overlay.active.output = output;
int delay;
- if (edge_has_adjacent_output_from_cursor(seat, output, edge)) {
+ if (edge_has_adjacent_output_from_cursor(seat, output, edge1)) {
delay = rc.snap_overlay_delay_inner;
} else {
delay = rc.snap_overlay_delay_outer;
/* Edge-snapping overlay */
struct output *output;
- enum view_edge edge = edge_from_cursor(seat, &output);
- if (edge != VIEW_EDGE_INVALID) {
- show_edge_overlay(seat, edge, output);
+ enum view_edge edge1, edge2;
+ if (edge_from_cursor(seat, &output, &edge1, &edge2)) {
+ show_edge_overlay(seat, edge1, edge2, output);
return;
}
enum view_edge edge)
{
struct wlr_box usable = output_usable_area_in_layout_coords(output);
- int x_offset = edge == VIEW_EDGE_RIGHT
- ? (usable.width + rc.gap) / 2 : rc.gap;
- int y_offset = edge == VIEW_EDGE_DOWN
- ? (usable.height + rc.gap) / 2 : rc.gap;
+ int x1 = rc.gap;
+ int y1 = rc.gap;
+ int x2 = usable.width - rc.gap;
+ int y2 = usable.height - rc.gap;
- int base_width, base_height;
- switch (edge) {
- case VIEW_EDGE_LEFT:
- case VIEW_EDGE_RIGHT:
- base_width = (usable.width - 3 * rc.gap) / 2;
- base_height = usable.height - 2 * rc.gap;
- break;
- case VIEW_EDGE_UP:
- case VIEW_EDGE_DOWN:
- base_width = usable.width - 2 * rc.gap;
- base_height = (usable.height - 3 * rc.gap) / 2;
- break;
- default:
- case VIEW_EDGE_CENTER:
- base_width = usable.width - 2 * rc.gap;
- base_height = usable.height - 2 * rc.gap;
- break;
+ if (edge & VIEW_EDGE_RIGHT) {
+ x1 = (usable.width + rc.gap) / 2;
+ }
+ if (edge & VIEW_EDGE_LEFT) {
+ x2 = (usable.width - rc.gap) / 2;
+ }
+ if (edge & VIEW_EDGE_DOWN) {
+ y1 = (usable.height + rc.gap) / 2;
+ }
+ if (edge & VIEW_EDGE_UP) {
+ y2 = (usable.height - rc.gap) / 2;
}
struct wlr_box dst = {
- .x = x_offset + usable.x,
- .y = y_offset + usable.y,
- .width = base_width,
- .height = base_height,
+ .x = x1 + usable.x,
+ .y = y1 + usable.y,
+ .width = x2 - x1,
+ .height = y2 - y1,
};
if (view) {
if (tiled) {
if (!strcasecmp(direction, "center")) {
return VIEW_EDGE_CENTER;
+ } else if (!strcasecmp(direction, "up-left")) {
+ return VIEW_EDGE_UPLEFT;
+ } else if (!strcasecmp(direction, "up-right")) {
+ return VIEW_EDGE_UPRIGHT;
+ } else if (!strcasecmp(direction, "down-left")) {
+ return VIEW_EDGE_DOWNLEFT;
+ } else if (!strcasecmp(direction, "down-right")) {
+ return VIEW_EDGE_DOWNRIGHT;
}
}
case VIEW_EDGE_DOWN:
edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT;
break;
+ case VIEW_EDGE_UPLEFT:
+ edge = WLR_EDGE_TOP | WLR_EDGE_LEFT;
+ break;
+ case VIEW_EDGE_UPRIGHT:
+ edge = WLR_EDGE_TOP | WLR_EDGE_RIGHT;
+ break;
+ case VIEW_EDGE_DOWNLEFT:
+ edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT;
+ break;
+ case VIEW_EDGE_DOWNRIGHT:
+ edge = WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT;
+ break;
+ /* TODO: VIEW_EDGE_CENTER? */
default:
edge = WLR_EDGE_NONE;
}