]> git.mdlowis.com Git - proto/labwc.git/commitdiff
theme: create hover button fallbacks
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Sun, 17 Dec 2023 01:16:49 +0000 (02:16 +0100)
committerConsolatis <35009135+Consolatis@users.noreply.github.com>
Thu, 21 Dec 2023 01:42:23 +0000 (02:42 +0100)
...by copying the non-hover variant and adding a transparent overlay.

Co-authored-by: @johanmalm
include/common/graphic-helpers.h
include/common/string-helpers.h
src/buffer.c
src/common/graphic-helpers.c
src/common/string-helpers.c
src/theme.c

index 7bdd1246c8d39051f1ae7dcece818c65725a1677..ccf170223be261bb3fa5c1c8a93795d748829634 100644 (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 */
index 731f93fee6f39658e39c14f3ce6629d22dae3ef6..20f74d5a3f3ab943c0696903211d6ff19360e015 100644 (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
index e96c4d883a7beba53ce7a8a13c8f928efb387867..0921acdd093c7a27879bc234cc3bf5fcf91235f0 100644 (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;
index 9f8141bd81c46e8c783e76882a4af4a93ea84de7..862decd94a1350bf4803af2ed038c5cf674b3f12 100644 (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,
+       };
+}
index 01ea09f4688bf955bac3bde42044a0f324735919..ec2c86bd911a1e949e82310c5a6b7c52bd646a5f 100644 (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)
 {
index ce71a4d49107b3442845bc27d1d2888e86b8e287..e7e52cccb10586e1479c5baaa84aac4bf5fb0f1d 100644 (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