mirror of
https://github.com/labwc/labwc.git
synced 2025-11-05 13:29:58 -05:00
edges: limit edge attraction and resistance...
...to edges actually encountered by motion during interactive moves and resizes. In addition, ignore edge resistance and attraction for minimized views.
This commit is contained in:
parent
14f5733584
commit
cf34e60240
6 changed files with 233 additions and 67 deletions
190
src/edges.c
190
src/edges.c
|
|
@ -34,10 +34,45 @@ edges_initialize(struct border *edges)
|
|||
edges->left = INT_MIN;
|
||||
}
|
||||
|
||||
static inline struct edge
|
||||
build_edge(struct border region, enum view_edge direction, int pad)
|
||||
{
|
||||
struct edge edge = { 0 };
|
||||
|
||||
switch (direction) {
|
||||
case VIEW_EDGE_LEFT:
|
||||
edge.offset = clipped_sub(region.left, pad);
|
||||
edge.min = region.top;
|
||||
edge.max = region.bottom;
|
||||
break;
|
||||
case VIEW_EDGE_RIGHT:
|
||||
edge.offset = clipped_add(region.right, pad);
|
||||
edge.min = region.top;
|
||||
edge.max = region.bottom;
|
||||
break;
|
||||
case VIEW_EDGE_UP:
|
||||
edge.offset = clipped_sub(region.top, pad);
|
||||
edge.min = region.left;
|
||||
edge.max = region.right;
|
||||
break;
|
||||
case VIEW_EDGE_DOWN:
|
||||
edge.offset = clipped_add(region.bottom, pad);
|
||||
edge.min = region.left;
|
||||
edge.max = region.right;
|
||||
break;
|
||||
default:
|
||||
/* Should never be reached */
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
static void
|
||||
validate_edges(struct border *valid_edges,
|
||||
validate_single_region_edge(int *valid_edge,
|
||||
struct border view, struct border target,
|
||||
struct border region, edge_validator_t validator)
|
||||
struct border region, edge_validator_t validator,
|
||||
enum view_edge direction)
|
||||
{
|
||||
/*
|
||||
* When a view snaps to another while moving to its target, it can do
|
||||
|
|
@ -55,28 +90,53 @@ validate_edges(struct border *valid_edges,
|
|||
* the region borders for aligned edges only.
|
||||
*/
|
||||
|
||||
struct border region_pad = {
|
||||
.top = clipped_sub(region.top, rc.gap),
|
||||
.right = clipped_add(region.right, rc.gap),
|
||||
.bottom = clipped_add(region.bottom, rc.gap),
|
||||
.left = clipped_sub(region.left, rc.gap),
|
||||
};
|
||||
validator(valid_edge,
|
||||
build_edge(view, direction, 0),
|
||||
build_edge(target, direction, 0),
|
||||
build_edge(region, view_edge_invert(direction), 0),
|
||||
build_edge(region, direction, rc.gap));
|
||||
}
|
||||
|
||||
static void
|
||||
validate_edges(struct border *valid_edges,
|
||||
struct border view, struct border target,
|
||||
struct border region, edge_validator_t validator)
|
||||
{
|
||||
/* Check for edges encountered during movement of left edge */
|
||||
validator(&valid_edges->left, view.left, target.left,
|
||||
region.right, region_pad.left, /* lesser */ true);
|
||||
validate_single_region_edge(&valid_edges->left,
|
||||
view, target, region, validator, VIEW_EDGE_LEFT);
|
||||
|
||||
/* Check for edges encountered during movement of right edge */
|
||||
validator(&valid_edges->right, view.right, target.right,
|
||||
region.left, region_pad.right, /* lesser */ false);
|
||||
validate_single_region_edge(&valid_edges->right,
|
||||
view, target, region, validator, VIEW_EDGE_RIGHT);
|
||||
|
||||
/* Check for edges encountered during movement of top edge */
|
||||
validator(&valid_edges->top, view.top, target.top,
|
||||
region.bottom, region_pad.top, /* lesser */ true);
|
||||
validate_single_region_edge(&valid_edges->top,
|
||||
view, target, region, validator, VIEW_EDGE_UP);
|
||||
|
||||
/* Check for edges encountered during movement of bottom edge */
|
||||
validator(&valid_edges->bottom, view.bottom, target.bottom,
|
||||
region.top, region_pad.bottom, /* lesser */ false);
|
||||
validate_single_region_edge(&valid_edges->bottom,
|
||||
view, target, region, validator, VIEW_EDGE_DOWN);
|
||||
}
|
||||
|
||||
static void
|
||||
validate_single_output_edge(int *valid_edge,
|
||||
struct border view, struct border target,
|
||||
struct border region, edge_validator_t validator,
|
||||
enum view_edge direction)
|
||||
{
|
||||
static struct border unbounded = {
|
||||
.top = INT_MIN,
|
||||
.right = INT_MAX,
|
||||
.bottom = INT_MAX,
|
||||
.left = INT_MIN,
|
||||
};
|
||||
|
||||
validator(valid_edge,
|
||||
build_edge(view, direction, 0),
|
||||
build_edge(target, direction, 0),
|
||||
build_edge(region, direction, 0),
|
||||
build_edge(unbounded, direction, 0));
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -108,21 +168,31 @@ validate_output_edges(struct border *valid_edges,
|
|||
* only the non-infinite edges.
|
||||
*/
|
||||
|
||||
struct border output = {
|
||||
.top = usable.y,
|
||||
.right = usable.x + usable.width,
|
||||
.bottom = usable.y + usable.height,
|
||||
.left = usable.x,
|
||||
};
|
||||
|
||||
/* Left edge encounters a half-infinite region to the left of the output */
|
||||
validator(&valid_edges->left, view.left, target.left,
|
||||
usable.x, INT_MIN, /* lesser */ true);
|
||||
|
||||
validate_single_output_edge(&valid_edges->left,
|
||||
view, target, output, validator, VIEW_EDGE_LEFT);
|
||||
|
||||
/* Right edge encounters a half-infinite region to the right of the output */
|
||||
validator(&valid_edges->right, view.right, target.right,
|
||||
usable.x + usable.width, INT_MAX, /* lesser */ false);
|
||||
|
||||
validate_single_output_edge(&valid_edges->right,
|
||||
view, target, output, validator, VIEW_EDGE_RIGHT);
|
||||
|
||||
/* Top edge encounters a half-infinite region above the output */
|
||||
validator(&valid_edges->top, view.top, target.top,
|
||||
usable.y, INT_MIN, /* lesser */ true);
|
||||
|
||||
validate_single_output_edge(&valid_edges->top,
|
||||
view, target, output, validator, VIEW_EDGE_UP);
|
||||
|
||||
/* Bottom edge encounters a half-infinite region below the output */
|
||||
validator(&valid_edges->bottom, view.bottom, target.bottom,
|
||||
usable.y + usable.height, INT_MAX, /* lesser */ false);
|
||||
validate_single_output_edge(&valid_edges->bottom,
|
||||
view, target, output, validator, VIEW_EDGE_DOWN);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -145,7 +215,7 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
|
|||
|
||||
struct view *v;
|
||||
for_each_view(v, &view->server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
|
||||
if (v == view || !output_is_usable(v->output)) {
|
||||
if (v == view || v->minimized || !output_is_usable(v->output)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -317,3 +387,73 @@ edges_adjust_resize_geom(struct view *view, struct border edges,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static double
|
||||
linear_interp(int x, int x1, int y1, int x2, int y2)
|
||||
{
|
||||
/*
|
||||
* For a line y = mx + b that passes through both (x1, y1) and
|
||||
* (x2, y2), find and return the value y for a given point x.
|
||||
*
|
||||
* The point x does not need to fall in the range [x1, x2].
|
||||
*/
|
||||
|
||||
/* No need to interpolate if line is horizontal */
|
||||
int rise = y2 - y1;
|
||||
if (rise == 0) {
|
||||
return y2;
|
||||
}
|
||||
|
||||
/* For degenerate line, just pick a midpoint */
|
||||
int run = x2 - x1;
|
||||
if (run == 0) {
|
||||
return 0.5 * (y1 + y2);
|
||||
}
|
||||
|
||||
/* Othewise, linearly interpolate */
|
||||
int dx = x - x1;
|
||||
return y1 + dx * (rise / (double)run);
|
||||
}
|
||||
|
||||
bool
|
||||
edges_traverse_edge(struct edge current, struct edge target, struct edge obstacle)
|
||||
{
|
||||
/*
|
||||
* Each edge structure defines a line segment that can be represented
|
||||
* in a local coordinate system as starting at (offset, min) and
|
||||
* finishing at (offset, max).
|
||||
*
|
||||
* The starting and ending points of the "current" edge trace
|
||||
* respective lines
|
||||
*
|
||||
* 1. (current.offset, current.min) -> (target.offset, target.min)
|
||||
* 2. (current.offset, current.max) -> (target.offset, target.max)
|
||||
*
|
||||
* as the segment transits from its current position to its target.
|
||||
* Hence, motion of the entire edge from current to target will sweep a
|
||||
* quadrilateral bounded by (locally) vertical lines at current.offset
|
||||
* and target.offset as well as the segments (1) and (2) above.
|
||||
*
|
||||
* To test if the motion will encounter the obstacle edge, we need to
|
||||
* test if any of the obstacle edge falls within this quadrilateral.
|
||||
* Thus, we need to find the extent of the quadrilateral at the same
|
||||
* offset as the obstacle: a segment with starting point
|
||||
* (obstacle.offset, lo) and ending point (obstacle.offset, hi).
|
||||
*/
|
||||
|
||||
double lo =
|
||||
linear_interp(obstacle.offset,
|
||||
current.offset, current.min, target.offset, target.min);
|
||||
|
||||
/* Motion misses when obstacle ends above start of quad segment */
|
||||
if (obstacle.max < lo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double hi =
|
||||
linear_interp(obstacle.offset,
|
||||
current.offset, current.max, target.offset, target.max);
|
||||
|
||||
/* Motion hits when obstacle starts above the end of quad segment */
|
||||
return obstacle.min <= hi;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,16 @@
|
|||
#include "view.h"
|
||||
|
||||
static void
|
||||
check_edge(int *next, int current, int target,
|
||||
int oppose, int align, bool lesser, int tolerance)
|
||||
check_edge(int *next, struct edge current, struct edge target,
|
||||
struct edge oppose, struct edge align, int tolerance)
|
||||
{
|
||||
int cur = current.offset;
|
||||
int tgt = target.offset;
|
||||
int opp = oppose.offset;
|
||||
int aln = align.offset;
|
||||
|
||||
/* Ignore non-moving edges */
|
||||
if (current == target) {
|
||||
if (cur == tgt) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -32,56 +37,56 @@ check_edge(int *next, int current, int target,
|
|||
*/
|
||||
|
||||
/* Direction of motion for the edge */
|
||||
const bool decreasing = target < current;
|
||||
const bool decreasing = tgt < cur;
|
||||
|
||||
/* Check the opposing edge */
|
||||
bool valid = false;
|
||||
if (decreasing) {
|
||||
const int lo = clipped_sub(oppose, abs(tolerance));
|
||||
const int hi = clipped_sub(oppose, MIN(tolerance, 0));
|
||||
valid = target >= lo && target < hi;
|
||||
const int lo = clipped_sub(opp, abs(tolerance));
|
||||
const int hi = clipped_sub(opp, MIN(tolerance, 0));
|
||||
valid = tgt >= lo && tgt < hi;
|
||||
} else {
|
||||
/* Check for increasing movement across opposing edge */
|
||||
const int lo = clipped_add(oppose, MIN(tolerance, 0));
|
||||
const int hi = clipped_add(oppose, abs(tolerance));
|
||||
valid = target > lo && target <= hi;
|
||||
const int lo = clipped_add(opp, MIN(tolerance, 0));
|
||||
const int hi = clipped_add(opp, abs(tolerance));
|
||||
valid = tgt > lo && tgt <= hi;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
*next = edge_get_best(*next, oppose, decreasing);
|
||||
if (valid && edges_traverse_edge(current, target, oppose)) {
|
||||
*next = edge_get_best(*next, opp, decreasing);
|
||||
}
|
||||
|
||||
/* Check the aligned edge */
|
||||
valid = false;
|
||||
if (decreasing) {
|
||||
const int lo = clipped_sub(align, abs(tolerance));
|
||||
const int hi = clipped_sub(align, MIN(tolerance, 0));
|
||||
valid = target >= lo && target < hi;
|
||||
const int lo = clipped_sub(aln, abs(tolerance));
|
||||
const int hi = clipped_sub(aln, MIN(tolerance, 0));
|
||||
valid = tgt >= lo && tgt < hi;
|
||||
} else {
|
||||
const int lo = clipped_add(align, MIN(tolerance, 0));
|
||||
const int hi = clipped_add(align, abs(tolerance));
|
||||
valid = target > lo && target <= hi;
|
||||
const int lo = clipped_add(aln, MIN(tolerance, 0));
|
||||
const int hi = clipped_add(aln, abs(tolerance));
|
||||
valid = tgt > lo && tgt <= hi;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
*next = edge_get_best(*next, align, decreasing);
|
||||
if (valid && edges_traverse_edge(current, target, align)) {
|
||||
*next = edge_get_best(*next, aln, decreasing);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_edge_output(int *next, int current, int target,
|
||||
int oppose, int align, bool lesser)
|
||||
check_edge_output(int *next, struct edge current, struct edge target,
|
||||
struct edge oppose, struct edge align)
|
||||
{
|
||||
check_edge(next, current, target,
|
||||
oppose, align, lesser, rc.screen_edge_strength);
|
||||
oppose, align, rc.screen_edge_strength);
|
||||
}
|
||||
|
||||
static void
|
||||
check_edge_window(int *next, int current, int target,
|
||||
int oppose, int align, bool lesser)
|
||||
check_edge_window(int *next, struct edge current, struct edge target,
|
||||
struct edge oppose, struct edge align)
|
||||
{
|
||||
check_edge(next, current, target,
|
||||
oppose, align, lesser, rc.window_edge_strength);
|
||||
oppose, align, rc.window_edge_strength);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
22
src/snap.c
22
src/snap.c
|
|
@ -12,9 +12,15 @@
|
|||
#include "view.h"
|
||||
|
||||
static void
|
||||
check_edge(int *next, int current, int target, int oppose, int align, bool lesser)
|
||||
check_edge(int *next, struct edge current, struct edge target,
|
||||
struct edge oppose, struct edge align)
|
||||
{
|
||||
if (current == target) {
|
||||
int cur = current.offset;
|
||||
int tgt = target.offset;
|
||||
int opp = oppose.offset;
|
||||
int aln = align.offset;
|
||||
|
||||
if (cur == tgt) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -32,18 +38,16 @@ check_edge(int *next, int current, int target, int oppose, int align, bool lesse
|
|||
*/
|
||||
|
||||
/* Direction of motion for the edge */
|
||||
const bool decreasing = target < current;
|
||||
const bool decreasing = tgt < cur;
|
||||
|
||||
/* Check the opposing edge */
|
||||
if ((target <= oppose && oppose < current) ||
|
||||
(current < oppose && oppose <= target)) {
|
||||
*next = edge_get_best(*next, oppose, decreasing);
|
||||
if ((tgt <= opp && opp < cur) || (cur < opp && opp <= tgt)) {
|
||||
*next = edge_get_best(*next, opp, decreasing);
|
||||
}
|
||||
|
||||
/* Check the aligned edge */
|
||||
if ((target <= align && align < current) ||
|
||||
(current < align && align <= target)) {
|
||||
*next = edge_get_best(*next, align, decreasing);
|
||||
if ((tgt <= aln && aln < cur) || (cur < aln && aln <= tgt)) {
|
||||
*next = edge_get_best(*next, aln, decreasing);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ view_is_focusable_from(struct view *view, struct wlr_surface *prev)
|
|||
* They may be called repeatably during output layout changes.
|
||||
*/
|
||||
|
||||
static enum view_edge
|
||||
enum view_edge
|
||||
view_edge_invert(enum view_edge edge)
|
||||
{
|
||||
switch (edge) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue