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

@ -7,7 +7,7 @@ struct view;
struct lab_layer_surface;
struct lab_layer_popup;
struct menuitem;
struct ssd_button;
struct ssd_part;
struct scaled_scene_buffer;
enum node_descriptor_type {
@ -21,7 +21,7 @@ enum node_descriptor_type {
LAB_NODE_DESC_MENUITEM,
LAB_NODE_DESC_TREE,
LAB_NODE_DESC_SCALED_SCENE_BUFFER,
LAB_NODE_DESC_SSD_BUTTON,
LAB_NODE_DESC_SSD_PART,
};
struct node_descriptor {
@ -44,7 +44,7 @@ struct node_descriptor {
* - LAB_NODE_DESC_LAYER_SURFACE struct lab_layer_surface
* - LAB_NODE_DESC_LAYER_POPUP struct lab_layer_popup
* - LAB_NODE_DESC_MENUITEM struct menuitem
* - LAB_NODE_DESC_SSD_BUTTON struct ssd_button
* - LAB_NODE_DESC_SSD_PART struct ssd_part
*/
void node_descriptor_create(struct wlr_scene_node *scene_node,
enum node_descriptor_type type, void *data);
@ -77,10 +77,10 @@ struct menuitem *node_menuitem_from_node(
struct wlr_scene_node *wlr_scene_node);
/**
* node_ssd_button_from_node - return ssd_button struct from node
* node_ssd_part_from_node - return ssd_part struct from node
* @wlr_scene_node: wlr_scene_node from which to return data
*/
struct ssd_button *node_ssd_button_from_node(
struct ssd_part *node_ssd_part_from_node(
struct wlr_scene_node *wlr_scene_node);
/**

View file

@ -3,52 +3,53 @@
#define LABWC_SSD_INTERNAL_H
#include <wlr/util/box.h>
#include "common/macros.h"
#include "ssd.h"
#include "theme.h"
#include "view.h"
#define FOR_EACH(tmp, ...) \
{ \
__typeof__(tmp) _x[] = { __VA_ARGS__, NULL }; \
size_t _i = 0; \
for ((tmp) = _x[_i]; _i < ARRAY_SIZE(_x) - 1; (tmp) = _x[++_i])
#define FOR_EACH_END }
struct ssd_button {
struct view *view;
enum ssd_part_type type;
/*
* Bitmap of lab_button_state that represents a combination of
* hover/toggled/rounded states.
*/
uint8_t state_set;
/*
* Image buffers for each combination of hover/toggled/rounded states.
* img_buffers[state_set] is displayed. Some of these can be NULL
* (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons).
*
* When "type" is LAB_SSD_BUTTON_WINDOW_ICON, these are all NULL and
* window_icon is used instead.
*/
struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1];
struct scaled_icon_buffer *window_icon;
struct wl_listener destroy;
};
struct ssd_sub_tree {
struct wlr_scene_tree *tree;
struct wl_list parts; /* ssd_part.link */
};
struct ssd_state_title_width {
int width;
bool truncated;
};
/*
* The scene-graph of SSD 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
* +--...
*/
struct ssd {
struct view *view;
struct wlr_scene_tree *tree;
@ -81,33 +82,49 @@ struct ssd {
struct wlr_box geometry;
struct ssd_state_title {
char *text;
struct ssd_state_title_width active;
struct ssd_state_title_width inactive;
/* indexed by enum ssd_active_state */
struct ssd_state_title_width dstates[2];
} title;
} state;
/* An invisible area around the view which allows resizing */
struct ssd_sub_tree extents;
struct ssd_extents_scene {
struct wlr_scene_tree *tree;
struct wlr_scene_rect *top, *bottom, *left, *right;
} extents;
/* The top of the view, containing buttons, title, .. */
struct {
struct ssd_titlebar_scene {
int height;
struct wlr_scene_tree *tree;
struct ssd_sub_tree active;
struct ssd_sub_tree inactive;
struct ssd_titlebar_subtree {
struct wlr_scene_tree *tree;
struct wlr_scene_buffer *corner_left;
struct wlr_scene_buffer *corner_right;
struct wlr_scene_buffer *bar;
struct scaled_font_buffer *title;
/* List of ssd_parts that represent buttons */
struct wl_list buttons_left;
struct wl_list buttons_right;
} subtrees[2]; /* indexed by enum ssd_active_state */
} titlebar;
/* Borders allow resizing as well */
struct {
struct ssd_border_scene {
struct wlr_scene_tree *tree;
struct ssd_sub_tree active;
struct ssd_sub_tree inactive;
struct ssd_border_subtree {
struct wlr_scene_tree *tree;
struct wlr_scene_rect *top, *bottom, *left, *right;
} subtrees[2]; /* indexed by enum ssd_active_state */
} border;
struct {
struct ssd_shadow_scene {
struct wlr_scene_tree *tree;
struct ssd_sub_tree active;
struct ssd_sub_tree inactive;
struct ssd_shadow_subtree {
struct wlr_scene_tree *tree;
struct wlr_scene_buffer *top, *bottom, *left, *right,
*top_left, *top_right, *bottom_left, *bottom_right;
} subtrees[2]; /* indexed by enum ssd_active_state */
} shadow;
/*
@ -118,46 +135,61 @@ struct ssd {
struct border margin;
};
/*
* ssd_part wraps a scene-node with ssd-specific information and can be
* accessed with node_ssd_part_from_node(wlr_scene_node *).
* This allows get_cursor_context() in desktop.c to see which SSD part is under
* the cursor.
*/
struct ssd_part {
enum ssd_part_type type;
/* Buffer pointer. May be NULL */
struct scaled_font_buffer *buffer;
struct view *view;
/* This part represented in scene graph */
struct wlr_scene_node *node;
struct wl_list link;
struct wl_listener node_destroy;
};
struct ssd_part_button {
struct ssd_part base;
/*
* Bitmap of lab_button_state that represents a combination of
* hover/toggled/rounded states.
*/
uint8_t state_set;
/*
* Image buffers for each combination of hover/toggled/rounded states.
* img_buffers[state_set] is displayed. Some of these can be NULL
* (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons).
*
* When the type of ssd_part pointing to ssd_button is
* LAB_SSD_BUTTON_WINDOW_ICON, these are all NULL and window_icon is
* used instead.
*/
struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1];
struct scaled_icon_buffer *window_icon;
struct wl_list link; /* ssd_titlebar_subtree.buttons_{left,right} */
};
/* FIXME: This structure is redundant as ssd_part contains view */
struct ssd_hover_state {
struct view *view;
struct ssd_button *button;
struct ssd_part_button *button;
};
struct wlr_buffer;
struct wlr_scene_tree;
/* SSD internal helpers to create various SSD elements */
/* TODO: Replace some common args with a struct */
struct ssd_part *add_scene_part(
struct wl_list *part_list, enum ssd_part_type type);
struct ssd_part *add_scene_rect(
struct wl_list *list, enum ssd_part_type type,
struct wlr_scene_tree *parent, int width, int height, int x, int y,
float color[4]);
struct ssd_part *add_scene_buffer(
struct wl_list *list, enum ssd_part_type type,
struct wlr_scene_tree *parent, struct wlr_buffer *buffer, int x, int y);
struct ssd_part *add_scene_button(struct wl_list *part_list,
struct ssd_part *attach_ssd_part(enum ssd_part_type type, struct view *view,
struct wlr_scene_node *node);
struct ssd_part_button *attach_ssd_part_button(struct wl_list *button_parts,
enum ssd_part_type type, struct wlr_scene_tree *parent,
struct lab_img *buffers[LAB_BS_ALL + 1], int x, int y,
struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y,
struct view *view);
/* SSD internal helpers */
struct ssd_part *ssd_get_part(
struct wl_list *part_list, enum ssd_part_type type);
void ssd_destroy_parts(struct wl_list *list);
struct ssd_part_button *button_try_from_ssd_part(struct ssd_part *part);
/* SSD internal */
void ssd_titlebar_create(struct ssd *ssd);

View file

@ -6,6 +6,13 @@
#include "common/edge.h"
#include "config/types.h"
enum ssd_active_state {
SSD_INACTIVE = 0,
SSD_ACTIVE = 1,
};
#define FOR_EACH_ACTIVE_STATE(active) for (active = SSD_INACTIVE; active <= SSD_ACTIVE; active++)
struct wlr_cursor;
/*
@ -36,11 +43,8 @@ enum ssd_part_type {
LAB_SSD_BUTTON,
LAB_SSD_PART_TITLEBAR,
LAB_SSD_PART_TITLEBAR_CORNER_RIGHT,
LAB_SSD_PART_TITLEBAR_CORNER_LEFT,
LAB_SSD_PART_TITLE,
/* shared by shadows, borders and extents */
LAB_SSD_PART_CORNER_TOP_LEFT,
LAB_SSD_PART_CORNER_TOP_RIGHT,
LAB_SSD_PART_CORNER_BOTTOM_RIGHT,
@ -64,8 +68,8 @@ enum ssd_part_type {
/* Forward declare arguments */
struct ssd;
struct ssd_button;
struct ssd_hover_state;
struct ssd_part;
struct view;
struct wlr_scene;
struct wlr_scene_node;
@ -96,12 +100,17 @@ struct ssd_hover_state *ssd_hover_state_new(void);
void ssd_update_button_hover(struct wlr_scene_node *node,
struct ssd_hover_state *hover_state);
enum ssd_part_type ssd_button_get_type(const struct ssd_button *button);
struct view *ssd_button_get_view(const struct ssd_button *button);
enum ssd_part_type ssd_part_get_type(const struct ssd_part *part);
struct view *ssd_part_get_view(const struct ssd_part *part);
/* Public SSD helpers */
enum ssd_part_type ssd_get_part_type(const struct ssd *ssd,
struct wlr_scene_node *node, struct wlr_cursor *cursor);
/*
* Returns a part type that represents a mouse context like "Top", "Left" and
* "TRCorner" when the cursor is on the window border or resizing handle.
*/
enum ssd_part_type ssd_get_resizing_type(const struct ssd *ssd,
struct wlr_cursor *cursor);
enum lab_edge ssd_resize_edges(enum ssd_part_type type);
bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate);
enum lab_ssd_mode ssd_mode_parse(const char *mode);

View file

@ -192,6 +192,7 @@ struct theme {
int mag_border_width;
};
/* TODO: replace with enum ssd_active_state */
#define THEME_INACTIVE 0
#define THEME_ACTIVE 1