mirror of
https://github.com/labwc/labwc.git
synced 2025-11-06 13:29:58 -05:00
Convert SSD to scene nodes
This commit is contained in:
parent
f2d40a8d69
commit
5862b65f89
23 changed files with 1134 additions and 557 deletions
7
src/ssd/meson.build
Normal file
7
src/ssd/meson.build
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
labwc_sources += files(
|
||||
'ssd.c',
|
||||
'ssd_part.c',
|
||||
'ssd_titlebar.c',
|
||||
'ssd_border.c',
|
||||
'ssd_extents.c',
|
||||
)
|
||||
300
src/ssd/ssd.c
Normal file
300
src/ssd/ssd.c
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* Helpers for view server side decorations
|
||||
*
|
||||
* Copyright (C) Johan Malm 2020-2021
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "config/rcxml.h"
|
||||
#include "common/font.h"
|
||||
#include "labwc.h"
|
||||
#include "theme.h"
|
||||
#include "ssd.h"
|
||||
|
||||
/* TODO: use theme->title_height instead of SSD_HEIGHT */
|
||||
struct border
|
||||
ssd_thickness(struct view *view)
|
||||
{
|
||||
struct theme *theme = view->server->theme;
|
||||
struct border border = {
|
||||
.top = SSD_HEIGHT,
|
||||
.bottom = theme->border_width,
|
||||
.left = theme->border_width,
|
||||
.right = theme->border_width,
|
||||
};
|
||||
return border;
|
||||
}
|
||||
|
||||
struct wlr_box
|
||||
ssd_max_extents(struct view *view)
|
||||
{
|
||||
struct border border = ssd_thickness(view);
|
||||
struct wlr_box box = {
|
||||
.x = view->x - border.left,
|
||||
.y = view->y - border.top,
|
||||
.width = view->w + border.left + border.right,
|
||||
.height = view->h + border.top + border.bottom,
|
||||
};
|
||||
return box;
|
||||
}
|
||||
|
||||
bool
|
||||
ssd_is_button(enum ssd_part_type type)
|
||||
{
|
||||
return type == LAB_SSD_BUTTON_CLOSE
|
||||
|| type == LAB_SSD_BUTTON_MAXIMIZE
|
||||
|| type == LAB_SSD_BUTTON_ICONIFY
|
||||
|| type == LAB_SSD_BUTTON_WINDOW_MENU;
|
||||
}
|
||||
|
||||
enum ssd_part_type
|
||||
ssd_get_part_type(struct view *view, struct wlr_scene_node *node)
|
||||
{
|
||||
if (!node) {
|
||||
return LAB_SSD_NONE;
|
||||
} else if (node->type == WLR_SCENE_NODE_SURFACE) {
|
||||
return LAB_SSD_CLIENT;
|
||||
} else if (!view->ssd.tree) {
|
||||
return LAB_SSD_NONE;
|
||||
}
|
||||
|
||||
struct wl_list *part_list = NULL;
|
||||
struct wlr_scene_node *grandparent =
|
||||
node->parent ? node->parent->parent : NULL;
|
||||
|
||||
/* active titlebar */
|
||||
if (node->parent == &view->ssd.titlebar.active.tree->node) {
|
||||
part_list = &view->ssd.titlebar.active.parts;
|
||||
} else if (grandparent == &view->ssd.titlebar.active.tree->node) {
|
||||
part_list = &view->ssd.titlebar.active.parts;
|
||||
|
||||
/* extents */
|
||||
} else if (node->parent == &view->ssd.extents.tree->node) {
|
||||
part_list = &view->ssd.extents.parts;
|
||||
|
||||
/* active border */
|
||||
} else if (node->parent == &view->ssd.border.active.tree->node) {
|
||||
part_list = &view->ssd.border.active.parts;
|
||||
|
||||
/* inactive titlebar */
|
||||
} else if (node->parent == &view->ssd.titlebar.inactive.tree->node) {
|
||||
part_list = &view->ssd.titlebar.inactive.parts;
|
||||
} else if (grandparent == &view->ssd.titlebar.inactive.tree->node) {
|
||||
part_list = &view->ssd.titlebar.inactive.parts;
|
||||
|
||||
/* inactive border */
|
||||
} else if (node->parent == &view->ssd.border.inactive.tree->node) {
|
||||
part_list = &view->ssd.border.inactive.parts;
|
||||
}
|
||||
|
||||
if (part_list) {
|
||||
struct ssd_part *part;
|
||||
wl_list_for_each(part, part_list, link) {
|
||||
if (node == part->node) {
|
||||
return part->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return LAB_SSD_NONE;
|
||||
}
|
||||
|
||||
enum ssd_part_type
|
||||
ssd_at(struct view *view, double lx, double ly)
|
||||
{
|
||||
double sx, sy;
|
||||
struct wlr_scene_node *node = wlr_scene_node_at(
|
||||
&view->server->scene->node, lx, ly, &sx, &sy);
|
||||
return ssd_get_part_type(view, node);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ssd_resize_edges(enum ssd_part_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case LAB_SSD_PART_TOP:
|
||||
return WLR_EDGE_TOP;
|
||||
case LAB_SSD_PART_RIGHT:
|
||||
return WLR_EDGE_RIGHT;
|
||||
case LAB_SSD_PART_BOTTOM:
|
||||
return WLR_EDGE_BOTTOM;
|
||||
case LAB_SSD_PART_LEFT:
|
||||
return WLR_EDGE_LEFT;
|
||||
case LAB_SSD_PART_CORNER_TOP_LEFT:
|
||||
return WLR_EDGE_TOP | WLR_EDGE_LEFT;
|
||||
case LAB_SSD_PART_CORNER_TOP_RIGHT:
|
||||
return WLR_EDGE_RIGHT | WLR_EDGE_TOP;
|
||||
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
|
||||
return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT;
|
||||
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
|
||||
return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_ssd_set_active(struct ssd *ssd, bool active)
|
||||
{
|
||||
wlr_scene_node_set_enabled(&ssd->border.active.tree->node, active);
|
||||
wlr_scene_node_set_enabled(&ssd->titlebar.active.tree->node, active);
|
||||
wlr_scene_node_set_enabled(&ssd->border.inactive.tree->node, !active);
|
||||
wlr_scene_node_set_enabled(&ssd->titlebar.inactive.tree->node, !active);
|
||||
}
|
||||
|
||||
void
|
||||
ssd_create(struct view *view)
|
||||
{
|
||||
if (view->ssd.tree) {
|
||||
/* SSD was hidden. Just enable it */
|
||||
wlr_log(WLR_ERROR, "Unhiding SSD");
|
||||
wlr_scene_node_set_enabled(&view->ssd.tree->node, true);
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "Creating SSD");
|
||||
view->ssd.tree = wlr_scene_tree_create(&view->scene_tree->node);
|
||||
wlr_scene_node_lower_to_bottom(&view->ssd.tree->node);
|
||||
ssd_extents_create(view);
|
||||
ssd_border_create(view);
|
||||
ssd_titlebar_create(view);
|
||||
}
|
||||
|
||||
void
|
||||
ssd_update_geometry(struct view *view)
|
||||
{
|
||||
/* TODO: verify we are not called without reason. like in commit handlers */
|
||||
if (!view->ssd.tree || !view->surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (view->ssd.enabled && !view->ssd.tree->node.state.enabled) {
|
||||
wlr_scene_node_set_enabled(&view->ssd.tree->node, true);
|
||||
}
|
||||
if (!view->ssd.enabled && view->ssd.tree->node.state.enabled) {
|
||||
wlr_scene_node_set_enabled(&view->ssd.tree->node, false);
|
||||
}
|
||||
|
||||
int width = view->surface->current.width;
|
||||
int height = view->surface->current.height;
|
||||
if (width == view->ssd.state.width && height == view->ssd.state.height) {
|
||||
return;
|
||||
}
|
||||
ssd_extents_update(view);
|
||||
ssd_border_update(view);
|
||||
ssd_titlebar_update(view);
|
||||
|
||||
view->ssd.state.width = width;
|
||||
view->ssd.state.height = height;
|
||||
}
|
||||
|
||||
void
|
||||
ssd_hide(struct view *view)
|
||||
{
|
||||
if (!view->ssd.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "Hiding SSD");
|
||||
wlr_scene_node_set_enabled(&view->ssd.tree->node, false);
|
||||
}
|
||||
|
||||
void ssd_reload(struct view *view)
|
||||
{
|
||||
if (!view->ssd.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool view_was_active = view->server->ssd_focused_view == view;
|
||||
ssd_destroy(view);
|
||||
ssd_create(view);
|
||||
if (view_was_active) {
|
||||
view->server->ssd_focused_view = view;
|
||||
} else {
|
||||
_ssd_set_active(&view->ssd, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ssd_destroy(struct view *view)
|
||||
{
|
||||
if (!view->ssd.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "Destroying SSD");
|
||||
|
||||
/* Maybe reset focused view */
|
||||
if (view->server->ssd_focused_view == view) {
|
||||
view->server->ssd_focused_view = NULL;
|
||||
}
|
||||
|
||||
/* Maybe reset hover view */
|
||||
struct ssd_hover_state *hover_state;
|
||||
hover_state = &view->server->ssd_hover_state;
|
||||
if (hover_state->view == view) {
|
||||
hover_state->view = NULL;
|
||||
hover_state->type = LAB_SSD_NONE;
|
||||
hover_state->node = NULL;
|
||||
}
|
||||
|
||||
/* Destroy subcomponents */
|
||||
ssd_titlebar_destroy(view);
|
||||
ssd_border_destroy(view);
|
||||
ssd_extents_destroy(view);
|
||||
wlr_scene_node_destroy(&view->ssd.tree->node);
|
||||
view->ssd.tree = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate)
|
||||
{
|
||||
if (whole == candidate) {
|
||||
return true;
|
||||
}
|
||||
if (whole == LAB_SSD_PART_TITLEBAR) {
|
||||
return candidate >= LAB_SSD_BUTTON_CLOSE
|
||||
&& candidate <= LAB_SSD_PART_TITLE;
|
||||
}
|
||||
if (whole == LAB_SSD_FRAME) {
|
||||
return candidate >= LAB_SSD_BUTTON_CLOSE
|
||||
&& candidate <= LAB_SSD_CLIENT;
|
||||
}
|
||||
if (whole == LAB_SSD_PART_TOP) {
|
||||
return candidate == LAB_SSD_PART_CORNER_TOP_LEFT
|
||||
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
|
||||
}
|
||||
if (whole == LAB_SSD_PART_RIGHT) {
|
||||
return candidate == LAB_SSD_PART_CORNER_TOP_RIGHT
|
||||
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT;
|
||||
}
|
||||
if (whole == LAB_SSD_PART_BOTTOM) {
|
||||
return candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT
|
||||
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
|
||||
}
|
||||
if (whole == LAB_SSD_PART_LEFT) {
|
||||
return candidate == LAB_SSD_PART_CORNER_TOP_LEFT
|
||||
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ssd_set_active(struct view *view)
|
||||
{
|
||||
if (!view->ssd.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct view *last = view->server->ssd_focused_view;
|
||||
if (last == view) {
|
||||
return;
|
||||
}
|
||||
if (last && last->ssd.tree) {
|
||||
_ssd_set_active(&last->ssd, false);
|
||||
}
|
||||
_ssd_set_active(&view->ssd, true);
|
||||
view->server->ssd_focused_view = view;
|
||||
}
|
||||
96
src/ssd/ssd_border.c
Normal file
96
src/ssd/ssd_border.c
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "labwc.h"
|
||||
#include "ssd.h"
|
||||
#include "theme.h"
|
||||
#include "common/scene-helpers.h"
|
||||
|
||||
#define FOR_EACH_STATE(view, tmp) FOR_EACH(tmp, \
|
||||
&(view)->ssd.border.active, \
|
||||
&(view)->ssd.border.inactive)
|
||||
|
||||
void
|
||||
ssd_border_create(struct view *view)
|
||||
{
|
||||
struct theme *theme = view->server->theme;
|
||||
int width = view->surface->current.width;
|
||||
int height = view->surface->current.height;
|
||||
int full_width = width + 2 * theme->border_width;
|
||||
|
||||
float *color;
|
||||
struct wlr_scene_node *parent;
|
||||
struct ssd_sub_tree *subtree;
|
||||
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
subtree->tree = wlr_scene_tree_create(&view->ssd.tree->node);
|
||||
parent = &subtree->tree->node;
|
||||
wlr_scene_node_set_position(parent, -theme->border_width, 0);
|
||||
if (subtree == &view->ssd.border.active) {
|
||||
color = theme->window_active_border_color;
|
||||
} else {
|
||||
color = theme->window_inactive_border_color;
|
||||
wlr_scene_node_set_enabled(parent, false);
|
||||
}
|
||||
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);
|
||||
} FOR_EACH_END
|
||||
}
|
||||
|
||||
void
|
||||
ssd_border_update(struct view *view)
|
||||
{
|
||||
struct theme *theme = view->server->theme;
|
||||
|
||||
int width = view->surface->current.width;
|
||||
int height = view->surface->current.height;
|
||||
int full_width = width + 2 * theme->border_width;
|
||||
|
||||
struct ssd_part *part;
|
||||
struct wlr_scene_rect *rect;
|
||||
struct ssd_sub_tree *subtree;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
wl_list_for_each(part, &subtree->parts, link) {
|
||||
rect = lab_wlr_scene_get_rect(part->node);
|
||||
switch (part->type) {
|
||||
case LAB_SSD_PART_LEFT:
|
||||
wlr_scene_rect_set_size(rect, theme->border_width, height);
|
||||
continue;
|
||||
case LAB_SSD_PART_RIGHT:
|
||||
wlr_scene_rect_set_size(rect, theme->border_width, height);
|
||||
wlr_scene_node_set_position(
|
||||
part->node, theme->border_width + width, 0);
|
||||
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;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} FOR_EACH_END
|
||||
}
|
||||
|
||||
void
|
||||
ssd_border_destroy(struct view *view)
|
||||
{
|
||||
if (!view->ssd.border.active.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct ssd_sub_tree *subtree;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
ssd_destroy_parts(&subtree->parts);
|
||||
wlr_scene_node_destroy(&subtree->tree->node);
|
||||
subtree->tree = NULL;
|
||||
} FOR_EACH_END
|
||||
}
|
||||
|
||||
#undef FOR_EACH_STATE
|
||||
119
src/ssd/ssd_extents.c
Normal file
119
src/ssd/ssd_extents.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "labwc.h"
|
||||
#include "ssd.h"
|
||||
#include "theme.h"
|
||||
#include "common/scene-helpers.h"
|
||||
|
||||
void
|
||||
ssd_extents_create(struct view *view)
|
||||
{
|
||||
struct theme *theme = view->server->theme;
|
||||
float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
struct wl_list *part_list = &view->ssd.extents.parts;
|
||||
int width = view->surface->current.width;
|
||||
int height = view->surface->current.height;
|
||||
int full_height = height + theme->border_width + SSD_HEIGHT;
|
||||
int full_width = width + 2 * theme->border_width;
|
||||
int extended_area = EXTENDED_AREA;
|
||||
|
||||
view->ssd.extents.tree = wlr_scene_tree_create(
|
||||
&view->ssd.tree->node);
|
||||
struct wlr_scene_node *parent =
|
||||
&view->ssd.extents.tree->node;
|
||||
wlr_scene_node_set_position(parent,
|
||||
-(theme->border_width + extended_area), -(SSD_HEIGHT + extended_area));
|
||||
wl_list_init(&view->ssd.extents.parts);
|
||||
|
||||
/* Top */
|
||||
add_scene_rect(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent,
|
||||
extended_area, extended_area,
|
||||
0, 0, invisible);
|
||||
add_scene_rect(part_list, LAB_SSD_PART_TOP, parent,
|
||||
full_width, extended_area,
|
||||
extended_area, 0, invisible);
|
||||
add_scene_rect(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent,
|
||||
extended_area, extended_area,
|
||||
extended_area + full_width, 0, invisible);
|
||||
|
||||
/* Sides */
|
||||
add_scene_rect(part_list, LAB_SSD_PART_LEFT, parent,
|
||||
extended_area, full_height,
|
||||
0, extended_area, invisible);
|
||||
add_scene_rect(part_list, LAB_SSD_PART_RIGHT, parent,
|
||||
extended_area, full_height,
|
||||
extended_area + full_width, extended_area, invisible);
|
||||
|
||||
/* Bottom */
|
||||
add_scene_rect(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent,
|
||||
extended_area, extended_area,
|
||||
0, extended_area + full_height, invisible);
|
||||
add_scene_rect(part_list, LAB_SSD_PART_BOTTOM, parent,
|
||||
full_width, extended_area,
|
||||
extended_area, extended_area + full_height, invisible);
|
||||
add_scene_rect(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent,
|
||||
extended_area, extended_area,
|
||||
extended_area + full_width, extended_area + full_height, invisible);
|
||||
}
|
||||
|
||||
void
|
||||
ssd_extents_update(struct view *view)
|
||||
{
|
||||
struct theme *theme = view->server->theme;
|
||||
|
||||
int width = view->surface->current.width;
|
||||
int height = view->surface->current.height;
|
||||
int full_height = height + theme->border_width + SSD_HEIGHT;
|
||||
int full_width = width + 2 * theme->border_width;
|
||||
int extended_area = EXTENDED_AREA;
|
||||
|
||||
struct ssd_part *part;
|
||||
struct wlr_scene_rect *rect;
|
||||
wl_list_for_each(part, &view->ssd.extents.parts, link) {
|
||||
rect = lab_wlr_scene_get_rect(part->node);
|
||||
switch (part->type) {
|
||||
case LAB_SSD_PART_TOP:
|
||||
wlr_scene_rect_set_size(rect, full_width, extended_area);
|
||||
continue;
|
||||
case LAB_SSD_PART_CORNER_TOP_RIGHT:
|
||||
wlr_scene_node_set_position(
|
||||
part->node, extended_area + full_width, 0);
|
||||
continue;
|
||||
case LAB_SSD_PART_LEFT:
|
||||
wlr_scene_rect_set_size(rect, extended_area, full_height);
|
||||
continue;
|
||||
case LAB_SSD_PART_RIGHT:
|
||||
wlr_scene_rect_set_size(rect, extended_area, full_height);
|
||||
wlr_scene_node_set_position(
|
||||
part->node, extended_area + full_width, extended_area);
|
||||
continue;
|
||||
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
|
||||
wlr_scene_node_set_position(
|
||||
part->node, 0, extended_area + full_height);
|
||||
continue;
|
||||
case LAB_SSD_PART_BOTTOM:
|
||||
wlr_scene_rect_set_size(rect, full_width, extended_area);
|
||||
wlr_scene_node_set_position(
|
||||
part->node, extended_area, extended_area + full_height);
|
||||
continue;
|
||||
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
|
||||
wlr_scene_node_set_position(part->node,
|
||||
extended_area + full_width, extended_area + full_height);
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ssd_extents_destroy(struct view *view)
|
||||
{
|
||||
if (!view->ssd.extents.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
ssd_destroy_parts(&view->ssd.extents.parts);
|
||||
wlr_scene_node_destroy(&view->ssd.extents.tree->node);
|
||||
view->ssd.extents.tree = NULL;
|
||||
}
|
||||
109
src/ssd/ssd_part.c
Normal file
109
src/ssd/ssd_part.c
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <assert.h>
|
||||
#include "labwc.h"
|
||||
#include "ssd.h"
|
||||
|
||||
struct ssd_part *
|
||||
add_scene_part(struct wl_list *part_list, enum ssd_part_type type)
|
||||
{
|
||||
struct ssd_part *part = calloc(1, sizeof(struct ssd_part));
|
||||
part->type = type;
|
||||
wl_list_insert(part_list->prev, &part->link);
|
||||
return part;
|
||||
}
|
||||
|
||||
struct ssd_part *
|
||||
add_scene_rect(struct wl_list *list, enum ssd_part_type type,
|
||||
struct wlr_scene_node *parent, int width, int height,
|
||||
int x, int y, float color[4])
|
||||
{
|
||||
struct ssd_part *part = add_scene_part(list, type);
|
||||
part->node = &wlr_scene_rect_create(
|
||||
parent, width, height, color)->node;
|
||||
wlr_scene_node_set_position(part->node, x, y);
|
||||
return part;
|
||||
}
|
||||
|
||||
struct ssd_part *
|
||||
add_scene_buffer(struct wl_list *list, enum ssd_part_type type,
|
||||
struct wlr_scene_node *parent, struct wlr_buffer *buffer,
|
||||
int x, int y)
|
||||
{
|
||||
struct ssd_part *part = add_scene_part(list, type);
|
||||
part->node = &wlr_scene_buffer_create(parent, buffer)->node;
|
||||
wlr_scene_node_set_position(part->node, x, y);
|
||||
return part;
|
||||
}
|
||||
|
||||
static void
|
||||
finish_scene_button(struct wl_list *part_list, enum ssd_part_type type,
|
||||
struct wlr_scene_node *parent, struct wlr_buffer *icon_buffer)
|
||||
{
|
||||
float hover_bg[4] = {0.15f, 0.15f, 0.15f, 0.3f};
|
||||
|
||||
/* Icon */
|
||||
add_scene_buffer(part_list, type, parent, icon_buffer,
|
||||
(BUTTON_WIDTH - icon_buffer->width) / 2,
|
||||
(SSD_HEIGHT - icon_buffer->height) / 2);
|
||||
|
||||
/* Hover overlay */
|
||||
struct ssd_part *hover_part;
|
||||
hover_part = add_scene_rect(part_list, type, parent,
|
||||
BUTTON_WIDTH, SSD_HEIGHT, 0, 0, hover_bg);
|
||||
wlr_scene_node_set_enabled(hover_part->node, false);
|
||||
}
|
||||
|
||||
struct ssd_part *
|
||||
add_scene_button_corner(struct wl_list *part_list, enum ssd_part_type type,
|
||||
struct wlr_scene_node *parent, struct wlr_buffer *corner_buffer,
|
||||
struct wlr_buffer *icon_buffer, int x)
|
||||
{
|
||||
struct ssd_part *part;
|
||||
/* Background */
|
||||
part = add_scene_buffer(part_list, type, parent, corner_buffer, x, 0);
|
||||
finish_scene_button(part_list, type, part->node, icon_buffer);
|
||||
return part;
|
||||
}
|
||||
|
||||
struct ssd_part *
|
||||
add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
|
||||
struct wlr_scene_node *parent, float *bg_color,
|
||||
struct wlr_buffer *icon_buffer, int x)
|
||||
{
|
||||
struct ssd_part *part;
|
||||
/* Background */
|
||||
part = add_scene_rect(part_list, type, parent,
|
||||
BUTTON_WIDTH, SSD_HEIGHT, x, 0, bg_color);
|
||||
finish_scene_button(part_list, type, part->node, icon_buffer);
|
||||
return part;
|
||||
}
|
||||
|
||||
struct ssd_part *
|
||||
ssd_get_part(struct wl_list *part_list, enum ssd_part_type type)
|
||||
{
|
||||
struct ssd_part *part;
|
||||
wl_list_for_each(part, part_list, link) {
|
||||
if (part->type == type) {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
ssd_destroy_parts(struct wl_list *list)
|
||||
{
|
||||
struct ssd_part *part, *tmp;
|
||||
wl_list_for_each_reverse_safe(part, tmp, list, link) {
|
||||
if (part->node) {
|
||||
wlr_scene_node_destroy(part->node);
|
||||
}
|
||||
if (part->buffer) {
|
||||
wlr_buffer_drop(&part->buffer->base);
|
||||
}
|
||||
wl_list_remove(&part->link);
|
||||
free(part);
|
||||
}
|
||||
assert(wl_list_empty(list));
|
||||
}
|
||||
306
src/ssd/ssd_titlebar.c
Normal file
306
src/ssd/ssd_titlebar.c
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "labwc.h"
|
||||
#include "ssd.h"
|
||||
#include "theme.h"
|
||||
#include "common/font.h"
|
||||
#include "common/scene-helpers.h"
|
||||
|
||||
#define FOR_EACH_STATE(view, tmp) FOR_EACH(tmp, \
|
||||
&(view)->ssd.titlebar.active, \
|
||||
&(view)->ssd.titlebar.inactive)
|
||||
|
||||
void
|
||||
ssd_titlebar_create(struct view *view)
|
||||
{
|
||||
struct theme *theme = view->server->theme;
|
||||
int width = view->surface->current.width;
|
||||
int full_width = width + 2 * theme->border_width;
|
||||
|
||||
float *color;
|
||||
struct wlr_scene_node *parent;
|
||||
struct wlr_buffer *corner_top_left;
|
||||
struct wlr_buffer *corner_top_right;
|
||||
|
||||
struct ssd_sub_tree *subtree;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
subtree->tree = wlr_scene_tree_create(&view->ssd.tree->node);
|
||||
parent = &subtree->tree->node;
|
||||
wlr_scene_node_set_position(parent, -theme->border_width, -SSD_HEIGHT);
|
||||
if (subtree == &view->ssd.titlebar.active) {
|
||||
color = theme->window_active_title_bg_color;
|
||||
corner_top_left = &theme->corner_top_left_active_normal->base;
|
||||
corner_top_right = &theme->corner_top_right_active_normal->base;
|
||||
} else {
|
||||
color = theme->window_inactive_title_bg_color;
|
||||
corner_top_left = &theme->corner_top_left_inactive_normal->base;
|
||||
corner_top_right = &theme->corner_top_right_inactive_normal->base;
|
||||
wlr_scene_node_set_enabled(parent, false);
|
||||
}
|
||||
wl_list_init(&subtree->parts);
|
||||
|
||||
/* Title */
|
||||
add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent,
|
||||
full_width - BUTTON_WIDTH * BUTTON_COUNT, SSD_HEIGHT,
|
||||
BUTTON_WIDTH, 0, color);
|
||||
/* Buttons */
|
||||
add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU,
|
||||
parent, corner_top_left,
|
||||
&theme->xbm_menu_active_unpressed->base, 0);
|
||||
add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent,
|
||||
color, &theme->xbm_iconify_active_unpressed->base,
|
||||
full_width - BUTTON_WIDTH * 3);
|
||||
add_scene_button(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent,
|
||||
color, &theme->xbm_maximize_active_unpressed->base,
|
||||
full_width - BUTTON_WIDTH * 2);
|
||||
add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent,
|
||||
corner_top_right, &theme->xbm_close_active_unpressed->base,
|
||||
full_width - BUTTON_WIDTH * 1);
|
||||
} FOR_EACH_END
|
||||
ssd_update_title(view);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_direct_child(struct wlr_scene_node *node, struct ssd_sub_tree *subtree)
|
||||
{
|
||||
return node->parent == &subtree->tree->node;
|
||||
}
|
||||
|
||||
void
|
||||
ssd_titlebar_update(struct view *view)
|
||||
{
|
||||
int width = view->surface->current.width;
|
||||
if (width == view->ssd.state.width) {
|
||||
return;
|
||||
}
|
||||
int full_width = width + 2 * view->server->theme->border_width;
|
||||
|
||||
struct ssd_part *part;
|
||||
struct ssd_sub_tree *subtree;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
wl_list_for_each(part, &subtree->parts, link) {
|
||||
switch (part->type) {
|
||||
case LAB_SSD_PART_TITLEBAR:
|
||||
wlr_scene_rect_set_size(lab_wlr_scene_get_rect(part->node),
|
||||
full_width - BUTTON_WIDTH * BUTTON_COUNT, SSD_HEIGHT);
|
||||
continue;
|
||||
case LAB_SSD_BUTTON_ICONIFY:
|
||||
if (is_direct_child(part->node, subtree)) {
|
||||
wlr_scene_node_set_position(part->node,
|
||||
full_width - BUTTON_WIDTH * 3, 0);
|
||||
}
|
||||
continue;
|
||||
case LAB_SSD_BUTTON_MAXIMIZE:
|
||||
if (is_direct_child(part->node, subtree)) {
|
||||
wlr_scene_node_set_position(part->node,
|
||||
full_width - BUTTON_WIDTH * 2, 0);
|
||||
}
|
||||
continue;
|
||||
case LAB_SSD_BUTTON_CLOSE:
|
||||
if (is_direct_child(part->node, subtree)) {
|
||||
wlr_scene_node_set_position(part->node,
|
||||
full_width - BUTTON_WIDTH * 1, 0);
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} FOR_EACH_END
|
||||
ssd_update_title(view);
|
||||
}
|
||||
|
||||
void
|
||||
ssd_titlebar_destroy(struct view *view)
|
||||
{
|
||||
if (!view->ssd.titlebar.active.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct ssd_sub_tree *subtree;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
ssd_destroy_parts(&subtree->parts);
|
||||
wlr_scene_node_destroy(&subtree->tree->node);
|
||||
subtree->tree = NULL;
|
||||
} FOR_EACH_END
|
||||
|
||||
if (view->ssd.state.title.text) {
|
||||
free(view->ssd.state.title.text);
|
||||
view->ssd.state.title.text = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For ssd_update_title* we do not early out because
|
||||
* .active and .inactive may result in different sizes
|
||||
* of the title (font family/size) or background of
|
||||
* the title (different button/border width).
|
||||
*/
|
||||
|
||||
static void
|
||||
ssd_update_title_positions(struct view *view)
|
||||
{
|
||||
struct theme *theme = view->server->theme;
|
||||
int width = view->surface->current.width;
|
||||
int full_width = width + 2 * view->server->theme->border_width;
|
||||
|
||||
int x, y;
|
||||
struct wlr_scene_rect *rect;
|
||||
struct ssd_part *part;
|
||||
struct ssd_sub_tree *subtree;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE);
|
||||
if (!part) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Failed to position SSD title: title node not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
x = 0;
|
||||
y = (SSD_HEIGHT - part->buffer->base.height) / 2;
|
||||
rect = lab_wlr_scene_get_rect(part->node->parent);
|
||||
if (rect->width <= 0) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Failed to position SSD title: not enough screen space");
|
||||
wlr_scene_node_set_position(part->node, x, y);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) {
|
||||
if (part->buffer->base.width + BUTTON_WIDTH * 2 <= rect->width) {
|
||||
/* Center based on the full width */
|
||||
x = (full_width - part->buffer->base.width) / 2;
|
||||
x -= BUTTON_WIDTH;
|
||||
} else {
|
||||
/*
|
||||
* Center based on the width between the buttons.
|
||||
* Title jumps around once this is hit but its still
|
||||
* better than to hide behind the buttons on the right.
|
||||
*/
|
||||
x = (rect->width - part->buffer->base.width) / 2;
|
||||
}
|
||||
} else if (theme->window_label_text_justify == LAB_JUSTIFY_RIGHT) {
|
||||
x = rect->width - part->buffer->base.width;
|
||||
} else if (theme->window_label_text_justify == LAB_JUSTIFY_LEFT) {
|
||||
/* TODO: maybe add some theme x padding here? */
|
||||
}
|
||||
wlr_scene_node_set_position(part->node, x, y);
|
||||
} FOR_EACH_END
|
||||
}
|
||||
|
||||
void
|
||||
ssd_update_title(struct view *view)
|
||||
{
|
||||
if (!view->ssd.tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *title = (char *)view_get_string_prop(view, "title");
|
||||
if (!title || !*title) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct theme *theme = view->server->theme;
|
||||
struct ssd_state_title *state = &view->ssd.state.title;
|
||||
bool title_unchanged = state->text && !strcmp(title, state->text);
|
||||
|
||||
/* TODO: Do we only have active window fonts? */
|
||||
struct font font = {
|
||||
.name = rc.font_name_activewindow,
|
||||
.size = rc.font_size_activewindow,
|
||||
};
|
||||
|
||||
float *text_color;
|
||||
struct wlr_scene_rect *rect;
|
||||
struct ssd_part *part;
|
||||
struct ssd_part *parent_part;
|
||||
struct ssd_sub_tree *subtree;
|
||||
struct ssd_state_title_width *dstate;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
parent_part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR);
|
||||
if (!parent_part) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Failed to update SSD title: parent node not found");
|
||||
continue;
|
||||
}
|
||||
rect = lab_wlr_scene_get_rect(parent_part->node);
|
||||
if (rect->width <= 0) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Failed to update SSD title: not enough screen space");
|
||||
continue;
|
||||
}
|
||||
if (subtree == &view->ssd.titlebar.active) {
|
||||
dstate = &state->active;
|
||||
text_color = theme->window_active_label_text_color;
|
||||
} else {
|
||||
dstate = &state->inactive;
|
||||
text_color = theme->window_inactive_label_text_color;
|
||||
}
|
||||
if (title_unchanged
|
||||
&& !dstate->truncated && dstate->width < rect->width) {
|
||||
/* title the same + we don't need to resize title */
|
||||
continue;
|
||||
}
|
||||
part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE);
|
||||
if (!part) {
|
||||
part = add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE);
|
||||
}
|
||||
if (part->node) {
|
||||
wlr_scene_node_destroy(part->node);
|
||||
}
|
||||
font_buffer_update(
|
||||
&part->buffer, rect->width, title, &font, text_color);
|
||||
part->node = &wlr_scene_buffer_create(
|
||||
parent_part->node, &part->buffer->base)->node;
|
||||
dstate->width = part->buffer->base.width;
|
||||
dstate->truncated = rect->width <= dstate->width;
|
||||
} FOR_EACH_END
|
||||
|
||||
if (state->text) {
|
||||
free(state->text);
|
||||
}
|
||||
state->text = strdup(title);
|
||||
ssd_update_title_positions(view);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the wlr_scene_node for hover effect.
|
||||
* To disable the hover effect later on just call
|
||||
* wlr_scene_node_set_enabled(node, false).
|
||||
*/
|
||||
struct wlr_scene_node *
|
||||
ssd_button_hover_enable(struct view *view, enum ssd_part_type type)
|
||||
{
|
||||
if (!view->ssd.tree) {
|
||||
wlr_log(WLR_ERROR, "%s() for destroyed view", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(ssd_is_button(type));
|
||||
struct ssd_part *part;
|
||||
struct ssd_sub_tree *subtree;
|
||||
FOR_EACH_STATE(view, subtree) {
|
||||
if (subtree->tree->node.state.enabled) {
|
||||
part = ssd_get_part(&subtree->parts, type);
|
||||
if (!part) {
|
||||
wlr_log(WLR_ERROR, "hover enable failed to find button");
|
||||
return NULL;
|
||||
}
|
||||
struct wlr_scene_node *child;
|
||||
wl_list_for_each(child, &part->node->state.children, state.link) {
|
||||
if (child->type == WLR_SCENE_NODE_RECT) {
|
||||
wlr_scene_node_set_enabled(child, true);
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
} FOR_EACH_END
|
||||
|
||||
wlr_log(WLR_ERROR, "hover enable failed to find button");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#undef FOR_EACH_STATE
|
||||
Loading…
Add table
Add a link
Reference in a new issue