mirror of
https://github.com/labwc/labwc.git
synced 2026-04-10 08:21:07 -04:00
ssd: unround hover effects when the window is tiled
This also fixes another problem that the window menu button as the fallback of the window icon is not rounded correctly. Now the translucent hover effect is stored separately from the non-hover icons and rendered on top of them. This might incur some overhead on pixman renderer, but it's not obvious in my testing.
This commit is contained in:
parent
915d638f8a
commit
35d6ada229
5 changed files with 150 additions and 145 deletions
|
|
@ -18,12 +18,18 @@
|
|||
struct ssd_button {
|
||||
struct view *view;
|
||||
enum ssd_part_type type;
|
||||
|
||||
struct wlr_scene_node *normal;
|
||||
struct wlr_scene_node *hover;
|
||||
struct wlr_scene_node *toggled;
|
||||
/*
|
||||
* Hover icons provided by user or builtin translucent hover overlay.
|
||||
* Hover overlays are rendered on top of normal/toggled nodes.
|
||||
*/
|
||||
struct wlr_scene_node *hover;
|
||||
struct wlr_scene_node *toggled_hover;
|
||||
struct wlr_scene_tree *icon_tree;
|
||||
struct wlr_scene_tree *hover_tree;
|
||||
|
||||
struct wlr_scene_tree *untoggled_tree;
|
||||
struct wlr_scene_tree *toggled_tree;
|
||||
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ enum lab_shape {
|
|||
LAB_CIRCLE,
|
||||
};
|
||||
|
||||
enum ssd_corner {
|
||||
LAB_CORNER_UNKNOWN = 0,
|
||||
LAB_CORNER_TOP_LEFT,
|
||||
LAB_CORNER_TOP_RIGHT,
|
||||
};
|
||||
|
||||
struct theme_snapping_overlay {
|
||||
bool bg_enabled;
|
||||
bool border_enabled;
|
||||
|
|
@ -170,6 +176,10 @@ struct theme {
|
|||
struct lab_data_buffer *button_omnipresent_inactive_hover;
|
||||
struct lab_data_buffer *button_exclusive_inactive_hover;
|
||||
|
||||
struct lab_data_buffer *button_hover_overlay_left;
|
||||
struct lab_data_buffer *button_hover_overlay_right;
|
||||
struct lab_data_buffer *button_hover_overlay_middle;
|
||||
|
||||
struct lab_data_buffer *corner_top_left_active_normal;
|
||||
struct lab_data_buffer *corner_top_right_active_normal;
|
||||
struct lab_data_buffer *corner_top_left_inactive_normal;
|
||||
|
|
|
|||
|
|
@ -137,12 +137,13 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
|
|||
rc.theme->window_button_width, rc.theme->title_height, 0, 0,
|
||||
invisible);
|
||||
|
||||
struct wlr_scene_tree *untoggled_tree = wlr_scene_tree_create(parent);
|
||||
|
||||
/* Icon */
|
||||
struct wlr_scene_tree *icon_tree = wlr_scene_tree_create(parent);
|
||||
struct wlr_box icon_geo = get_scale_box(icon_buffer,
|
||||
rc.theme->window_button_width, rc.theme->title_height);
|
||||
struct ssd_part *icon_part = add_scene_buffer(part_list, type,
|
||||
icon_tree, icon_buffer, icon_geo.x, icon_geo.y);
|
||||
untoggled_tree, icon_buffer, icon_geo.x, icon_geo.y);
|
||||
|
||||
/* Make sure big icons are scaled down if necessary */
|
||||
wlr_scene_buffer_set_dest_size(
|
||||
|
|
@ -150,18 +151,20 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
|
|||
icon_geo.width, icon_geo.height);
|
||||
|
||||
/* Hover icon */
|
||||
struct wlr_scene_tree *hover_tree = wlr_scene_tree_create(parent);
|
||||
wlr_scene_node_set_enabled(&hover_tree->node, false);
|
||||
struct wlr_box hover_geo = get_scale_box(hover_buffer,
|
||||
rc.theme->window_button_width, rc.theme->title_height);
|
||||
struct ssd_part *hover_part = add_scene_buffer(part_list, type,
|
||||
hover_tree, hover_buffer, hover_geo.x, hover_geo.y);
|
||||
|
||||
untoggled_tree, hover_buffer, hover_geo.x, hover_geo.y);
|
||||
wlr_scene_node_set_enabled(hover_part->node, false);
|
||||
/* Make sure big icons are scaled down if necessary */
|
||||
wlr_scene_buffer_set_dest_size(
|
||||
wlr_scene_buffer_from_node(hover_part->node),
|
||||
hover_geo.width, hover_geo.height);
|
||||
|
||||
/* Empty if add_toggled_icon() is not subsequently called */
|
||||
struct wlr_scene_tree *toggled_tree = wlr_scene_tree_create(parent);
|
||||
wlr_scene_node_set_enabled(&toggled_tree->node, false);
|
||||
|
||||
struct ssd_button *button = ssd_button_descriptor_create(button_root->node);
|
||||
button->type = type;
|
||||
button->view = view;
|
||||
|
|
@ -169,8 +172,8 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
|
|||
button->hover = hover_part->node;
|
||||
button->toggled = NULL;
|
||||
button->toggled_hover = NULL;
|
||||
button->icon_tree = icon_tree;
|
||||
button->hover_tree = hover_tree;
|
||||
button->untoggled_tree = untoggled_tree;
|
||||
button->toggled_tree = toggled_tree;
|
||||
return button_root;
|
||||
}
|
||||
|
||||
|
|
@ -184,18 +187,16 @@ add_toggled_icon(struct ssd_button *button, struct wl_list *part_list,
|
|||
rc.theme->window_button_width, rc.theme->title_height);
|
||||
|
||||
struct ssd_part *alticon_part = add_scene_buffer(part_list, type,
|
||||
button->icon_tree, icon_buffer, icon_geo.x, icon_geo.y);
|
||||
button->toggled_tree, icon_buffer, icon_geo.x, icon_geo.y);
|
||||
|
||||
wlr_scene_buffer_set_dest_size(
|
||||
wlr_scene_buffer_from_node(alticon_part->node),
|
||||
icon_geo.width, icon_geo.height);
|
||||
|
||||
wlr_scene_node_set_enabled(alticon_part->node, false);
|
||||
|
||||
struct wlr_box hover_geo = get_scale_box(hover_buffer,
|
||||
rc.theme->window_button_width, rc.theme->title_height);
|
||||
struct ssd_part *althover_part = add_scene_buffer(part_list, type,
|
||||
button->hover_tree, hover_buffer, hover_geo.x, hover_geo.y);
|
||||
button->toggled_tree, hover_buffer, hover_geo.x, hover_geo.y);
|
||||
|
||||
wlr_scene_buffer_set_dest_size(
|
||||
wlr_scene_buffer_from_node(althover_part->node),
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
static void set_squared_corners(struct ssd *ssd, bool enable);
|
||||
static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable);
|
||||
static void set_hover_overlays_squared(struct ssd *ssd, bool squared);
|
||||
static void update_visible_buttons(struct ssd *ssd);
|
||||
|
||||
static void
|
||||
|
|
@ -174,6 +175,7 @@ ssd_titlebar_create(struct ssd *ssd)
|
|||
} FOR_EACH_END
|
||||
|
||||
update_visible_buttons(ssd);
|
||||
set_hover_overlays_squared(ssd, false);
|
||||
|
||||
ssd_update_title(ssd);
|
||||
ssd_update_window_icon(ssd);
|
||||
|
|
@ -223,6 +225,8 @@ set_squared_corners(struct ssd *ssd, bool enable)
|
|||
part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT);
|
||||
wlr_scene_node_set_enabled(part->node, !enable);
|
||||
} FOR_EACH_END
|
||||
|
||||
set_hover_overlays_squared(ssd, enable);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -241,13 +245,8 @@ set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable)
|
|||
button = node_ssd_button_from_node(part->node);
|
||||
|
||||
if (button->toggled) {
|
||||
wlr_scene_node_set_enabled(button->toggled, enable);
|
||||
wlr_scene_node_set_enabled(button->normal, !enable);
|
||||
}
|
||||
|
||||
if (button->toggled_hover) {
|
||||
wlr_scene_node_set_enabled(button->toggled_hover, enable);
|
||||
wlr_scene_node_set_enabled(button->hover, !enable);
|
||||
wlr_scene_node_set_enabled(&button->toggled_tree->node, enable);
|
||||
wlr_scene_node_set_enabled(&button->untoggled_tree->node, !enable);
|
||||
}
|
||||
} FOR_EACH_END
|
||||
}
|
||||
|
|
@ -583,12 +582,98 @@ ssd_update_title(struct ssd *ssd)
|
|||
ssd_update_title_positions(ssd, offset_left, offset_right);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_hover_overlay_buffer(struct wlr_scene_node *node)
|
||||
{
|
||||
struct wlr_scene_buffer *scene_buffer =
|
||||
wlr_scene_buffer_from_node(node);
|
||||
return scene_buffer->buffer == &rc.theme->button_hover_overlay_left->base
|
||||
|| scene_buffer->buffer == &rc.theme->button_hover_overlay_right->base
|
||||
|| scene_buffer->buffer == &rc.theme->button_hover_overlay_middle->base;
|
||||
}
|
||||
|
||||
static struct wlr_buffer *
|
||||
hover_overlay_for_corner(enum ssd_corner corner)
|
||||
{
|
||||
switch (corner) {
|
||||
case LAB_CORNER_TOP_LEFT:
|
||||
return &rc.theme->button_hover_overlay_left->base;
|
||||
case LAB_CORNER_TOP_RIGHT:
|
||||
return &rc.theme->button_hover_overlay_right->base;
|
||||
case LAB_CORNER_UNKNOWN:
|
||||
return &rc.theme->button_hover_overlay_middle->base;
|
||||
default:
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_hover_overlay(struct ssd_button *button, enum ssd_corner corner)
|
||||
{
|
||||
assert(button);
|
||||
|
||||
/*
|
||||
* When button->(toggled_)hover is the builtin hover overlay (not an
|
||||
* icon provided by user), update its shape.
|
||||
*/
|
||||
if (is_hover_overlay_buffer(button->hover)) {
|
||||
wlr_scene_buffer_set_buffer(
|
||||
wlr_scene_buffer_from_node(button->hover),
|
||||
hover_overlay_for_corner(corner));
|
||||
}
|
||||
if (button->toggled_hover
|
||||
&& is_hover_overlay_buffer(button->toggled_hover)) {
|
||||
wlr_scene_buffer_set_buffer(
|
||||
wlr_scene_buffer_from_node(button->toggled_hover),
|
||||
hover_overlay_for_corner(corner));
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the shape of hover overlay on corner buttons */
|
||||
static void
|
||||
set_hover_overlays_squared(struct ssd *ssd, bool squared)
|
||||
{
|
||||
struct ssd_part *part;
|
||||
struct ssd_sub_tree *subtree;
|
||||
|
||||
FOR_EACH_STATE(ssd, subtree) {
|
||||
struct title_button *b;
|
||||
wl_list_for_each(b, &rc.title_buttons_left, link) {
|
||||
part = ssd_get_part(&subtree->parts, b->type);
|
||||
struct ssd_button *button = node_ssd_button_from_node(part->node);
|
||||
set_hover_overlay(button,
|
||||
squared ? LAB_CORNER_UNKNOWN : LAB_CORNER_TOP_LEFT);
|
||||
break;
|
||||
}
|
||||
wl_list_for_each_reverse(b, &rc.title_buttons_right, link) {
|
||||
part = ssd_get_part(&subtree->parts, b->type);
|
||||
struct ssd_button *button = node_ssd_button_from_node(part->node);
|
||||
set_hover_overlay(button,
|
||||
squared ? LAB_CORNER_UNKNOWN : LAB_CORNER_TOP_RIGHT);
|
||||
break;
|
||||
}
|
||||
} FOR_EACH_END
|
||||
}
|
||||
|
||||
static void
|
||||
ssd_button_set_hover(struct ssd_button *button, bool enabled)
|
||||
{
|
||||
assert(button);
|
||||
wlr_scene_node_set_enabled(&button->hover_tree->node, enabled);
|
||||
wlr_scene_node_set_enabled(&button->icon_tree->node, !enabled);
|
||||
|
||||
/*
|
||||
* Keep showing non-hover icon when the hover icon is not provided and
|
||||
* hover overlay is shown on top of it.
|
||||
*/
|
||||
wlr_scene_node_set_enabled(button->normal,
|
||||
is_hover_overlay_buffer(button->hover) || !enabled);
|
||||
wlr_scene_node_set_enabled(button->hover, enabled);
|
||||
|
||||
if (button->toggled) {
|
||||
wlr_scene_node_set_enabled(button->toggled,
|
||||
is_hover_overlay_buffer(button->toggled_hover) || !enabled);
|
||||
wlr_scene_node_set_enabled(button->toggled_hover, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
145
src/theme.c
145
src/theme.c
|
|
@ -51,19 +51,13 @@ struct button {
|
|||
} active, inactive;
|
||||
};
|
||||
|
||||
enum corner {
|
||||
LAB_CORNER_UNKNOWN = 0,
|
||||
LAB_CORNER_TOP_LEFT,
|
||||
LAB_CORNER_TOP_RIGHT,
|
||||
};
|
||||
|
||||
struct rounded_corner_ctx {
|
||||
struct wlr_box *box;
|
||||
double radius;
|
||||
double line_width;
|
||||
float *fill_color;
|
||||
float *border_color;
|
||||
enum corner corner;
|
||||
enum ssd_corner corner;
|
||||
};
|
||||
|
||||
#define zero_array(arr) memset(arr, 0, sizeof(arr))
|
||||
|
|
@ -82,91 +76,19 @@ zdrop(struct lab_data_buffer **buffer)
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
match_button_by_name(struct title_button *b, const char *icon_name)
|
||||
static struct lab_data_buffer *
|
||||
create_hover_overlay(struct theme *theme, enum ssd_corner corner)
|
||||
{
|
||||
assert(icon_name);
|
||||
return (b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu"))
|
||||
|| (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max"))
|
||||
|| (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled"))
|
||||
|| (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify"))
|
||||
|| (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close"))
|
||||
|| (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade"))
|
||||
|| (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade_toggled"))
|
||||
|| (b->type == LAB_SSD_BUTTON_OMNIPRESENT && !strcmp(icon_name, "desk"))
|
||||
|| (b->type == LAB_SSD_BUTTON_OMNIPRESENT && !strcmp(icon_name, "desk_toggled"));
|
||||
}
|
||||
|
||||
static enum corner
|
||||
corner_from_icon_name(const char *icon_name)
|
||||
{
|
||||
assert(icon_name);
|
||||
|
||||
struct title_button *b;
|
||||
wl_list_for_each(b, &rc.title_buttons_left, link) {
|
||||
if (match_button_by_name(b, icon_name)) {
|
||||
return LAB_CORNER_TOP_LEFT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
wl_list_for_each_reverse(b, &rc.title_buttons_right, link) {
|
||||
if (match_button_by_name(b, icon_name)) {
|
||||
return LAB_CORNER_TOP_RIGHT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return LAB_CORNER_UNKNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
create_hover_fallback(struct theme *theme, const char *icon_name,
|
||||
struct lab_data_buffer **hover_buffer,
|
||||
struct lab_data_buffer *icon_buffer)
|
||||
{
|
||||
assert(icon_name);
|
||||
assert(icon_buffer);
|
||||
assert(!*hover_buffer);
|
||||
|
||||
struct surface_context icon =
|
||||
get_cairo_surface_from_lab_data_buffer(icon_buffer);
|
||||
int icon_width = cairo_image_surface_get_width(icon.surface);
|
||||
int icon_height = cairo_image_surface_get_height(icon.surface);
|
||||
|
||||
int width = theme->window_button_width;
|
||||
int height = theme->title_height;
|
||||
|
||||
if (width && height) {
|
||||
/*
|
||||
* Proportionately increase size of hover_buffer if the
|
||||
* non-hover 'donor' buffer is larger than the allocated space.
|
||||
* It will get scaled down again by wlroots when rendered and as
|
||||
* required by the current output scale.
|
||||
*
|
||||
* This ensures that icons > width or > height keep their aspect
|
||||
* ratio and are rendered the same as without the hover overlay.
|
||||
*/
|
||||
double scale = MAX((double)icon_width / width,
|
||||
(double)icon_height / height);
|
||||
if (scale > 1.0f) {
|
||||
width = (double)width * scale;
|
||||
height = (double)height * scale;
|
||||
}
|
||||
}
|
||||
struct lab_data_buffer *hover_buffer = buffer_create_cairo(width, height, 1.0, true);
|
||||
|
||||
*hover_buffer = buffer_create_cairo(width, height, 1.0, true);
|
||||
|
||||
cairo_t *cairo = (*hover_buffer)->cairo;
|
||||
cairo_t *cairo = hover_buffer->cairo;
|
||||
cairo_surface_t *surf = cairo_get_target(cairo);
|
||||
|
||||
/* Background */
|
||||
cairo_set_source_surface(cairo, icon.surface,
|
||||
(width - icon_width) / 2, (height - icon_height) / 2);
|
||||
cairo_paint(cairo);
|
||||
|
||||
/* Overlay (pre-multiplied alpha) */
|
||||
float overlay_color[4] = { 0.15f, 0.15f, 0.15f, 0.3f};
|
||||
int radius = MIN(width, height) / 2;
|
||||
enum corner corner = corner_from_icon_name(icon_name);
|
||||
|
||||
switch (theme->window_button_hover_bg_shape) {
|
||||
case LAB_CIRCLE:
|
||||
|
|
@ -217,9 +139,7 @@ create_hover_fallback(struct theme *theme, const char *icon_name,
|
|||
}
|
||||
cairo_surface_flush(surf);
|
||||
|
||||
if (icon.is_duplicate) {
|
||||
cairo_surface_destroy(icon.surface);
|
||||
}
|
||||
return hover_buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -416,6 +336,13 @@ load_buttons(struct theme *theme)
|
|||
.inactive.rgba = theme->window_inactive_button_close_unpressed_image_color,
|
||||
}, };
|
||||
|
||||
theme->button_hover_overlay_left =
|
||||
create_hover_overlay(theme, LAB_CORNER_TOP_LEFT);
|
||||
theme->button_hover_overlay_right =
|
||||
create_hover_overlay(theme, LAB_CORNER_TOP_RIGHT);
|
||||
theme->button_hover_overlay_middle =
|
||||
create_hover_overlay(theme, LAB_CORNER_UNKNOWN);
|
||||
|
||||
char filename[4096] = {0};
|
||||
for (size_t i = 0; i < ARRAY_SIZE(buttons); ++i) {
|
||||
struct button *b = &buttons[i];
|
||||
|
|
@ -473,49 +400,25 @@ load_buttons(struct theme *theme)
|
|||
* Applicable to basic buttons such as max, max_toggled and
|
||||
* iconify. There are no bitmap fallbacks for *_hover icons.
|
||||
*/
|
||||
if (!b->fallback_button) {
|
||||
continue;
|
||||
}
|
||||
if (!*b->active.buffer) {
|
||||
if (!*b->active.buffer && b->fallback_button) {
|
||||
img_xbm_from_bitmap(b->fallback_button,
|
||||
b->active.buffer, b->active.rgba);
|
||||
}
|
||||
if (!*b->inactive.buffer) {
|
||||
if (!*b->inactive.buffer && b->fallback_button) {
|
||||
img_xbm_from_bitmap(b->fallback_button,
|
||||
b->inactive.buffer, b->inactive.rgba);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If hover-icons do not exist, add fallbacks by copying the non-hover
|
||||
* variant (base) and then adding an overlay.
|
||||
*/
|
||||
for (size_t i = 0; i < ARRAY_SIZE(buttons); i++) {
|
||||
struct button *hover_button = &buttons[i];
|
||||
|
||||
if (!strstr(hover_button->name, "_hover")) {
|
||||
continue;
|
||||
/*
|
||||
* When *_hover icon is not provided, set hover overlay instead.
|
||||
* This is rendered on top of non-hover icons. Their shapes are
|
||||
* updated on SSD creation.
|
||||
*/
|
||||
if (!*b->active.buffer) {
|
||||
*b->active.buffer = theme->button_hover_overlay_middle;
|
||||
}
|
||||
|
||||
/* If name=='foo_hover', basename='foo' */
|
||||
char basename[64] = {0};
|
||||
snprintf(basename, sizeof(basename), "%s", hover_button->name);
|
||||
trim_last_field(basename, '_');
|
||||
for (size_t j = 0; j < ARRAY_SIZE(buttons); j++) {
|
||||
struct button *base = &buttons[j];
|
||||
if (!strcmp(basename, base->name)) {
|
||||
if (!*hover_button->active.buffer) {
|
||||
create_hover_fallback(theme, basename,
|
||||
hover_button->active.buffer,
|
||||
*base->active.buffer);
|
||||
}
|
||||
if (!*hover_button->inactive.buffer) {
|
||||
create_hover_fallback(theme, basename,
|
||||
hover_button->inactive.buffer,
|
||||
*base->inactive.buffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!*b->inactive.buffer) {
|
||||
*b->inactive.buffer = theme->button_hover_overlay_middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue