diff --git a/docs/themerc b/docs/themerc index 83ea726b..195f36a0 100644 --- a/docs/themerc +++ b/docs/themerc @@ -6,7 +6,9 @@ # make sure all other lines are commented out or deleted. # general -border.width: 1 +border.width: 6 +border.beveled: yes +border.bevel_width:2 # # The global padding.{width,height} of openbox are not supported because diff --git a/include/common/borderset.h b/include/common/borderset.h new file mode 100644 index 00000000..d4016d8c --- /dev/null +++ b/include/common/borderset.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include +#include + +#ifndef LABWC_BORDERSET_H +#define LABWC_BORDERSET_H + +enum border_type { + BORDER_NONE, BORDER_SINGLE, BORDER_DOUBLE, BORDER_INSET, BORDER_DOUBLE_INSET, BORDER_FLAT +}; + +struct borderset { + // Base colour, but could be used as a tracking hash for images or whatever in the future + uint32_t id; + // width (since I suspect a 2px border scaled up to 20px might look weird) + int size; + // Single or double bevel, etc. + enum border_type type; + // So we can disambiguate multiple possible designs cached together + int bevelSize; + struct lab_data_buffer *top; + struct lab_data_buffer *left; + struct lab_data_buffer *right; + struct lab_data_buffer *bottom; + struct lab_data_buffer *tl; + struct lab_data_buffer *tr; + struct lab_data_buffer *bl; + struct lab_data_buffer *br; + struct borderset *next; +}; + +struct bufferset { + enum border_type type; + int border_width; + struct wlr_scene_buffer *top; + struct wlr_scene_buffer *left; + struct wlr_scene_buffer *right; + struct wlr_scene_buffer *bottom; + struct wlr_scene_buffer *tl; + struct wlr_scene_buffer *tr; + struct wlr_scene_buffer *bl; + struct wlr_scene_buffer *br; + +}; + +extern struct borderset *border_cache; + +struct borderset *get_borders(uint32_t id, int size, enum border_type, int bevelSize); + +struct borderset *create_buffer(uint32_t id, int size, enum border_type, int bevelSize); + +struct bufferset *generate_bufferset(struct wlr_scene_tree *tree, + struct borderset *borderset, int bw); + +void renderBufferset(struct bufferset *bufferset, int width, int height, int y); + +void renderBuffersetXY(struct bufferset *bufferset, int width, int height, int x, int y); + +void clearborder_cache(struct borderset *borderset); + +#endif /* LABWC_BORDERSET_H */ diff --git a/include/common/lab-scene-rect.h b/include/common/lab-scene-rect.h index 5005fbb3..9bab96b2 100644 --- a/include/common/lab-scene-rect.h +++ b/include/common/lab-scene-rect.h @@ -2,6 +2,7 @@ #ifndef LABWC_LAB_SCENE_RECT_H #define LABWC_LAB_SCENE_RECT_H #include +#include "common/borderset.h" struct wlr_scene_tree; @@ -12,6 +13,8 @@ struct lab_scene_rect_options { float *bg_color; /* can be NULL */ int width; int height; + enum border_type border_type; + int bevel_width; }; struct lab_scene_rect { diff --git a/include/common/macros.h b/include/common/macros.h index 25226261..f3240206 100644 --- a/include/common/macros.h +++ b/include/common/macros.h @@ -65,4 +65,15 @@ #define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) \ (WLR_VERSION_NUM >= (((major) << 16) | ((minor) << 8) | (micro))) +/** + * PIXEL () - calculate pixel offset in an array + * + * @param x x-coordinate + * @param y y-coordinate + * @param size width of the buffer in pixes + * Assumes "bw" was defined externally + */ + +#define PIXEL(x, y, size) (size * y + x) + #endif /* LABWC_MACROS_H */ diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 600b9076..38280582 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -106,6 +106,7 @@ struct ssd { struct scaled_font_buffer *title; struct wl_list buttons_left; /* ssd_button.link */ struct wl_list buttons_right; /* ssd_button.link */ + struct bufferset *textured_borders; } subtrees[2]; /* indexed by enum ssd_active_state */ } titlebar; @@ -115,6 +116,7 @@ struct ssd { struct ssd_border_subtree { struct wlr_scene_tree *tree; struct wlr_scene_rect *top, *bottom, *left, *right; + struct bufferset *textured_borders; } subtrees[2]; /* indexed by enum ssd_active_state */ } border; diff --git a/include/theme.h b/include/theme.h index 2fb499a9..f28c0cab 100644 --- a/include/theme.h +++ b/include/theme.h @@ -11,6 +11,7 @@ #include #include #include "common/node-type.h" +#include "common/borderset.h" struct lab_img; @@ -56,6 +57,10 @@ struct theme_background { float color_split_to[4]; float color_to[4]; float color_to_split_to[4]; + enum border_type border_type; + int border_width; + int bevel_width; + bool exclusive; }; struct theme { @@ -92,10 +97,17 @@ struct theme { /* TODO: add toggled/hover/pressed/disabled colors for buttons */ float button_colors[LAB_NODE_BUTTON_LAST + 1][4]; + enum border_type button_border_type; + int button_border_width; + int button_bevel_width; + float button_border_color[4]; + float button_hover_border_color[4]; float border_color[4]; float toggled_keybinds_color[4]; float label_text_color[4]; + enum border_type border_type; + int bevel_width; /* window drop-shadows */ int shadow_size; @@ -142,6 +154,8 @@ struct theme { int menu_max_width; int menu_border_width; float menu_border_color[4]; + enum border_type menu_border_type; + int menu_bevel_width; int menu_items_padding_x; int menu_items_padding_y; @@ -149,6 +163,12 @@ struct theme { float menu_items_text_color[4]; float menu_items_active_bg_color[4]; float menu_items_active_text_color[4]; + enum border_type menu_items_border_type; + int menu_items_bevel_width; + enum border_type menu_title_border_type; + int menu_title_bevel_width; + enum border_type menu_items_active_border_type; + int menu_items_active_bevel_width; int menu_separator_line_thickness; int menu_separator_padding_width; @@ -164,6 +184,8 @@ struct theme { float osd_bg_color[4]; float osd_border_color[4]; float osd_label_text_color[4]; + enum border_type osd_border_type; + int osd_border_bevel_width; struct window_switcher_classic_theme { int width; diff --git a/include/view.h b/include/view.h index c2764f94..6c6dc795 100644 --- a/include/view.h +++ b/include/view.h @@ -244,6 +244,7 @@ struct view { int width, height; struct wlr_scene_tree *tree; struct wlr_scene_rect *border; + struct bufferset *textured_borders; struct wlr_scene_rect *background; struct scaled_font_buffer *text; } resize_indicator; diff --git a/src/common/borderset.c b/src/common/borderset.c new file mode 100644 index 00000000..59d9827c --- /dev/null +++ b/src/common/borderset.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include "common/borderset.h" +#include "common/mem.h" +#include "common/macros.h" +#include "buffer.h" + +struct borderset *get_borders(uint32_t id, int size, enum border_type type, int bevelSize) +{ + struct borderset *current = border_cache; + struct borderset *last; + + // Preventing building nonsense borders: + // If you try for a double bevel but it's so deep they would overlap, + // convert to a single bevel + if (type == BORDER_DOUBLE && (bevelSize > size/2)) { + type = BORDER_SINGLE; + } + + if (type == BORDER_DOUBLE_INSET && (bevelSize > size/2)) { + type = BORDER_INSET; + } + + // Anything with a size of 0 is converted to a 1-pixel flat border so as to + // prevent empty allocations + + if (size < 1) { + type = BORDER_FLAT; + size = 1; + } + + while (current) { + if (current->size == size && current->id == id && + current->type == type && current->bevelSize == bevelSize) { + return current; + } + last = current; + current = current->next; + } + // Fall through, we need to create a buffer. + + if (!border_cache) { + border_cache = create_buffer(id, size, type, bevelSize); + return border_cache; + } else { + last->next = create_buffer(id, size, type, bevelSize); + return last->next; + } + return NULL; +} + +struct borderset *create_buffer(uint32_t id, int size, enum border_type type, int bevelSize) +{ + struct borderset *new_borderset = znew(*new_borderset); + + new_borderset->next = NULL; + new_borderset->id = id; + new_borderset->size = size; + new_borderset->type = type; + new_borderset->bevelSize = bevelSize; + + // Use ID as a AARRGGBB colour + uint8_t a = id >> 24 & 255; + uint8_t r = id >> 16 & 255; + uint8_t g = id >> 8 & 255; + uint8_t b = id & 255; + + uint32_t r1 = r * 5 / 4; + if (r1 > a) { + r1 = a; + } + uint32_t g1 = g * 5 / 4; + if (g1 > a) { + g1 = a; + } + uint32_t b1 = b * 5 / 4; + if (b1 > a) { + b1 = a; + } + + /* darker outline */ + uint32_t r0 = r / 2; + uint32_t g0 = g / 2; + uint32_t b0 = b / 2; + + uint32_t hl32 = ((uint32_t)a << 24) | ((uint32_t)r1 << 16) + | ((uint32_t)g1 << 8) | (uint32_t)b1; + uint32_t ll32 = ((uint32_t)a << 24) | ((uint32_t)r0 << 16) + | ((uint32_t)g0 << 8) | (uint32_t)b0; + uint32_t temp; + + // All borders have NxN corners + uint32_t *tl = znew_n(uint32_t, size*size); + uint32_t *tr = znew_n(uint32_t, size*size); + uint32_t *bl = znew_n(uint32_t, size*size); + uint32_t *br = znew_n(uint32_t, size*size); + uint32_t *top = NULL; + uint32_t *left = NULL; + uint32_t *right = NULL; + uint32_t *bottom = NULL; + size_t side_size = 0; + + switch (type) { + case BORDER_INSET: + temp = ll32; + ll32 = hl32; + hl32 = temp; + + top = znew(uint32_t); + left = znew(uint32_t); + right = znew(uint32_t); + bottom = znew(uint32_t); + side_size = 1; + *top = hl32; + *left = hl32; + *right = ll32; + *bottom = ll32; + + // Fill with solid + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + tl[PIXEL(j, k, size)] = hl32; + tr[PIXEL(size - 1 - j, k, size)] = (j > k) ? hl32 : ll32; + bl[PIXEL(size - 1 -j, k, size)] = (j > k) ? hl32 : ll32; + br[PIXEL(j, k, size)] = ll32; + } + } + + break; + + case BORDER_SINGLE: // Single bevel borders have 1x1 sides + top = znew(uint32_t); + left = znew(uint32_t); + right = znew(uint32_t); + bottom = znew(uint32_t); + side_size = 1; + *top = hl32; + *left = hl32; + *right = ll32; + *bottom = ll32; + + // Fill with solid + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + tl[PIXEL(j, k, size)] = hl32; + tr[PIXEL(size - 1 - j, k, size)] = (j > k) ? hl32 : ll32; + bl[PIXEL(size - 1 -j, k, size)] = (j > k) ? hl32 : ll32; + br[PIXEL(j, k, size)] = ll32; + } + } + + break; + + case BORDER_DOUBLE_INSET: + temp = ll32; + ll32 = hl32; + hl32 = temp; + top = znew_n(uint32_t, size); + left = znew_n(uint32_t, size); + right = znew_n(uint32_t, size); + bottom = znew_n(uint32_t, size); + side_size = size; + + for (int i = 0; i < size; i++) { + if (i < bevelSize) { + left[i] = hl32; + top[i] = hl32; + right[i] = hl32; + bottom[i] = hl32; + + } else if (i > (size-bevelSize-1)) { + left[i] = ll32; + top[i] = ll32; + right[i] = ll32; + bottom[i] = ll32; + + } else { + left[i] = id; + top[i] = id; + right[i] = id; + bottom[i] = id; + } + } + + // Blank corners... + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + tl[PIXEL(i, j, size)] = id; + tr[PIXEL(i, j, size)] = id; + bl[PIXEL(i, j, size)] = id; + br[PIXEL(i, j, size)] = id; + } + } + + // Main Corners + for (int i = 0; i < bevelSize; i++) { + // Solid bar parts + for (int j = 0; j < size; j++) { + // Top left corner: Entire "bevel size" top rows are highlighted + tl[PIXEL(j, i, size)] = hl32; + // First "bevel size" top columns are highlighted + tl[PIXEL(i, j, size)] = hl32; + + // Bottom Right corner: Entire "bevel size" last rows are lowlight + br[PIXEL(j, (size-1-i), size)] = ll32; + // Last "bevel size" columns are lowlight + br[PIXEL((size-1-i), j, size)] = ll32; + + // Bottom left corner: Entire "bevel size" last rows are lowlight + bl[PIXEL(j, (size-1-i), size)] = ll32; + // First "bevel size" columns are highlight, except for + // the bottom right corner + bl[PIXEL(i, j, size)] = hl32; + + // Top Right corner: Entire "bevel size" first rows are highlight + tr[PIXEL(j, i, size)] = hl32; + // Last "bevel size" columns are lowlight, except for the top left + tr[PIXEL((size-1-i), j, size)] = ll32; + } + } + // Beveled Corner Parts + for (int i = 0; i < bevelSize; i++) { + for (int j = 0; j < bevelSize; j++) { + // Outer Corners + // Bottom left corner: + // First "bevel size" columns are highlight, except + // for the bottom right corner + bl[PIXEL(i, (size - 1 - j), size)] = (j >= i) ? hl32 : ll32; + + // Top Right corner: + // Last "bevel size" columns are lowlight, except for the top left + tr[PIXEL((size-1-i), j, size)] = (j > i) ? ll32 : hl32; + + // Inner Corners + // Top left corner: Bottom right is all dark + tl[PIXEL((size-1-i), (size - 1 - j), size)] = ll32; + + // Bottom Right corner: Top left is all light + br[PIXEL(i, j, size)] = hl32; + + // Top Right corner: + // Interior bottom left is dark on top, light on bottom + tr[PIXEL(i, (size-1-j), size)] = (i > j) ? hl32 : ll32; + + // Bottom Left corner: + // Interior top right is dark on top, light on bottom + bl[PIXEL((size-1-i), j, size)] = (i > j) ? ll32 : hl32; + } + } + + break; + + case BORDER_DOUBLE: + top = znew_n(uint32_t, size); + left = znew_n(uint32_t, size); + right = znew_n(uint32_t, size); + bottom = znew_n(uint32_t, size); + side_size = size; + + for (int i = 0; i < size; i++) { + if (i < bevelSize) { + left[i] = hl32; + top[i] = hl32; + right[i] = hl32; + bottom[i] = hl32; + + } else if (i > (size-bevelSize-1)) { + left[i] = ll32; + top[i] = ll32; + right[i] = ll32; + bottom[i] = ll32; + + } else { + left[i] = id; + top[i] = id; + right[i] = id; + bottom[i] = id; + } + } + + // Blank corners... + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + tl[PIXEL(i, j, size)] = id; + tr[PIXEL(i, j, size)] = id; + bl[PIXEL(i, j, size)] = id; + br[PIXEL(i, j, size)] = id; + } + } + + // Main Corners + for (int i = 0; i < bevelSize; i++) { + // Solid bar parts + for (int j = 0; j < size; j++) { + // Top left corner: Entire "bevel size" top rows are highlighted + tl[PIXEL(j, i, size)] = hl32; + // First "bevel size" top columns are highlighted + tl[PIXEL(i, j, size)] = hl32; + + // Bottom Right corner: Entire "bevel size" last rows are lowlight + br[PIXEL(j, (size-1-i), size)] = ll32; + // Last "bevel size" columns are lowlight + br[PIXEL((size-1-i), j, size)] = ll32; + + // Bottom left corner: Entire "bevel size" last rows are lowlight + bl[PIXEL(j, (size-1-i), size)] = ll32; + // First "bevel size" columns are highlight, except for + // the bottom right corner + bl[PIXEL(i, j, size)] = hl32; + + // Top Right corner: Entire "bevel size" first rows are highlight + tr[PIXEL(j, i, size)] = hl32; + // Last "bevel size" columns are lowlight, except for the top left + tr[PIXEL((size-1-i), j, size)] = ll32; + } + } + // Beveled Corner Parts + for (int i = 0; i < bevelSize; i++) { + for (int j = 0; j < bevelSize; j++) { + // Outer Corners + // Bottom left corner: + // First "bevel size" columns are highlight, except + // for the bottom right corner + bl[PIXEL(i, (size - 1 - j), size)] = (j >= i) ? hl32 : ll32; + + // Top Right corner: + // Last "bevel size" columns are lowlight, except for the top left + tr[PIXEL((size-1-i), j, size)] = (j > i) ? ll32 : hl32; + + // Inner Corners + // Top left corner: Bottom right is all dark + tl[PIXEL((size-1-i), (size - 1 - j), size)] = ll32; + + // Bottom Right corner: Top left is all light + br[PIXEL(i, j, size)] = hl32; + + // Top Right corner: + // Interior bottom left is dark on top, light on bottom + tr[PIXEL(i, (size-1-j), size)] = (i > j) ? hl32 : ll32; + + // Bottom Left corner: + // Interior top right is dark on top, light on bottom + bl[PIXEL((size-1-i), j, size)] = (i > j) ? ll32 : hl32; + } + } + + break; + + case BORDER_FLAT: // Placeholder that uses buffers but for a flat colour + case BORDER_NONE: // Provided as a fallback but should not be actually requested/rendered + top = znew(uint32_t); + left = znew(uint32_t); + right = znew(uint32_t); + bottom = znew(uint32_t); + side_size = 1; + *top = id; + *left = id; + *right = id; + *bottom = id; + + // Fill with solid + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + tl[PIXEL(j, k, size)] = id; + tr[PIXEL(size - 1 - j, k, size)] = id; + bl[PIXEL(size - 1 -j, k, size)] = id; + br[PIXEL(j, k, size)] = id; + } + } + + break; + } + assert(side_size > 0); + + new_borderset->top = buffer_create_from_data(top, 1, side_size, 4); + new_borderset->left = buffer_create_from_data(left, side_size, 1, side_size * 4); + new_borderset->right = buffer_create_from_data(right, side_size, 1, side_size * 4); + new_borderset->bottom = buffer_create_from_data(bottom, 1, side_size, 4); + + new_borderset->tl = buffer_create_from_data(tl, size, size, size * 4); + new_borderset->tr = buffer_create_from_data(tr, size, size, size * 4); + new_borderset->bl = buffer_create_from_data(bl, size, size, size * 4); + new_borderset->br = buffer_create_from_data(br, size, size, size * 4); + + return new_borderset; +} + +struct bufferset *generate_bufferset(struct wlr_scene_tree *tree, + struct borderset *borderset, int bw) +{ + struct bufferset *bufferset = znew(struct bufferset); + + bufferset->top = wlr_scene_buffer_create(tree, &borderset->top->base); + bufferset->left = wlr_scene_buffer_create(tree, &borderset->left->base); + bufferset->right = wlr_scene_buffer_create(tree, &borderset->right->base); + bufferset->bottom = wlr_scene_buffer_create(tree, &borderset->bottom->base); + bufferset->tl = wlr_scene_buffer_create(tree, &borderset->tl->base); + bufferset->tr = wlr_scene_buffer_create(tree, &borderset->tr->base); + bufferset->bl = wlr_scene_buffer_create(tree, &borderset->bl->base); + bufferset->br = wlr_scene_buffer_create(tree, &borderset->br->base); + + bufferset->border_width = bw; + + wlr_scene_buffer_set_filter_mode(bufferset->top, WLR_SCALE_FILTER_NEAREST); + wlr_scene_buffer_set_filter_mode(bufferset->left, WLR_SCALE_FILTER_NEAREST); + wlr_scene_buffer_set_filter_mode(bufferset->right, WLR_SCALE_FILTER_NEAREST); + wlr_scene_buffer_set_filter_mode(bufferset->bottom, WLR_SCALE_FILTER_NEAREST); + wlr_scene_buffer_set_filter_mode(bufferset->tl, WLR_SCALE_FILTER_NEAREST); + wlr_scene_buffer_set_filter_mode(bufferset->tr, WLR_SCALE_FILTER_NEAREST); + wlr_scene_buffer_set_filter_mode(bufferset->bl, WLR_SCALE_FILTER_NEAREST); + wlr_scene_buffer_set_filter_mode(bufferset->br, WLR_SCALE_FILTER_NEAREST); + + return bufferset; +} + +void renderBufferset(struct bufferset *bufferset, int width, int height, int y) +{ + renderBuffersetXY(bufferset, width, height, 0, y); +} + +void renderBuffersetXY(struct bufferset *bufferset, int width, int height, int x, int y) +{ + wlr_scene_buffer_set_dest_size(bufferset->top, + width - 2 * bufferset->border_width, bufferset->border_width); + wlr_scene_node_set_position(&bufferset->top->node, + x+bufferset->border_width, y); + + wlr_scene_buffer_set_dest_size(bufferset->bottom, + width - 2 * bufferset->border_width, bufferset->border_width); + wlr_scene_node_set_position(&bufferset->bottom->node, + x+bufferset->border_width, y+height - bufferset->border_width); + + wlr_scene_buffer_set_dest_size(bufferset->left, + bufferset->border_width, height - bufferset->border_width * 2); + wlr_scene_node_set_position(&bufferset->left->node, + x, bufferset->border_width+y); + + wlr_scene_buffer_set_dest_size(bufferset->right, + bufferset->border_width, height - bufferset->border_width * 2); + wlr_scene_node_set_position(&bufferset->right->node, + x+width - bufferset->border_width, y+bufferset->border_width); + + wlr_scene_buffer_set_dest_size(bufferset->tl, + bufferset->border_width, bufferset->border_width); + wlr_scene_node_set_position(&bufferset->tl->node, + x, y); + + wlr_scene_buffer_set_dest_size(bufferset->tr, + bufferset->border_width, bufferset->border_width); + wlr_scene_node_set_position(&bufferset->tr->node, + x+width-bufferset->border_width, y); + + wlr_scene_buffer_set_dest_size(bufferset->br, + bufferset->border_width, bufferset->border_width); + wlr_scene_node_set_position(&bufferset->br->node, + x+width-bufferset->border_width, y+height-bufferset->border_width); + + wlr_scene_buffer_set_dest_size(bufferset->bl, + bufferset->border_width, bufferset->border_width); + wlr_scene_node_set_position(&bufferset->bl->node, + x, height-bufferset->border_width+y); +} + +void clearborder_cache(struct borderset *borderset) +{ + if (!borderset) { + return; + } + if (borderset->next) { + clearborder_cache(borderset->next); + } + wlr_buffer_drop(&borderset->top->base); + wlr_buffer_drop(&borderset->left->base); + wlr_buffer_drop(&borderset->right->base); + wlr_buffer_drop(&borderset->bottom->base); + wlr_buffer_drop(&borderset->tl->base); + wlr_buffer_drop(&borderset->tr->base); + wlr_buffer_drop(&borderset->bl->base); + wlr_buffer_drop(&borderset->br->base); + free(borderset); +} diff --git a/src/common/lab-scene-rect.c b/src/common/lab-scene-rect.c index 6689d48f..ad5c4cf7 100644 --- a/src/common/lab-scene-rect.c +++ b/src/common/lab-scene-rect.c @@ -4,10 +4,15 @@ #include #include "common/mem.h" #include "common/scene-helpers.h" +#include "common/macros.h" +#include "common/borderset.h" +#include "theme.h" +#include "buffer.h" struct border_scene { struct wlr_scene_tree *tree; struct wlr_scene_rect *top, *bottom, *left, *right; + struct bufferset *textured_borders; }; static void @@ -37,10 +42,29 @@ lab_scene_rect_create(struct wlr_scene_tree *parent, struct border_scene *border = &rect->borders[i]; float *color = opts->border_colors[i]; border->tree = lab_wlr_scene_tree_create(rect->tree); + border->top = lab_wlr_scene_rect_create(border->tree, 0, 0, color); border->right = lab_wlr_scene_rect_create(border->tree, 0, 0, color); border->bottom = lab_wlr_scene_rect_create(border->tree, 0, 0, color); border->left = lab_wlr_scene_rect_create(border->tree, 0, 0, color); + + if (opts->border_type) { + float r = color[0]; + float g = color[1]; + float b = color[2]; + float a = color[3]; + int bw = rect->border_width; + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + opts->border_type, opts->bevel_width); + border->textured_borders = generate_bufferset(border->tree, + renderedborders, bw); + } else { + border->textured_borders = NULL; + } } rect->node_destroy.notify = handle_node_destroy; @@ -81,6 +105,9 @@ resize_border(struct border_scene *border, int border_width, int width, int heig wlr_scene_rect_set_size(border->bottom, width, border_width); wlr_scene_rect_set_size(border->left, border_width, height - border_width * 2); wlr_scene_rect_set_size(border->right, border_width, height - border_width * 2); + if (border->textured_borders) { + renderBufferset(border->textured_borders, width, height, 0); + } } void diff --git a/src/common/meson.build b/src/common/meson.build index 4cf52023..d9b24b23 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,4 +1,5 @@ labwc_sources += files( + 'borderset.c', 'box.c', 'buf.c', 'dir.c', diff --git a/src/cycle/cycle.c b/src/cycle/cycle.c index 77195176..0c3643f9 100644 --- a/src/cycle/cycle.c +++ b/src/cycle/cycle.c @@ -37,6 +37,8 @@ update_preview_outlines(struct view *view) }, .nr_borders = 3, .border_width = theme->osd_window_switcher_preview_border_width, + .border_type = theme->osd_border_type, + .bevel_width = theme->osd_border_bevel_width }; rect = lab_scene_rect_create(&server.scene->tree, &opts); wlr_scene_node_place_above(&rect->tree->node, diff --git a/src/cycle/osd-classic.c b/src/cycle/osd-classic.c index 7e5abfb6..28590de7 100644 --- a/src/cycle/osd-classic.c +++ b/src/cycle/osd-classic.c @@ -120,6 +120,8 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output) .bg_color = bg_color, .width = w, .height = h, + .border_type = theme->osd_border_type, + .bevel_width = theme->osd_border_bevel_width }; lab_scene_rect_create(osd_output->tree, &bg_opts); @@ -202,6 +204,8 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output) .bg_color = active_bg_color, .width = w - 2 * padding, .height = switcher_theme->item_height, + .border_type = theme->osd_border_type, + .bevel_width = theme->osd_border_bevel_width }; struct lab_scene_rect *highlight_rect = lab_scene_rect_create( item->active_tree, &highlight_opts); diff --git a/src/cycle/osd-scroll.c b/src/cycle/osd-scroll.c index e788a479..128b5031 100644 --- a/src/cycle/osd-scroll.c +++ b/src/cycle/osd-scroll.c @@ -3,7 +3,9 @@ #include #include "common/lab-scene-rect.h" #include "common/scene-helpers.h" +#include "config/rcxml.h" #include "labwc.h" +#include "theme.h" #include "cycle.h" #include "output.h" @@ -12,6 +14,8 @@ cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_ar int delta_y, int nr_cols, int nr_rows, int nr_visible_rows, float *border_color, float *bg_color) { + struct theme *theme = rc.theme; + if (nr_visible_rows >= nr_rows) { /* OSD doesn't have so many windows to scroll through */ return; @@ -35,6 +39,8 @@ cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_ar .bg_color = bg_color, .width = bar_area.width, .height = bar_area.height * nr_visible_rows / nr_rows, + .border_type = theme->osd_border_type, + .bevel_width = theme->osd_border_bevel_width }; scroll->bar = lab_scene_rect_create(scroll->bar_tree, &scrollbar_opts); } diff --git a/src/cycle/osd-thumbnail.c b/src/cycle/osd-thumbnail.c index 623708a0..dbfe5e61 100644 --- a/src/cycle/osd-thumbnail.c +++ b/src/cycle/osd-thumbnail.c @@ -154,6 +154,8 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view, .bg_color = switcher_theme->item_active_bg_color, .width = switcher_theme->item_width, .height = switcher_theme->item_height, + .border_type = theme->osd_border_type, + .bevel_width = theme->osd_border_bevel_width }; item->active_bg = lab_scene_rect_create(tree, &opts); @@ -286,6 +288,8 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output) .bg_color = theme->osd_bg_color, .width = items_width + 2 * padding, .height = items_height + 2 * padding, + .border_type = theme->osd_border_type, + .bevel_width = theme->osd_border_bevel_width }; struct lab_scene_rect *bg = lab_scene_rect_create(osd_output->tree, &bg_opts); diff --git a/src/main.c b/src/main.c index 373f4480..0534b542 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,7 @@ #include "theme.h" #include "translate.h" #include "menu/menu.h" +#include "common/borderset.h" /* * Globals @@ -40,6 +41,8 @@ static const struct option long_options[] = { {0, 0, 0, 0} }; +struct borderset *border_cache; + static const char labwc_usage[] = "Usage: labwc [options...]\n" " -c, --config Specify config file (with path)\n" diff --git a/src/menu/menu.c b/src/menu/menu.c index 251f9a30..61d9caf7 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -32,6 +32,8 @@ #include "translate.h" #include "view.h" #include "workspaces.h" +#include "common/borderset.h" +#include "buffer.h" #define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */ #define PIPEMENU_TIMEOUT_IN_MS 4000 /* 4 seconds */ @@ -163,10 +165,11 @@ item_create(struct menu *menu, const char *text, const char *icon_name, bool sho static struct wlr_scene_tree * item_create_scene_for_state(struct menuitem *item, float *text_color, - float *bg_color) + float *bg_color, int state) { struct menu *menu = item->parent; struct theme *theme = rc.theme; + struct bufferset *bufferset = NULL; /* Tree to hold background and label buffers */ struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(item->tree); @@ -191,6 +194,37 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, /* Create background */ lab_wlr_scene_rect_create(tree, bg_width, theme->menu_item_height, bg_color); + int bw = theme->menu_border_width; + if (rc.theme->menu_items_active_border_type && state) { + float r = bg_color[0]; + float g = bg_color[1]; + float b = bg_color[2]; + float a = bg_color[3]; + + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + rc.theme->menu_items_active_border_type, + rc.theme->menu_items_active_bevel_width); + bufferset = generate_bufferset(tree, renderedborders, bw); + } else if (rc.theme->menu_items_border_type && !state) { + float r = bg_color[0]; + float g = bg_color[1]; + float b = bg_color[2]; + float a = bg_color[3]; + + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + rc.theme->menu_items_border_type, + rc.theme->menu_items_bevel_width); + bufferset = generate_bufferset(tree, renderedborders, bw); + } + /* Create icon */ bool show_app_icon = !strcmp(item->parent->id, "client-list-combined-menu") && item->client_list_view; @@ -219,6 +253,10 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, int y = (theme->menu_item_height - label_buffer->height) / 2; wlr_scene_node_set_position(&label_buffer->scene_buffer->node, x, y); + if (bufferset) { + renderBufferset(bufferset, bg_width, theme->menu_item_height, 0); + } + if (!item->arrow) { return tree; } @@ -252,10 +290,10 @@ item_create_scene(struct menuitem *menuitem, int *item_y) /* Create scenes for unselected/selected states */ menuitem->normal_tree = item_create_scene_for_state(menuitem, theme->menu_items_text_color, - theme->menu_items_bg_color); + theme->menu_items_bg_color, 0); menuitem->selected_tree = item_create_scene_for_state(menuitem, theme->menu_items_active_text_color, - theme->menu_items_active_bg_color); + theme->menu_items_active_bg_color, 1); /* Hide selected state */ wlr_scene_node_set_enabled(&menuitem->selected_tree->node, false); @@ -338,6 +376,8 @@ title_create_scene(struct menuitem *menuitem, int *item_y) assert(menuitem->type == LAB_MENU_TITLE); struct menu *menu = menuitem->parent; struct theme *theme = rc.theme; + struct bufferset *bufferset = NULL; + float *bg_color = theme->menu_title_bg_color; float *text_color = theme->menu_title_text_color; @@ -357,6 +397,23 @@ title_create_scene(struct menuitem *menuitem, int *item_y) goto error; } + int bw = theme->menu_border_width; + if (rc.theme->menu_title_border_type) { + float r = bg_color[0]; + float g = bg_color[1]; + float b = bg_color[2]; + float a = bg_color[3]; + + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + rc.theme->menu_title_border_type, + rc.theme->menu_title_bevel_width); + bufferset = generate_bufferset(menuitem->tree, renderedborders, bw); + } + /* Background */ lab_wlr_scene_rect_create(menuitem->normal_tree, bg_width, theme->menu_header_height, bg_color); @@ -385,6 +442,10 @@ title_create_scene(struct menuitem *menuitem, int *item_y) int title_y = (theme->menu_header_height - title_font_buffer->height) / 2; wlr_scene_node_set_position(&title_font_buffer->scene_buffer->node, title_x, title_y); + + if (bufferset && rc.theme->menu_title_border_type) { + renderBufferset(bufferset, bg_width, theme->menu_item_height, 0); + } error: wlr_scene_node_set_position(&menuitem->tree->node, theme->menu_border_width, *item_y); @@ -457,6 +518,8 @@ menu_create_scene(struct menu *menu) .border_width = theme->menu_border_width, .width = menu->size.width, .height = menu->size.height, + .border_type = theme->menu_border_type, + .bevel_width = theme->menu_bevel_width }; struct lab_scene_rect *bg_rect = lab_scene_rect_create(menu->scene_tree, &opts); diff --git a/src/server.c b/src/server.c index fd48efed..a25ed300 100644 --- a/src/server.c +++ b/src/server.c @@ -73,6 +73,7 @@ #include "view.h" #include "workspaces.h" #include "xwayland.h" +#include "common/borderset.h" #define LAB_EXT_DATA_CONTROL_VERSION 1 #define LAB_EXT_FOREIGN_TOPLEVEL_LIST_VERSION 1 @@ -89,6 +90,9 @@ reload_config_and_theme(void) scaled_buffer_invalidate_sharing(); rcxml_finish(); + clearborder_cache(border_cache); + border_cache = NULL; + rcxml_read(rc.config_file); theme_finish(rc.theme); theme_init(rc.theme, rc.theme_name); diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index fa1166a3..8f17e3e8 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -12,6 +12,8 @@ #include "ssd.h" #include "theme.h" #include "view.h" +#include "common/borderset.h" +#include "buffer.h" #define PADDING rc.theme->osd_window_switcher_classic.padding @@ -36,6 +38,23 @@ resize_indicator_reconfigure_view(struct resize_indicator *indicator) /* Colors */ wlr_scene_rect_set_color(indicator->border, theme->osd_border_color); wlr_scene_rect_set_color(indicator->background, theme->osd_bg_color); + + if (rc.theme->osd_border_type) { + float r = theme->osd_border_color[0]; + float g = theme->osd_border_color[1]; + float b = theme->osd_border_color[2]; + float a = theme->osd_border_color[3]; + int bw = theme->osd_border_width; + + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + theme->osd_border_type, theme->osd_border_bevel_width); + indicator->textured_borders = generate_bufferset(indicator->tree, + renderedborders, bw); + } } static void @@ -50,6 +69,7 @@ resize_indicator_init(struct view *view) indicator->tree, 0, 0, rc.theme->osd_border_color); indicator->background = lab_wlr_scene_rect_create( indicator->tree, 0, 0, rc.theme->osd_bg_color); + indicator->text = scaled_font_buffer_create(indicator->tree); wlr_scene_node_set_enabled(&indicator->tree->node, false); @@ -80,7 +100,9 @@ resize_indicator_reconfigure(void) wl_list_for_each(view, &server.views, link) { struct resize_indicator *indicator = &view->resize_indicator; if (indicator->tree) { - resize_indicator_reconfigure_view(indicator); + // Destroy the old tree so it doesn't have the leftover styling/sizing. + wlr_scene_node_destroy(&indicator->tree->node); + indicator->tree = NULL; } if (view != server.grabbed_view) { continue; @@ -117,6 +139,11 @@ resize_indicator_set_size(struct resize_indicator *indicator, int width) wlr_scene_rect_set_size(indicator->background, indicator->width - 2 * rc.theme->osd_border_width, indicator->height - 2 * rc.theme->osd_border_width); + + if (rc.theme->osd_border_type) { + renderBufferset(indicator->textured_borders, indicator->width, + indicator->height, 0); + } } void diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index c2c99f8d..8885b0d1 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -2,13 +2,16 @@ #include #include +#include "buffer.h" #include "common/macros.h" +#include "common/mem.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "ssd.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" +#include "common/borderset.h" void ssd_border_create(struct ssd *ssd) @@ -18,6 +21,7 @@ ssd_border_create(struct ssd *ssd) struct view *view = ssd->view; struct theme *theme = rc.theme; + int bw = theme->border_width; int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); int full_width = width + 2 * theme->border_width; @@ -33,26 +37,48 @@ ssd_border_create(struct ssd *ssd) struct wlr_scene_tree *parent = subtree->tree; wlr_scene_node_set_enabled(&parent->node, active); float *color = theme->window[active].border_color; + if (theme->window[active].border_type) { + // These will otherwise get left under the window when we reload - subtree->left = lab_wlr_scene_rect_create(parent, - theme->border_width, height, color); - wlr_scene_node_set_position(&subtree->left->node, 0, 0); + subtree->left = lab_wlr_scene_rect_create(parent, 1, 1, color); + subtree->right = lab_wlr_scene_rect_create(parent, 1, 1, color); + subtree->bottom = lab_wlr_scene_rect_create(parent, 1, 1, color); + subtree->top = lab_wlr_scene_rect_create(parent, 1, 1, color); - subtree->right = lab_wlr_scene_rect_create(parent, - theme->border_width, height, color); - wlr_scene_node_set_position(&subtree->right->node, - theme->border_width + width, 0); + /* From Pull request 3382 */ + float r = color[0]; + float g = color[1]; + float b = color[2]; + float a = color[3]; - subtree->bottom = lab_wlr_scene_rect_create(parent, - full_width, theme->border_width, color); - wlr_scene_node_set_position(&subtree->bottom->node, - 0, height); + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + theme->window[active].border_type, + theme->window[active].bevel_width); + subtree->textured_borders = generate_bufferset(subtree->tree, + renderedborders, bw); + } else { + subtree->left = lab_wlr_scene_rect_create(parent, + theme->border_width, height, color); + subtree->right = lab_wlr_scene_rect_create(parent, + theme->border_width, height, color); + subtree->bottom = lab_wlr_scene_rect_create(parent, + full_width, theme->border_width, color); + subtree->top = lab_wlr_scene_rect_create(parent, + MAX(width - 2 * corner_width, 0), theme->border_width, color); - subtree->top = lab_wlr_scene_rect_create(parent, - MAX(width - 2 * corner_width, 0), theme->border_width, color); - wlr_scene_node_set_position(&subtree->top->node, - theme->border_width + corner_width, - -(ssd->titlebar.height + theme->border_width)); + wlr_scene_node_set_position(&subtree->left->node, 0, 0); + wlr_scene_node_set_position(&subtree->right->node, + theme->border_width + width, 0); + wlr_scene_node_set_position(&subtree->bottom->node, + 0, height); + wlr_scene_node_set_position(&subtree->top->node, + theme->border_width + corner_width, + -(ssd->titlebar.height + theme->border_width)); + } } if (view->maximized == VIEW_AXIS_BOTH) { @@ -132,26 +158,28 @@ ssd_border_update(struct ssd *ssd) enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { struct ssd_border_subtree *subtree = &ssd->border.subtrees[active]; - - wlr_scene_rect_set_size(subtree->left, - theme->border_width, side_height); - wlr_scene_node_set_position(&subtree->left->node, - 0, side_y); - - wlr_scene_rect_set_size(subtree->right, - theme->border_width, side_height); - wlr_scene_node_set_position(&subtree->right->node, - theme->border_width + width, side_y); - - wlr_scene_rect_set_size(subtree->bottom, - full_width, theme->border_width); - wlr_scene_node_set_position(&subtree->bottom->node, - 0, height); - - wlr_scene_rect_set_size(subtree->top, - top_width, theme->border_width); - wlr_scene_node_set_position(&subtree->top->node, - top_x, -(ssd->titlebar.height + theme->border_width)); + if (theme->window[active].border_type) { + renderBufferset(subtree->textured_borders, full_width, + side_height+(ssd->titlebar.height + 2*theme->border_width), + -ssd->titlebar.height-theme->border_width); + } else { + wlr_scene_rect_set_size(subtree->left, + theme->border_width, side_height); + wlr_scene_node_set_position(&subtree->left->node, + 0, side_y); + wlr_scene_rect_set_size(subtree->right, + theme->border_width, side_height); + wlr_scene_node_set_position(&subtree->right->node, + theme->border_width + width, side_y); + wlr_scene_rect_set_size(subtree->bottom, + full_width, theme->border_width); + wlr_scene_node_set_position(&subtree->bottom->node, + 0, height); + wlr_scene_rect_set_size(subtree->top, + top_width, theme->border_width); + wlr_scene_node_set_position(&subtree->top->node, + top_x, -(ssd->titlebar.height + theme->border_width)); + } } } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index d1a08810..9b2aa9dc 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -18,6 +18,7 @@ #include "ssd-internal.h" #include "theme.h" #include "view.h" +#include "common/borderset.h" static void set_squared_corners(struct ssd *ssd, bool enable); static void set_alt_button_icon(struct ssd *ssd, enum lab_node_type type, bool enable); @@ -36,6 +37,7 @@ ssd_titlebar_create(struct ssd *ssd) LAB_NODE_TITLEBAR, view, /*data*/ NULL); enum ssd_active_state active; + bool should_force_update = FALSE; FOR_EACH_ACTIVE_STATE(active) { struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; subtree->tree = lab_wlr_scene_tree_create(ssd->titlebar.tree); @@ -52,6 +54,30 @@ ssd_titlebar_create(struct ssd *ssd) /* Background */ subtree->bar = lab_wlr_scene_buffer_create(parent, titlebar_fill); + if (theme->window[active].title_bg.border_type) { + /* Beveled Titlebar */ + float *color = theme->window[active].title_bg.color; + float r = color[0]; + float g = color[1]; + float b = color[2]; + float a = color[3]; + + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, + theme->window[active].title_bg.border_width, + theme->window[active].title_bg.border_type, + theme->window[active].title_bg.bevel_width); + subtree->textured_borders = generate_bufferset(subtree->tree, + renderedborders, theme->window[active].title_bg.border_width); + + // If we have the beveled borders, we actually have to run + // ssd_titlebar_update() to make sure we render the updated borders. + // Otherwise they disappear on reconfigure + should_force_update = true; + } /* * Work around the wlroots/pixman bug that widened 1px buffer * becomes translucent when bilinear filtering is used. @@ -73,9 +99,17 @@ ssd_titlebar_create(struct ssd *ssd) width - corner_width, -rc.theme->border_width); /* Title */ - subtree->title = scaled_font_buffer_create_for_titlebar( - subtree->tree, theme->titlebar_height, - theme->window[active].titlebar_pattern); + if (theme->window[active].title_bg.border_type) { + // Use a blank background pattern so it doesn't overlay + // a pattern on the order. + subtree->title = scaled_font_buffer_create_for_titlebar( + subtree->tree, theme->titlebar_height, + NULL); + } else { + subtree->title = scaled_font_buffer_create_for_titlebar( + subtree->tree, theme->titlebar_height, + theme->window[active].titlebar_pattern); + } assert(subtree->title); node_descriptor_create(&subtree->title->scene_buffer->node, LAB_NODE_TITLE, view, /*data*/ NULL); @@ -122,7 +156,10 @@ ssd_titlebar_create(struct ssd *ssd) if (squared) { ssd->state.was_squared = true; } - set_squared_corners(ssd, maximized || squared); + set_squared_corners(ssd, maximized || + squared || + theme->window[SSD_ACTIVE].title_bg.border_type || + theme->window[SSD_INACTIVE].title_bg.border_type); if (view->shaded) { set_alt_button_icon(ssd, LAB_NODE_BUTTON_SHADE, true); @@ -131,6 +168,10 @@ ssd_titlebar_create(struct ssd *ssd) if (view->visible_on_all_workspaces) { set_alt_button_icon(ssd, LAB_NODE_BUTTON_OMNIPRESENT, true); } + + if (should_force_update) { + ssd_titlebar_update(ssd); + } } static void @@ -280,7 +321,10 @@ ssd_titlebar_update(struct ssd *ssd) if (ssd->state.was_maximized != maximized || ssd->state.was_squared != squared) { - set_squared_corners(ssd, maximized || squared); + set_squared_corners(ssd, maximized || + squared || + theme->window[SSD_ACTIVE].title_bg.border_type || + theme->window[SSD_INACTIVE].title_bg.border_type); if (ssd->state.was_maximized != maximized) { set_alt_button_icon(ssd, LAB_NODE_BUTTON_MAXIMIZE, maximized); } @@ -308,7 +352,11 @@ ssd_titlebar_update(struct ssd *ssd) /* Center buttons vertically within titlebar */ int y = (theme->titlebar_height - theme->window_button_height) / 2; int x; - int bg_offset = maximized || squared ? 0 : corner_width; + int bg_offset = (maximized || + squared || + theme->window[SSD_INACTIVE].title_bg.border_type || + theme->window[SSD_ACTIVE].title_bg.border_type) + ? 0 : corner_width; enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { @@ -318,10 +366,14 @@ ssd_titlebar_update(struct ssd *ssd) x = theme->window_titlebar_padding_width; struct ssd_button *button; + int button_count = 0; + wl_list_for_each(button, &subtree->buttons_left, link) { wlr_scene_node_set_position(button->node, x, y); x += theme->window_button_width + theme->window_button_spacing; + button_count++; } + int exclusive_x = x; x = width - corner_width; wlr_scene_node_set_position(&subtree->corner_right->node, @@ -331,6 +383,25 @@ ssd_titlebar_update(struct ssd *ssd) wl_list_for_each(button, &subtree->buttons_right, link) { x -= theme->window_button_width + theme->window_button_spacing; wlr_scene_node_set_position(button->node, x, y); + button_count++; + } + + if (theme->window[active].title_bg.border_type) { + int titlebar_x = 0; + int titlebar_width = MAX(view->current.width, 0); + if (theme->window[active].title_bg.exclusive) { + titlebar_x = exclusive_x+theme->window_titlebar_padding_width; + titlebar_width = MAX( + (theme->window_button_width + theme->window_button_spacing) + * button_count, + titlebar_width - + (theme->window_button_width + theme->window_button_spacing) + * button_count + ); + } + + renderBuffersetXY(subtree->textured_borders, titlebar_width, + theme->titlebar_height, titlebar_x, 0); } } diff --git a/src/theme.c b/src/theme.c index 93ac1c5e..3fa9600a 100644 --- a/src/theme.c +++ b/src/theme.c @@ -91,6 +91,89 @@ draw_hover_overlay_on_button(cairo_t *cairo, int w, int h) cairo_fill(cairo); } +static void draw_beveled_border_on_button(cairo_t *cairo, int w, int h, int active, int hover) +{ + if (rc.theme->window[active].button_border_type) { + int bw = rc.theme->window[active].button_border_width; + float r, g, b, a; + if (!hover) { + r = rc.theme->window[active].button_border_color[0]; + g = rc.theme->window[active].button_border_color[1]; + b = rc.theme->window[active].button_border_color[2]; + a = rc.theme->window[active].button_border_color[3]; + } else { + r = rc.theme->window[active].button_hover_border_color[0]; + g = rc.theme->window[active].button_hover_border_color[1]; + b = rc.theme->window[active].button_hover_border_color[2]; + a = rc.theme->window[active].button_hover_border_color[3]; + } + + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + rc.theme->window[active].button_border_type, + rc.theme->window[active].button_bevel_width); + + cairo_set_source_surface(cairo, renderedborders->top->surface, 0, 0); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, bw, 0, w-bw*2, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->bottom->surface, bw, h-bw); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, bw, h-bw, w-bw*2, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->left->surface, 0, 0); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, 0, bw, bw, h-bw*2); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->right->surface, w-bw, bw); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, w-bw, bw, bw, h-bw*2); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->tl->surface, 0, 0); + cairo_rectangle(cairo, 0, 0, bw, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->tr->surface, w-bw, 0); + cairo_rectangle(cairo, w - bw, 0, bw, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->bl->surface, 0, h - bw); + cairo_rectangle(cairo, 0, h - bw, bw, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->br->surface, w - bw, h -bw); + cairo_rectangle(cairo, w - bw, h - bw, bw, bw); + cairo_fill(cairo); + } +} + +static void draw_hover_active_border_on_button(cairo_t *cairo, int w, int h) +{ + draw_beveled_border_on_button(cairo, w, h, SSD_ACTIVE, 1); +} + +static void draw_hover_inactive_border_on_button(cairo_t *cairo, int w, int h) +{ + draw_beveled_border_on_button(cairo, w, h, SSD_INACTIVE, 1); +} + +static void draw_nonhover_active_border_on_button(cairo_t *cairo, int w, int h) +{ + draw_beveled_border_on_button(cairo, w, h, SSD_ACTIVE, 0); +} + +static void draw_nonhover_inactive_border_on_button(cairo_t *cairo, int w, int h) +{ + draw_beveled_border_on_button(cairo, w, h, SSD_INACTIVE, 0); +} + /* Round the buffer for the leftmost button in the titlebar */ static void round_left_corner_button(cairo_t *cairo, int w, int h) @@ -222,6 +305,21 @@ load_button(struct theme *theme, struct button *b, enum ssd_active_state active) draw_hover_overlay_on_button); } + if (theme->window[active].button_border_type) { + if (active) { + if (b->state_set & LAB_BS_HOVERED) { + lab_img_add_modifier(*img, draw_hover_active_border_on_button); + } else { + lab_img_add_modifier(*img, draw_nonhover_active_border_on_button); + } + } else { + if (b->state_set & LAB_BS_HOVERED) { + lab_img_add_modifier(*img, draw_hover_inactive_border_on_button); + } else { + lab_img_add_modifier(*img, draw_nonhover_inactive_border_on_button); + } + } + } /* * If the loaded button is at the corner of the titlebar, also create * rounded variants. @@ -450,6 +548,28 @@ parse_hexstrs(const char *hexes, float colors[3][4]) g_strfreev(elements); } +static enum border_type parse_border_type(const char *str) +{ + char *lower = g_ascii_strdown(str, -1); + enum border_type border_type; + if (strstr(lower, "doublesunken")) { + border_type = BORDER_DOUBLE_INSET; + } else if (strstr(lower, "sunken")) { + border_type = BORDER_INSET; + } else if (strstr(lower, "doubleraised")) { + border_type = BORDER_DOUBLE; + } else if (strstr(lower, "raised")) { + border_type = BORDER_SINGLE; + } else if (strstr(lower, "flat")) { + border_type = BORDER_FLAT; + } else { + border_type = BORDER_NONE; + } + + g_free(lower); + return border_type; +} + static void parse_color(const char *str, float *rgba) { @@ -550,6 +670,28 @@ theme_builtin(struct theme *theme) theme->window[SSD_INACTIVE].title_bg.color_to[0] = FLT_MIN; theme->window[SSD_ACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; theme->window[SSD_INACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; + theme->window[SSD_ACTIVE].bevel_width = 0; + theme->window[SSD_ACTIVE].border_type = BORDER_NONE; + theme->window[SSD_ACTIVE].title_bg.bevel_width = 0; + theme->window[SSD_ACTIVE].title_bg.border_width = 0; + theme->window[SSD_ACTIVE].title_bg.exclusive = FALSE; + theme->window[SSD_ACTIVE].title_bg.border_type = BORDER_NONE; + theme->window[SSD_INACTIVE].bevel_width = 0; + theme->window[SSD_INACTIVE].border_type = BORDER_NONE; + theme->window[SSD_INACTIVE].title_bg.bevel_width = 0; + theme->window[SSD_INACTIVE].title_bg.border_width = 0; + theme->window[SSD_INACTIVE].title_bg.exclusive = FALSE; + theme->window[SSD_INACTIVE].title_bg.border_type = BORDER_NONE; + theme->window[SSD_ACTIVE].button_border_type = BORDER_NONE; + theme->window[SSD_ACTIVE].button_border_width = 0; + theme->window[SSD_ACTIVE].button_bevel_width = 0; + theme->window[SSD_ACTIVE].button_border_color[0] = FLT_MIN; + theme->window[SSD_ACTIVE].button_hover_border_color[0] = FLT_MIN; + theme->window[SSD_INACTIVE].button_border_type = BORDER_NONE; + theme->window[SSD_INACTIVE].button_border_width = 0; + theme->window[SSD_INACTIVE].button_bevel_width = 0; + theme->window[SSD_INACTIVE].button_border_color[0] = FLT_MIN; + theme->window[SSD_INACTIVE].button_hover_border_color[0] = FLT_MIN; parse_hexstr("#000000", theme->window[SSD_ACTIVE].label_text_color); parse_hexstr("#000000", theme->window[SSD_INACTIVE].label_text_color); @@ -581,6 +723,8 @@ theme_builtin(struct theme *theme) theme->menu_max_width = 200; theme->menu_border_width = INT_MIN; theme->menu_border_color[0] = FLT_MIN; + theme->menu_border_type = BORDER_NONE; + theme->menu_bevel_width = 0; theme->menu_items_padding_x = 7; theme->menu_items_padding_y = 4; @@ -588,6 +732,12 @@ theme_builtin(struct theme *theme) parse_hexstr("#000000", theme->menu_items_text_color); parse_hexstr("#e1dedb", theme->menu_items_active_bg_color); parse_hexstr("#000000", theme->menu_items_active_text_color); + theme->menu_items_border_type = BORDER_NONE; + theme->menu_items_bevel_width = 0; + theme->menu_title_border_type = BORDER_NONE; + theme->menu_title_bevel_width = 0; + theme->menu_items_active_border_type = BORDER_NONE; + theme->menu_items_active_bevel_width = 0; theme->menu_separator_line_thickness = 1; theme->menu_separator_padding_width = 6; @@ -633,6 +783,8 @@ theme_builtin(struct theme *theme) theme->osd_border_width = INT_MIN; theme->osd_border_color[0] = FLT_MIN; theme->osd_label_text_color[0] = FLT_MIN; + theme->osd_border_type = BORDER_NONE; + theme->osd_border_bevel_width = 0; if (wlr_renderer_is_pixman(server.renderer)) { /* Draw only outlined overlay by default to save CPU resource */ @@ -695,6 +847,7 @@ entry(struct theme *theme, const char *key, const char *value) theme->border_width = get_int_if_positive( value, "border.width"); } + if (match_glob(key, "window.titlebar.padding.width")) { theme->window_titlebar_padding_width = get_int_if_positive( value, "window.titlebar.padding.width"); @@ -710,12 +863,45 @@ entry(struct theme *theme, const char *key, const char *value) wlr_log(WLR_INFO, "padding.height is no longer supported"); } + if (match_glob(key, "window.active.title.bg") && parse_border_type(value)) { + theme->window[SSD_ACTIVE].title_bg.border_type = parse_border_type(value); + } + if (match_glob(key, "window.active.title.bg.width")) { + theme->window[SSD_ACTIVE].title_bg.border_width = + get_int_if_positive(value, "window.active.title.bg.width"); + } + + if (match_glob(key, "window.active.title.bg.exclusive")) { + set_bool(value, &theme->window[SSD_ACTIVE].title_bg.exclusive); + } + if (match_glob(key, "window.active.title.bg.bevel-width")) { + theme->window[SSD_ACTIVE].title_bg.bevel_width = + get_int_if_positive(value, "window.active.title.bg.bevel-width"); + } if (match_glob(key, "window.active.border.color")) { parse_color(value, theme->window[SSD_ACTIVE].border_color); } + + if (match_glob(key, "window.active.border.type")) { + theme->window[SSD_ACTIVE].border_type = parse_border_type(value); + } + if (match_glob(key, "window.active.border.bevel-width")) { + theme->window[SSD_ACTIVE].bevel_width = + get_int_if_positive(value, "window.active.border.bevel-width"); + } + if (match_glob(key, "window.inactive.border.color")) { parse_color(value, theme->window[SSD_INACTIVE].border_color); } + + if (match_glob(key, "window.inactive.border.bevel-width")) { + theme->window[SSD_INACTIVE].bevel_width = + get_int_if_positive(value, "window.inactive.border.bevel-width"); + } + + if (match_glob(key, "window.inactive.border.type")) { + theme->window[SSD_INACTIVE].border_type = parse_border_type(value); + } /* border.color is obsolete, but handled for backward compatibility */ if (match_glob(key, "border.color")) { parse_color(value, theme->window[SSD_ACTIVE].border_color); @@ -729,6 +915,22 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "window.active.title.bg")) { theme->window[SSD_ACTIVE].title_bg.gradient = parse_gradient(value); } + + if (match_glob(key, "window.inactive.title.bg") && parse_border_type(value)) { + theme->window[SSD_INACTIVE].title_bg.border_type = parse_border_type(value); + } + if (match_glob(key, "window.inactive.title.bg.width")) { + theme->window[SSD_INACTIVE].title_bg.border_width = + get_int_if_positive(value, "window.inactive.title.bg.width"); + } + if (match_glob(key, "window.inactive.title.bg.bevel-width")) { + theme->window[SSD_INACTIVE].title_bg.bevel_width = + get_int_if_positive(value, "window.inactive.title.bg.bevel-width"); + } + if (match_glob(key, "window.inactive.title.bg.exclusive")) { + set_bool(value, &theme->window[SSD_INACTIVE].title_bg.exclusive); + } + if (match_glob(key, "window.inactive.title.bg")) { theme->window[SSD_INACTIVE].title_bg.gradient = parse_gradient(value); } @@ -788,6 +990,46 @@ entry(struct theme *theme, const char *key, const char *value) value, "window.button.spacing"); } + if (match_glob(key, "window.inactive.button.bg") && parse_border_type(value)) { + theme->window[SSD_INACTIVE].button_border_type = parse_border_type(value); + } + if (match_glob(key, "window.inactive.button.bg.width")) { + theme->window[SSD_INACTIVE].button_border_width = + get_int_if_positive(value, "window.inactive.button.bg.width"); + } + if (match_glob(key, "window.inactive.button.bg.bevel-width")) { + theme->window[SSD_INACTIVE].button_bevel_width = + get_int_if_positive(value, "window.inactive.button.bg.bevel-width"); + } + + if (match_glob(key, "window.inactive.button.bg.border-color")) { + parse_color(value, theme->window[SSD_INACTIVE].button_border_color); + } + + if (match_glob(key, "window.inactive.button.bg.border-hover-color")) { + parse_color(value, theme->window[SSD_INACTIVE].button_hover_border_color); + } + + if (match_glob(key, "window.active.button.bg") && parse_border_type(value)) { + theme->window[SSD_ACTIVE].button_border_type = parse_border_type(value); + } + if (match_glob(key, "window.active.button.bg.width")) { + theme->window[SSD_ACTIVE].button_border_width = + get_int_if_positive(value, "window.active.button.bg.width"); + } + if (match_glob(key, "window.inactive.button.bg.bevel-width")) { + theme->window[SSD_ACTIVE].button_bevel_width = + get_int_if_positive(value, "window.active.button.bg.bevel-width"); + } + + if (match_glob(key, "window.active.button.bg.border-color")) { + parse_color(value, theme->window[SSD_ACTIVE].button_border_color); + } + + if (match_glob(key, "window.active.button.bg.border-hover-color")) { + parse_color(value, theme->window[SSD_ACTIVE].button_hover_border_color); + } + /* botton hover overlay */ if (match_glob(key, "window.button.hover.bg.color")) { parse_color(value, theme->window_button_hover_bg_color); @@ -905,6 +1147,14 @@ entry(struct theme *theme, const char *key, const char *value) parse_color(value, theme->menu_border_color); } + if (match_glob(key, "menu.bg")) { + theme->menu_border_type = parse_border_type(value); + } + if (match_glob(key, "menu.bg.bevel-width")) { + theme->menu_bevel_width = + get_int_if_positive(value, "menu.bg.bevel-width"); + } + if (match_glob(key, "menu.items.padding.x")) { theme->menu_items_padding_x = get_int_if_positive( value, "menu.items.padding.x"); @@ -916,6 +1166,14 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "menu.items.bg.color")) { parse_color(value, theme->menu_items_bg_color); } + if (match_glob(key, "menu.items.bg")) { + theme->menu_items_border_type = parse_border_type(value); + } + if (match_glob(key, "menu.items.bg.bevel-width")) { + theme->menu_items_bevel_width = + get_int_if_positive(value, "menu.items.bg.bevel-width"); + } + if (match_glob(key, "menu.items.text.color")) { parse_color(value, theme->menu_items_text_color); } @@ -926,6 +1184,14 @@ entry(struct theme *theme, const char *key, const char *value) parse_color(value, theme->menu_items_active_text_color); } + if (match_glob(key, "menu.items.active.bg")) { + theme->menu_items_active_border_type = parse_border_type(value); + } + if (match_glob(key, "menu.items.active.bg.bevel-width")) { + theme->menu_items_active_bevel_width = + get_int_if_positive(value, "menu.items.active.bg.bevel-width"); + } + if (match_glob(key, "menu.separator.width")) { theme->menu_separator_line_thickness = get_int_if_positive( value, "menu.separator.width"); @@ -952,6 +1218,14 @@ entry(struct theme *theme, const char *key, const char *value) parse_color(value, theme->menu_title_text_color); } + if (match_glob(key, "menu.title.bg")) { + theme->menu_title_border_type = parse_border_type(value); + } + if (match_glob(key, "menu.title.bg.bevel-width")) { + theme->menu_title_bevel_width = + get_int_if_positive(value, "menu.title.bg.bevel-width"); + } + if (match_glob(key, "osd.bg.color")) { parse_color(value, theme->osd_bg_color); } @@ -962,6 +1236,13 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "osd.border.color")) { parse_color(value, theme->osd_border_color); } + + if (match_glob(key, "osd.bg")) { + theme->osd_border_type = parse_border_type(value); + } + if (match_glob(key, "osd.bg.bevel-width")) { + theme->osd_border_bevel_width = get_int_if_positive(value, "osd.bg.bevel-width"); + } /* classic window switcher */ if (match_glob(key, "osd.window-switcher.style-classic.width") || match_glob(key, "osd.window-switcher.width")) { diff --git a/src/workspaces.c b/src/workspaces.c index 7ff471a5..892f95ff 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -19,6 +19,7 @@ #include "common/scene-helpers.h" #include "config/rcxml.h" #include "input/keyboard.h" +#include "common/borderset.h" #include "labwc.h" #include "output.h" #include "theme.h" @@ -96,18 +97,73 @@ _osd_update(void) cairo = cairo_create(buffer->surface); + int bw = theme->osd_border_width; /* Background */ set_cairo_color(cairo, theme->osd_bg_color); - cairo_rectangle(cairo, 0, 0, width, height); + cairo_rectangle(cairo, bw, bw, width-bw*2, height-bw*2); cairo_fill(cairo); /* Border */ - set_cairo_color(cairo, theme->osd_border_color); - struct wlr_fbox border_fbox = { - .width = width, - .height = height, - }; - draw_cairo_border(cairo, border_fbox, theme->osd_border_width); + if (theme->osd_border_type) { + float r = theme->osd_border_color[0]; + float g = theme->osd_border_color[1]; + float b = theme->osd_border_color[2]; + float a = theme->osd_border_color[3]; + + uint32_t colour32 = (uint32_t)(a*255) << 24 | + (uint32_t)(r*255) << 16 | + (uint32_t)(g*255) << 8 | + (uint32_t)(b*255); + struct borderset *renderedborders = get_borders(colour32, bw, + theme->osd_border_type, theme->osd_border_bevel_width); + + cairo_set_source_surface(cairo, renderedborders->top->surface, 0, 0); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, bw, 0, width-bw*2, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->bottom->surface, 0, 0); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, bw, height-bw, width-bw*2, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->left->surface, 0, 0); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, 0, bw, bw, height-bw*2); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->right->surface, 0, 0); + cairo_pattern_set_extend(cairo_get_source(cairo), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cairo, width-bw, bw, bw, height-bw*2); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->tl->surface, 0, 0); + cairo_rectangle(cairo, 0, 0, bw, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->tr->surface, width-bw, 0); + cairo_rectangle(cairo, width - bw, 0, bw, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->bl->surface, + 0, height - bw); + cairo_rectangle(cairo, 0, height - bw, bw, bw); + cairo_fill(cairo); + + cairo_set_source_surface(cairo, renderedborders->br->surface, + width - bw, height -bw); + cairo_rectangle(cairo, width - bw, height - bw, bw, bw); + cairo_fill(cairo); + + set_cairo_color(cairo, theme->osd_border_color); + } else { + set_cairo_color(cairo, theme->osd_border_color); + struct wlr_fbox border_fbox = { + .width = width, + .height = height, + }; + draw_cairo_border(cairo, border_fbox, theme->osd_border_width); + } /* Boxes */ uint16_t x;