theme: create hover button fallbacks

...by copying the non-hover variant and adding a transparent overlay.

Co-authored-by: @johanmalm
This commit is contained in:
Consolatis 2023-12-17 02:16:49 +01:00
parent d207e97992
commit 27de4e6398
6 changed files with 150 additions and 0 deletions

View file

@ -47,4 +47,14 @@ void set_cairo_color(cairo_t *cairo, float *color);
/* Draws a border with a specified line width */
void draw_cairo_border(cairo_t *cairo, struct wlr_fbox fbox, double line_width);
struct lab_data_buffer;
struct surface_context {
bool is_duplicate;
cairo_surface_t *surface;
};
struct surface_context get_cairo_surface_from_lab_data_buffer(
struct lab_data_buffer *buffer);
#endif /* LABWC_GRAPHIC_HELPERS_H */

View file

@ -2,6 +2,15 @@
#ifndef LABWC_STRING_HELPERS_H
#define LABWC_STRING_HELPERS_H
/**
* trim_last_field() - Trim last field of string splitting on provided delim
* @buf: string to trim
* @delim: delimitator
*
* Example: With delim='_' and buf="foo_bar_baz" the return value is "foo_bar"
*/
void trim_last_field(char *buf, char delim);
/**
* string_strip - strip white space left and right
* Note: this function does a left skip, so the returning pointer cannot be

View file

@ -129,6 +129,8 @@ buffer_create_wrap(void *pixel_data, uint32_t width, uint32_t height,
{
struct lab_data_buffer *buffer = znew(*buffer);
wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height);
buffer->unscaled_width = width;
buffer->unscaled_height = height;
buffer->data = pixel_data;
buffer->format = DRM_FORMAT_ARGB8888;
buffer->stride = stride;

View file

@ -3,8 +3,10 @@
#include <assert.h>
#include <cairo.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/box.h>
#include "buffer.h"
#include "common/graphic-helpers.h"
#include "common/mem.h"
@ -85,3 +87,32 @@ set_cairo_color(cairo_t *cairo, float *c)
{
cairo_set_source_rgba(cairo, c[0], c[1], c[2], c[3]);
}
struct surface_context
get_cairo_surface_from_lab_data_buffer(struct lab_data_buffer *buffer)
{
/* Handle CAIRO_FORMAT_ARGB32 buffers */
if (buffer->cairo) {
return (struct surface_context){
.is_duplicate = false,
.surface = cairo_get_target(buffer->cairo),
};
}
/* Handle DRM_FORMAT_ARGB8888 buffers */
int w = buffer->unscaled_width;
int h = buffer->unscaled_height;
cairo_surface_t *surface =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
if (!surface) {
return (struct surface_context){0};
}
unsigned char *data = cairo_image_surface_get_data(surface);
cairo_surface_flush(surface);
memcpy(data, buffer->data, h * buffer->stride);
cairo_surface_mark_dirty(surface);
return (struct surface_context){
.is_duplicate = true,
.surface = surface,
};
}

View file

@ -6,6 +6,15 @@
#include "common/mem.h"
#include "common/string-helpers.h"
void
trim_last_field(char *buf, char delim)
{
char *p = strrchr(buf, delim);
if (p) {
*p = '\0';
}
}
static void
rtrim(char **s)
{

View file

@ -7,6 +7,7 @@
#define _POSIX_C_SOURCE 200809L
#include "config.h"
#include <assert.h>
#include <cairo.h>
#include <ctype.h>
#include <drm_fourcc.h>
@ -56,6 +57,61 @@ drop(struct lab_data_buffer **buffer)
}
}
static void
create_hover_fallback(struct theme *theme, struct lab_data_buffer **hover_buffer,
struct lab_data_buffer *icon_buffer)
{
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);
/* TODO: need to somehow respect rounded corners */
int width = SSD_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;
}
}
*hover_buffer = buffer_create_cairo(width, height, 1.0, true);
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 (non-multiplied alpha) */
set_cairo_color(cairo, (float[4]) { 0.5f, 0.5f, 0.5f, 0.3f});
cairo_rectangle(cairo, 0, 0, width, height);
cairo_fill(cairo);
cairo_surface_flush(surf);
if (icon.is_duplicate) {
cairo_surface_destroy(icon.surface);
}
}
/*
* We use the following button filename schema: "BUTTON [TOGGLED] [STATE]"
* with the words separted by underscore, and the following meaning:
@ -204,6 +260,39 @@ load_buttons(struct theme *theme)
b->fallback_button, b->inactive.rgba);
}
}
/*
* If hover-icons do not exist, add fallbacks by coping 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;
}
/* 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,
hover_button->active.buffer,
*base->active.buffer);
}
if (!*hover_button->inactive.buffer) {
create_hover_fallback(theme,
hover_button->inactive.buffer,
*base->inactive.buffer);
}
break;
}
}
}
}
static int