]> git.mdlowis.com Git - proto/labwc.git/commitdiff
ssd: allow arbitrary cairo pattern as titlebar background
authorJohn Lindgren <john@jlindgren.net>
Tue, 22 Apr 2025 16:20:15 +0000 (12:20 -0400)
committerJohn Lindgren <john@jlindgren.net>
Wed, 18 Jun 2025 19:48:24 +0000 (15:48 -0400)
The titlebar background is now first rendered to a 1px wide buffer,
then stretched horizontally. This allows vertical gradients to be used.

include/theme.h
src/ssd/ssd-titlebar.c
src/theme.c

index 283538d484137765c73cfd5b5b6b82e6d450d87e..9fe2857ceed875ec6ecc00fa042b1081a5898460 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef LABWC_THEME_H
 #define LABWC_THEME_H
 
-#include <stdio.h>
+#include <cairo.h>
 #include <wlr/render/wlr_renderer.h>
 #include "ssd.h"
 
@@ -89,6 +89,18 @@ struct theme {
                struct lab_img *button_imgs
                        [LAB_SSD_BUTTON_LAST + 1][LAB_BS_ALL + 1];
 
+               /*
+                * The titlebar background is specified as a cairo_pattern
+                * and then also rendered into a 1px wide buffer, which is
+                * stretched horizontally across the titlebar.
+                *
+                * This approach enables vertical gradients while saving
+                * some memory vs. rendering the entire titlebar into an
+                * image. It does not work for horizontal gradients.
+                */
+               cairo_pattern_t *titlebar_pattern;
+               struct lab_data_buffer *titlebar_fill;
+
                struct lab_data_buffer *corner_top_left_normal;
                struct lab_data_buffer *corner_top_right_normal;
 
index 84d28f751c2b837e1d15a096b0db4e19163c74c0..e57134ad4afd99a5553e5ccdca13d81a5884f9f0 100644 (file)
@@ -35,8 +35,8 @@ ssd_titlebar_create(struct ssd *ssd)
        int width = view->current.width;
        int corner_width = ssd_get_corner_width();
 
-       float *color;
        struct wlr_scene_tree *parent;
+       struct wlr_buffer *titlebar_fill;
        struct wlr_buffer *corner_top_left;
        struct wlr_buffer *corner_top_right;
        int active;
@@ -49,7 +49,7 @@ ssd_titlebar_create(struct ssd *ssd)
                parent = subtree->tree;
                active = (subtree == &ssd->titlebar.active) ?
                        THEME_ACTIVE : THEME_INACTIVE;
-               color = theme->window[active].title_bg_color;
+               titlebar_fill = &theme->window[active].titlebar_fill->base;
                corner_top_left = &theme->window[active].corner_top_left_normal->base;
                corner_top_right = &theme->window[active].corner_top_right_normal->base;
                wlr_scene_node_set_enabled(&parent->node, active);
@@ -57,9 +57,8 @@ ssd_titlebar_create(struct ssd *ssd)
                wl_list_init(&subtree->parts);
 
                /* Background */
-               add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent,
-                       MAX(width - corner_width * 2, 0), theme->titlebar_height,
-                       corner_width, 0, color);
+               add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent,
+                       titlebar_fill, corner_width, 0);
                add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent,
                        corner_top_left, -rc.theme->border_width, -rc.theme->border_width);
                add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, parent,
@@ -151,7 +150,8 @@ set_squared_corners(struct ssd *ssd, bool enable)
        FOR_EACH_STATE(ssd, subtree) {
                part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR);
                wlr_scene_node_set_position(part->node, x, 0);
-               wlr_scene_rect_set_size(wlr_scene_rect_from_node(part->node),
+               wlr_scene_buffer_set_dest_size(
+                       wlr_scene_buffer_from_node(part->node),
                        MAX(width - 2 * x, 0), theme->titlebar_height);
 
                part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT);
@@ -299,8 +299,8 @@ ssd_titlebar_update(struct ssd *ssd)
        int bg_offset = maximized || squared ? 0 : corner_width;
        FOR_EACH_STATE(ssd, subtree) {
                part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR);
-               wlr_scene_rect_set_size(
-                       wlr_scene_rect_from_node(part->node),
+               wlr_scene_buffer_set_dest_size(
+                       wlr_scene_buffer_from_node(part->node),
                        MAX(width - bg_offset * 2, 0), theme->titlebar_height);
 
                x = theme->window_titlebar_padding_width;
@@ -458,7 +458,7 @@ ssd_update_title(struct ssd *ssd)
        bool title_unchanged = state->text && !strcmp(title, state->text);
 
        const float *text_color;
-       const float *bg_color;
+       const float bg_color[4] = {0, 0, 0, 0}; /* ignored */
        struct font *font = NULL;
        struct ssd_part *part;
        struct ssd_sub_tree *subtree;
@@ -474,7 +474,6 @@ ssd_update_title(struct ssd *ssd)
                        THEME_ACTIVE : THEME_INACTIVE;
                dstate = active ? &state->active : &state->inactive;
                text_color = theme->window[active].label_text_color;
-               bg_color = theme->window[active].title_bg_color;
                font = active ?  &rc.font_activewindow : &rc.font_inactivewindow;
 
                if (title_bg_width <= 0) {
@@ -492,7 +491,9 @@ ssd_update_title(struct ssd *ssd)
                if (!part) {
                        /* Initialize part and wlr_scene_buffer without attaching a buffer */
                        part = add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE);
-                       part->buffer = scaled_font_buffer_create(subtree->tree);
+                       part->buffer = scaled_font_buffer_create_for_titlebar(
+                               subtree->tree, theme->titlebar_height,
+                               theme->window[active].titlebar_pattern);
                        if (part->buffer) {
                                part->node = &part->buffer->scene_buffer->node;
                        } else {
index abc13010ca5bc755eda6fbbefabbdd610684520d..90fb21c87edb7795cc89ef29878e7d7603f72250 100644 (file)
@@ -53,7 +53,7 @@ struct rounded_corner_ctx {
        struct wlr_box *box;
        double radius;
        double line_width;
-       float *fill_color;
+       cairo_pattern_t *fill_pattern;
        float *border_color;
        enum corner corner;
 };
@@ -1109,10 +1109,24 @@ rounded_rect(struct rounded_corner_ctx *ctx)
        }
        cairo_close_path(cairo);
        cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
-       set_cairo_color(cairo, ctx->fill_color);
+       /*
+        * We need to offset the fill pattern vertically by the border
+        * width to line up with the rest of the titlebar. This is done
+        * by applying a transformation matrix to the pattern temporarily.
+        * It would be better to copy the pattern, but cairo does not
+        * provide a simple way to this.
+        */
+       cairo_matrix_t matrix;
+       cairo_matrix_init_translate(&matrix, 0, -ctx->line_width);
+       cairo_pattern_set_matrix(ctx->fill_pattern, &matrix);
+       cairo_set_source(cairo, ctx->fill_pattern);
        cairo_fill_preserve(cairo);
        cairo_stroke(cairo);
 
+       /* Reset the fill pattern transformation matrix afterward */
+       cairo_matrix_init_identity(&matrix);
+       cairo_pattern_set_matrix(ctx->fill_pattern, &matrix);
+
        /*
         * Stroke horizontal and vertical borders, shown by Xs and Ys
         * respectively in the figure below:
@@ -1215,6 +1229,33 @@ out:
        return buffer;
 }
 
+static struct lab_data_buffer *
+create_titlebar_fill(cairo_pattern_t *pattern, int height)
+{
+       /* create 1px wide buffer to be stretched horizontally */
+       struct lab_data_buffer *fill = buffer_create_cairo(1, height, 1);
+
+       cairo_t *cairo = cairo_create(fill->surface);
+       cairo_set_source(cairo, pattern);
+       cairo_paint(cairo);
+       cairo_surface_flush(fill->surface);
+       cairo_destroy(cairo);
+
+       return fill;
+}
+
+static void
+create_backgrounds(struct theme *theme)
+{
+       for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) {
+               theme->window[active].titlebar_pattern = color_to_pattern(
+                       theme->window[active].title_bg_color);
+               theme->window[active].titlebar_fill = create_titlebar_fill(
+                       theme->window[active].titlebar_pattern,
+                       theme->titlebar_height);
+       }
+}
+
 static void
 create_corners(struct theme *theme)
 {
@@ -1232,7 +1273,7 @@ create_corners(struct theme *theme)
                        .box = &box,
                        .radius = rc.corner_radius,
                        .line_width = theme->border_width,
-                       .fill_color = theme->window[active].title_bg_color,
+                       .fill_pattern = theme->window[active].titlebar_pattern,
                        .border_color = theme->window[active].border_color,
                        .corner = LAB_CORNER_TOP_LEFT,
                };
@@ -1567,6 +1608,7 @@ theme_init(struct theme *theme, struct server *server, const char *theme_name)
        paths_destroy(&paths);
 
        post_processing(theme);
+       create_backgrounds(theme);
        create_corners(theme);
        load_buttons(theme);
        create_shadows(theme);
@@ -1593,6 +1635,8 @@ theme_finish(struct theme *theme)
        }
 
        for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) {
+               zfree_pattern(theme->window[active].titlebar_pattern);
+               zdrop(&theme->window[active].titlebar_fill);
                zdrop(&theme->window[active].corner_top_left_normal);
                zdrop(&theme->window[active].corner_top_right_normal);
                zdrop(&theme->window[active].shadow_corner_top);