ssd: clean up scene management

Our codebase for ssd scenes has grown with a lot of technical debts:
- We needed to call `ssd_get_part()` everywhere to get the scene node of a
  ssd part. We then needed to cast it to `wlr_scene_rect` and
  `wlr_scene_buffer`. This bloated our codebase and even blocked
  duplicated button types in `<titlebar><layout>`.
- `ssd_get_part_type()` was a dirty hack. It compared parent, grandparent
  and grandgrandparent of a node with each subtree in the ssd to get the
  part type of the node.

To resolve this issues, this commit changes how ssd scenes are managed:
- Access scene rects and scene buffers just as a member of `struct ssd`.
- `ssd_part` is now a attachment to a scene node that can be accessed via
  node_descriptor->data, with a new node-descriptor type
  `LAB_NODE_DESC_SSD_PART`. `LAB_NODE_DESC_SSD_BUTTON` is unified into it.

Now the scene graph under ssd->tree looks like below. The parentheses
indicate the type of ssd_part attached to the node:

ssd->tree (LAB_SSD_NONE)
+--titlebar (LAB_SSD_PART_TITLEBAR)
|  +--inactive
|  |  +--background bar
|  |  +--left corner
|  |  +--right corner
|  |  +--title (LAB_SSD_PART_TITLE)
|  |  +--iconify button (LAB_SSD_BUTTON_ICONIFY)
|  |  |  +--normal close icon image
|  |  |  +--hovered close icon image
|  |  |  +--...
|  |  +--window icon (LAB_SSD_BUTTON_WINDOW_ICON)
|  |  |  +--window icon image
|  |  +--...
|  +--active
|     +--...
+--border
|  +--inactive
|  |  +--top
|  |  +--...
|  +--active
|     +--top
|     +--...
+--shadow
|  +--inactive
|  |  +--top
|  |  +--...
|  +--active
|     +--top
|     +--...
+--extents
   +--top
   +--...

When hovering on SSD, `get_cursor_context()` traverses this scene node
from the leaf. If it finds a `ssd_part` attached to the node, it returns
`ssd_part_type` that represents the resizing direction, button types or
`Title`/`Titlebar`.
This commit is contained in:
tokyo4j 2025-08-13 21:00:11 +09:00 committed by Johan Malm
parent aa3dbb4f85
commit f347a818e3
13 changed files with 516 additions and 726 deletions

View file

@ -2,16 +2,13 @@
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "common/macros.h"
#include "common/scene-helpers.h"
#include "labwc.h"
#include "ssd-internal.h"
#include "theme.h"
#include "view.h"
#define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \
&(ssd)->border.active, \
&(ssd)->border.inactive)
void
ssd_border_create(struct ssd *ssd)
{
@ -25,35 +22,37 @@ ssd_border_create(struct ssd *ssd)
int full_width = width + 2 * theme->border_width;
int corner_width = ssd_get_corner_width();
float *color;
struct wlr_scene_tree *parent;
struct ssd_sub_tree *subtree;
int active;
ssd->border.tree = wlr_scene_tree_create(ssd->tree);
wlr_scene_node_set_position(&ssd->border.tree->node, -theme->border_width, 0);
FOR_EACH_STATE(ssd, subtree) {
enum ssd_active_state active;
FOR_EACH_ACTIVE_STATE(active) {
struct ssd_border_subtree *subtree = &ssd->border.subtrees[active];
subtree->tree = wlr_scene_tree_create(ssd->border.tree);
parent = subtree->tree;
active = (subtree == &ssd->border.active) ?
THEME_ACTIVE : THEME_INACTIVE;
struct wlr_scene_tree *parent = subtree->tree;
wlr_scene_node_set_enabled(&parent->node, active);
color = theme->window[active].border_color;
float *color = theme->window[active].border_color;
wl_list_init(&subtree->parts);
add_scene_rect(&subtree->parts, LAB_SSD_PART_LEFT, parent,
theme->border_width, height, 0, 0, color);
add_scene_rect(&subtree->parts, LAB_SSD_PART_RIGHT, parent,
theme->border_width, height,
theme->border_width + width, 0, color);
add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent,
full_width, theme->border_width, 0, height, color);
add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent,
MAX(width - 2 * corner_width, 0), theme->border_width,
subtree->left = wlr_scene_rect_create(parent,
theme->border_width, height, color);
wlr_scene_node_set_position(&subtree->left->node, 0, 0);
subtree->right = wlr_scene_rect_create(parent,
theme->border_width, height, color);
wlr_scene_node_set_position(&subtree->right->node,
theme->border_width + width, 0);
subtree->bottom = wlr_scene_rect_create(parent,
full_width, theme->border_width, color);
wlr_scene_node_set_position(&subtree->bottom->node,
0, height);
subtree->top = wlr_scene_rect_create(parent,
MAX(width - 2 * corner_width, 0), theme->border_width, color);
wlr_scene_node_set_position(&subtree->top->node,
theme->border_width + corner_width,
-(ssd->titlebar.height + theme->border_width), color);
} FOR_EACH_END
-(ssd->titlebar.height + theme->border_width));
}
if (view->maximized == VIEW_AXIS_BOTH) {
wlr_scene_node_set_enabled(&ssd->border.tree->node, false);
@ -129,50 +128,30 @@ ssd_border_update(struct ssd *ssd)
? 0
: theme->border_width + corner_width;
struct ssd_part *part;
struct wlr_scene_rect *rect;
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(ssd, subtree) {
wl_list_for_each(part, &subtree->parts, link) {
rect = wlr_scene_rect_from_node(part->node);
switch (part->type) {
case LAB_SSD_PART_LEFT:
wlr_scene_rect_set_size(rect,
theme->border_width,
side_height);
wlr_scene_node_set_position(part->node,
0,
side_y);
continue;
case LAB_SSD_PART_RIGHT:
wlr_scene_rect_set_size(rect,
theme->border_width,
side_height);
wlr_scene_node_set_position(part->node,
theme->border_width + width,
side_y);
continue;
case LAB_SSD_PART_BOTTOM:
wlr_scene_rect_set_size(rect,
full_width,
theme->border_width);
wlr_scene_node_set_position(part->node,
0,
height);
continue;
case LAB_SSD_PART_TOP:
wlr_scene_rect_set_size(rect,
top_width,
theme->border_width);
wlr_scene_node_set_position(part->node,
top_x,
-(ssd->titlebar.height + theme->border_width));
continue;
default:
continue;
}
}
} FOR_EACH_END
enum ssd_active_state active;
FOR_EACH_ACTIVE_STATE(active) {
struct ssd_border_subtree *subtree = &ssd->border.subtrees[active];
wlr_scene_rect_set_size(subtree->left,
theme->border_width, side_height);
wlr_scene_node_set_position(&subtree->left->node,
0, side_y);
wlr_scene_rect_set_size(subtree->right,
theme->border_width, side_height);
wlr_scene_node_set_position(&subtree->right->node,
theme->border_width + width, side_y);
wlr_scene_rect_set_size(subtree->bottom,
full_width, theme->border_width);
wlr_scene_node_set_position(&subtree->bottom->node,
0, height);
wlr_scene_rect_set_size(subtree->top,
top_width, theme->border_width);
wlr_scene_node_set_position(&subtree->top->node,
top_x, -(ssd->titlebar.height + theme->border_width));
}
}
void
@ -181,15 +160,6 @@ ssd_border_destroy(struct ssd *ssd)
assert(ssd);
assert(ssd->border.tree);
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(ssd, subtree) {
ssd_destroy_parts(&subtree->parts);
wlr_scene_node_destroy(&subtree->tree->node);
subtree->tree = NULL;
} FOR_EACH_END
wlr_scene_node_destroy(&ssd->border.tree->node);
ssd->border.tree = NULL;
ssd->border = (struct ssd_border_scene){0};
}
#undef FOR_EACH_STATE