ssd: fix resizing on border corners and add <resize><cornerRange>

Eliminate corner extents and instead use cursor position to map SSD
borders and extents to corner contexts, with a size configurable by the
<resize><cornerRange> parameter. This simplifies extent handling,
eliminates bugs in the detection of corner context, and allows users to
expand corner targets if they wish.

Co-authored-by: Andrew J. Hesford <ajh@sideband.org>
This commit is contained in:
tokyo4j 2025-02-01 14:51:47 +09:00 committed by Andrew J. Hesford
parent 9ad6e3c68c
commit 950337b895
9 changed files with 102 additions and 60 deletions

View file

@ -1223,6 +1223,8 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state)
}
} else if (!strcasecmp(nodename, "drawContents.resize")) {
set_bool(content, &rc.resize_draw_contents);
} else if (!strcasecmp(nodename, "cornerRange.resize")) {
rc.resize_corner_range = atoi(content);
} else if (!strcasecmp(nodename, "mouseEmulation.tablet")) {
set_bool(content, &rc.tablet.force_mouse_emulation);
} else if (!strcasecmp(nodename, "mapToOutput.tablet")) {
@ -1498,6 +1500,7 @@ rcxml_init(void)
rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER;
rc.resize_draw_contents = true;
rc.resize_corner_range = -1;
rc.workspace_config.popuptime = INT_MIN;
rc.workspace_config.min_nr_workspaces = 1;

View file

@ -278,7 +278,8 @@ get_cursor_context(struct server *server)
case LAB_NODE_DESC_VIEW:
case LAB_NODE_DESC_XDG_POPUP:
ret.view = desc->data;
ret.type = ssd_get_part_type(ret.view->ssd, ret.node);
ret.type = ssd_get_part_type(
ret.view->ssd, ret.node, cursor);
if (ret.type == LAB_SSD_CLIENT) {
ret.surface = lab_wlr_surface_from_node(ret.node);
}

View file

@ -37,17 +37,10 @@ ssd_extents_create(struct ssd *ssd)
-(theme->border_width + extended_area),
-(ssd->titlebar.height + theme->border_width + extended_area));
/* Top */
add_extent(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent);
add_extent(part_list, LAB_SSD_PART_TOP, parent);
add_extent(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent);
/* Sides */
add_extent(part_list, LAB_SSD_PART_LEFT, parent);
add_extent(part_list, LAB_SSD_PART_RIGHT, parent);
/* Bottom */
add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent);
add_extent(part_list, LAB_SSD_PART_BOTTOM, parent);
add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent);
/* Initial manual update to keep X11 applications happy */
ssd_extents_update(ssd);
@ -76,11 +69,6 @@ ssd_extents_update(struct ssd *ssd)
int full_height = height + theme->border_width * 2 + ssd->titlebar.height;
int full_width = width + 2 * theme->border_width;
int extended_area = SSD_EXTENDED_AREA;
int corner_width = ssd_get_corner_width();
int corner_size = extended_area + theme->border_width +
MIN(corner_width, width) / 2;
int side_width = full_width + extended_area * 2 - corner_size * 2;
int side_height = full_height + extended_area * 2 - corner_size * 2;
struct wlr_box part_box;
struct wlr_box result_box;
@ -120,54 +108,30 @@ ssd_extents_update(struct ssd *ssd)
wl_list_for_each(part, &ssd->extents.parts, link) {
rect = wlr_scene_rect_from_node(part->node);
switch (part->type) {
case LAB_SSD_PART_CORNER_TOP_LEFT:
case LAB_SSD_PART_TOP:
target.x = 0;
target.y = 0;
target.width = corner_size;
target.height = corner_size;
break;
case LAB_SSD_PART_TOP:
target.x = corner_size;
target.y = 0;
target.width = side_width;
target.width = full_width + extended_area * 2;
target.height = extended_area;
break;
case LAB_SSD_PART_CORNER_TOP_RIGHT:
target.x = corner_size + side_width;
target.y = 0;
target.width = corner_size;
target.height = corner_size;
break;
case LAB_SSD_PART_LEFT:
target.x = 0;
target.y = corner_size;
target.y = extended_area;
target.width = extended_area;
target.height = side_height;
target.height = full_height;
break;
case LAB_SSD_PART_RIGHT:
target.x = extended_area + full_width;
target.y = corner_size;
target.y = extended_area;
target.width = extended_area;
target.height = side_height;
break;
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
target.x = 0;
target.y = corner_size + side_height;
target.width = corner_size;
target.height = corner_size;
target.height = full_height;
break;
case LAB_SSD_PART_BOTTOM:
target.x = corner_size;
target.x = 0;
target.y = extended_area + full_height;
target.width = side_width;
target.width = full_width + extended_area * 2;
target.height = extended_area;
break;
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
target.x = corner_size + side_width;
target.y = corner_size + side_height;
target.width = corner_size;
target.height = corner_size;
break;
default:
/* not reached */
assert(false);

View file

@ -74,8 +74,69 @@ ssd_max_extents(struct view *view)
};
}
/*
* Resizing and mouse contexts like 'Left', 'TLCorner', etc. in the vicinity of
* SSD borders, titlebars and extents can have effective "corner regions" that
* behave differently from single-edge contexts.
*
* Corner regions are active whenever the cursor is within a prescribed size
* (generally rc.resize_corner_range, but clipped to view size) of the view
* bounds, so check the cursor against the view here.
*/
static enum ssd_part_type
get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor)
{
struct view *view = ssd ? ssd->view : NULL;
if (!view || !cursor || !view->ssd_enabled || view->fullscreen) {
return LAB_SSD_NONE;
}
struct wlr_box view_box = view->current;
view_box.height = view_effective_height(view, /* use_pending */ false);
if (!view->ssd_titlebar_hidden) {
/* If the titlebar is visible, consider it part of the view */
int titlebar_height = view->server->theme->titlebar_height;
view_box.y -= titlebar_height;
view_box.height += titlebar_height;
}
if (wlr_box_contains_point(&view_box, cursor->x, cursor->y)) {
/* A cursor in bounds of the view is never in an SSD context */
return LAB_SSD_NONE;
}
int corner_height = MAX(0, MIN(rc.resize_corner_range, view_box.height / 2));
int corner_width = MAX(0, MIN(rc.resize_corner_range, view_box.width / 2));
bool left = cursor->x < view_box.x + corner_width;
bool right = cursor->x > view_box.x + view_box.width - corner_width;
bool top = cursor->y < view_box.y + corner_height;
bool bottom = cursor->y > view_box.y + view_box.height - corner_height;
if (top && left) {
return LAB_SSD_PART_CORNER_TOP_LEFT;
} else if (top && right) {
return LAB_SSD_PART_CORNER_TOP_RIGHT;
} else if (bottom && left) {
return LAB_SSD_PART_CORNER_BOTTOM_LEFT;
} else if (bottom && right) {
return LAB_SSD_PART_CORNER_BOTTOM_RIGHT;
} else if (top) {
return LAB_SSD_PART_TOP;
} else if (bottom) {
return LAB_SSD_PART_BOTTOM;
} else if (left) {
return LAB_SSD_PART_LEFT;
} else if (right) {
return LAB_SSD_PART_RIGHT;
}
return LAB_SSD_NONE;
}
enum ssd_part_type
ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node)
ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node,
struct wlr_cursor *cursor)
{
if (!node) {
return LAB_SSD_NONE;
@ -121,25 +182,25 @@ ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node)
part_list = &ssd->border.inactive.parts;
}
enum ssd_part_type part_type = LAB_SSD_NONE;
if (part_list) {
struct ssd_part *part;
wl_list_for_each(part, part_list, link) {
if (node == part->node) {
return part->type;
part_type = part->type;
break;
}
}
}
return LAB_SSD_NONE;
}
enum ssd_part_type
ssd_at(const struct ssd *ssd, struct wlr_scene *scene, double lx, double ly)
{
assert(scene);
double sx, sy;
struct wlr_scene_node *node = wlr_scene_node_at(
&scene->tree.node, lx, ly, &sx, &sy);
return ssd_get_part_type(ssd, node);
if (part_type == LAB_SSD_NONE) {
return part_type;
}
/* Perform cursor-based context checks */
enum ssd_part_type resizing_type = get_resizing_type(ssd, cursor);
return resizing_type != LAB_SSD_NONE ? resizing_type : part_type;
}
uint32_t

View file

@ -1388,6 +1388,10 @@ post_processing(struct theme *theme)
rc.corner_radius = theme->titlebar_height - 1;
}
if (rc.resize_corner_range < 0) {
rc.resize_corner_range = theme->titlebar_height / 2;
}
int min_button_hover_radius =
MIN(theme->window_button_width, theme->window_button_height) / 2;
if (theme->window_button_hover_bg_corner_radius > min_button_hover_radius) {