- iconify.xbm
- close.xbm
- menu.xbm
+- max_toggled.xbm
+
+Additional icons can be defined to be shown when the mouse pointer is hovering
+over the button in question:
+
+- max_hover.xbm
+- iconify_hover.xbm
+- close_hover.xbm
+- menu_hover.xbm
+- max_hover_toggled.xbm
One advantage of xbm buttons over other formats is that they change color based
on the theme. Other formats use the suffices "-active" and "-inactive" to align
struct lab_data_buffer;
/* button_xbm_load - Convert xbm file to buffer with cairo surface */
-void button_xbm_load(const char *button_name, struct lab_data_buffer **buffer,
- char *fallback_button, float *rgba);
+void button_xbm_load(const char *button_name, const char *alt_name,
+ struct lab_data_buffer **buffer, char *fallback_button, float *rgba);
#endif /* LABWC_BUTTON_XBM_H */
#include <wlr/util/box.h>
#include "common/macros.h"
#include "ssd.h"
+#include "view.h"
#define FOR_EACH(tmp, ...) \
{ \
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;
+ struct wlr_scene_node *toggled_hover;
struct wlr_scene_node *background;
struct wl_listener destroy;
struct ssd_hover_state {
struct view *view;
struct wlr_scene_node *node;
+ struct wlr_scene_node *old_node;
+ int maximized;
};
struct wlr_buffer;
struct ssd_part *add_scene_button(
struct wl_list *part_list, enum ssd_part_type type,
struct wlr_scene_tree *parent, float *bg_color,
- struct wlr_buffer *icon_buffer, int x, struct view *view);
+ struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer,
+ int x, struct view *view);
+void
+add_toggled_icon(struct wl_list *part_list, enum ssd_part_type type,
+ struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer);
struct ssd_part *add_scene_button_corner(
struct wl_list *part_list, enum ssd_part_type type,
enum ssd_part_type corner_type, struct wlr_scene_tree *parent,
struct wlr_buffer *corner_buffer, struct wlr_buffer *icon_buffer,
- int x, struct view *view);
+ struct wlr_buffer *hover_buffer, int x, struct view *view);
/* SSD internal helpers */
struct ssd_part *ssd_get_part(
/* textures */
struct lab_data_buffer *button_close_active_unpressed;
struct lab_data_buffer *button_maximize_active_unpressed;
+ struct lab_data_buffer *button_restore_active_unpressed;
struct lab_data_buffer *button_iconify_active_unpressed;
struct lab_data_buffer *button_menu_active_unpressed;
struct lab_data_buffer *button_close_inactive_unpressed;
struct lab_data_buffer *button_maximize_inactive_unpressed;
+ struct lab_data_buffer *button_restore_inactive_unpressed;
struct lab_data_buffer *button_iconify_inactive_unpressed;
struct lab_data_buffer *button_menu_inactive_unpressed;
+ struct lab_data_buffer *button_close_active_hover;
+ struct lab_data_buffer *button_maximize_active_hover;
+ struct lab_data_buffer *button_restore_active_hover;
+ struct lab_data_buffer *button_iconify_active_hover;
+ struct lab_data_buffer *button_menu_active_hover;
+
+ struct lab_data_buffer *button_close_inactive_hover;
+ struct lab_data_buffer *button_maximize_inactive_hover;
+ struct lab_data_buffer *button_restore_inactive_hover;
+ struct lab_data_buffer *button_iconify_inactive_hover;
+ struct lab_data_buffer *button_menu_inactive_hover;
+
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;
}
void
-button_xbm_load(const char *button_name, struct lab_data_buffer **buffer,
- char *fallback_button, float *rgba)
+button_xbm_load(const char *button_name, const char *alt_name,
+ struct lab_data_buffer **buffer, char *fallback_button, float *rgba)
{
struct pixmap pixmap = {0};
if (*buffer) {
free(tokens);
}
}
+ if (!pixmap.data && *alt_name) {
+ button_filename(alt_name, filename, sizeof(filename));
+ char *token_buffer = grab_file(filename);
+ if (token_buffer) {
+ struct token *tokens = tokenize_xbm(token_buffer);
+ free(token_buffer);
+ pixmap = parse_xbm_tokens(tokens);
+ if (tokens) {
+ free(tokens);
+ }
+ }
+ }
if (!pixmap.data) {
pixmap = parse_xbm_builtin(fallback_button, 6);
}
add_scene_button_corner(struct wl_list *part_list, enum ssd_part_type type,
enum ssd_part_type corner_type, struct wlr_scene_tree *parent,
struct wlr_buffer *corner_buffer, struct wlr_buffer *icon_buffer,
- int x, struct view *view)
+ struct wlr_buffer *hover_buffer, int x, struct view *view)
{
int offset_x;
float invisible[4] = { 0, 0, 0, 0 };
-offset_x, -rc.theme->border_width);
/* Finally just put a usual theme button on top, using an invisible hitbox */
- add_scene_button(part_list, type, parent, invisible, icon_buffer, 0, view);
+ add_scene_button(part_list, type, parent, invisible, icon_buffer, hover_buffer, 0, view);
return button_root;
}
struct ssd_part *
add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
struct wlr_scene_tree *parent, float *bg_color,
- struct wlr_buffer *icon_buffer, int x, struct view *view)
+ struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer,
+ int x, struct view *view)
{
- struct wlr_scene_node *hover;
- float hover_bg[4] = {0.15f, 0.15f, 0.15f, 0.3f};
-
struct ssd_part *button_root = add_scene_part(part_list, type);
parent = wlr_scene_tree_create(parent);
button_root->node = &parent->node;
wlr_scene_buffer_from_node(icon_part->node),
icon_geo.width, icon_geo.height);
- /* Hover overlay */
- hover = add_scene_rect(part_list, type, parent, SSD_BUTTON_WIDTH,
- rc.theme->title_height, 0, 0, hover_bg)->node;
- wlr_scene_node_set_enabled(hover, false);
+ /* Hover icon */
+ struct wlr_box hover_geo = get_scale_box(hover_buffer,
+ SSD_BUTTON_WIDTH, rc.theme->title_height);
+ struct ssd_part *hover_part = add_scene_buffer(part_list, type,
+ parent, hover_buffer, hover_geo.x, hover_geo.y);
+
+ /* 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);
+
+ wlr_scene_node_set_enabled(hover_part->node, false);
struct ssd_button *button = ssd_button_descriptor_create(button_root->node);
button->type = type;
button->view = view;
- button->hover = hover;
+ button->normal = icon_part->node;
+ button->hover = hover_part->node;
button->background = bg_rect->node;
+ button->toggled = NULL;
+ button->toggled_hover = NULL;
return button_root;
}
+void
+add_toggled_icon(struct wl_list *part_list, enum ssd_part_type type,
+ struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer)
+{
+ struct ssd_part *part = ssd_get_part(part_list, type);
+ struct ssd_button *button = node_ssd_button_from_node(part->node);
+ struct wlr_scene_tree *parent = wlr_scene_tree_from_node(part->node);
+
+ /* Alternate icon */
+ struct wlr_box icon_geo = get_scale_box(icon_buffer,
+ SSD_BUTTON_WIDTH, rc.theme->title_height);
+
+ struct ssd_part *alticon_part = add_scene_buffer(part_list, type,
+ parent, 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,
+ SSD_BUTTON_WIDTH, rc.theme->title_height);
+ struct ssd_part *althover_part = add_scene_buffer(part_list, type,
+ parent, hover_buffer, hover_geo.x, hover_geo.y);
+
+ wlr_scene_buffer_set_dest_size(
+ wlr_scene_buffer_from_node(althover_part->node),
+ hover_geo.width, hover_geo.height);
+
+ wlr_scene_node_set_enabled(althover_part->node, false);
+
+ button->toggled = alticon_part->node;
+ button->toggled_hover = althover_part->node;
+}
+
struct ssd_part *
ssd_get_part(struct wl_list *part_list, enum ssd_part_type type)
{
struct wlr_buffer *menu_button_unpressed;
struct wlr_buffer *iconify_button_unpressed;
struct wlr_buffer *maximize_button_unpressed;
+ struct wlr_buffer *restore_button_unpressed;
struct wlr_buffer *close_button_unpressed;
+ struct wlr_buffer *menu_button_hover;
+ struct wlr_buffer *iconify_button_hover;
+ struct wlr_buffer *maximize_button_hover;
+ struct wlr_buffer *restore_button_hover;
+ struct wlr_buffer *close_button_hover;
+
ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree);
struct ssd_sub_tree *subtree;
iconify_button_unpressed = &theme->button_iconify_active_unpressed->base;
close_button_unpressed = &theme->button_close_active_unpressed->base;
maximize_button_unpressed = &theme->button_maximize_active_unpressed->base;
+ restore_button_unpressed = &theme->button_restore_active_unpressed->base;
+ menu_button_hover = &theme->button_menu_active_hover->base;
+ iconify_button_hover = &theme->button_iconify_active_hover->base;
+ close_button_hover = &theme->button_close_active_hover->base;
+ maximize_button_hover = &theme->button_maximize_active_hover->base;
+ restore_button_hover = &theme->button_restore_active_hover->base;
} else {
color = theme->window_inactive_title_bg_color;
corner_top_left = &theme->corner_top_left_inactive_normal->base;
iconify_button_unpressed = &theme->button_iconify_inactive_unpressed->base;
maximize_button_unpressed =
&theme->button_maximize_inactive_unpressed->base;
+ restore_button_unpressed = &theme->button_restore_inactive_unpressed->base;
close_button_unpressed = &theme->button_close_inactive_unpressed->base;
+ menu_button_hover = &theme->button_menu_inactive_hover->base;
+ iconify_button_hover = &theme->button_iconify_inactive_hover->base;
+ close_button_hover = &theme->button_close_inactive_hover->base;
+ maximize_button_hover = &theme->button_maximize_inactive_hover->base;
+ restore_button_hover = &theme->button_restore_inactive_hover->base;
wlr_scene_node_set_enabled(&parent->node, false);
}
wl_list_init(&subtree->parts);
/* Buttons */
add_scene_button_corner(&subtree->parts,
LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_PART_CORNER_TOP_LEFT, parent,
- corner_top_left, menu_button_unpressed, 0, view);
+ corner_top_left, menu_button_unpressed, menu_button_hover, 0, view);
add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent,
- color, iconify_button_unpressed,
+ color, iconify_button_unpressed, iconify_button_hover,
width - SSD_BUTTON_WIDTH * 3, view);
add_scene_button(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent,
- color, maximize_button_unpressed,
+ color, maximize_button_unpressed, maximize_button_hover,
width - SSD_BUTTON_WIDTH * 2, view);
+ add_toggled_icon(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE,
+ restore_button_unpressed, restore_button_hover);
add_scene_button_corner(&subtree->parts,
LAB_SSD_BUTTON_CLOSE, LAB_SSD_PART_CORNER_TOP_RIGHT, parent,
- corner_top_right, close_button_unpressed,
+ corner_top_right, close_button_unpressed, close_button_hover,
width - SSD_BUTTON_WIDTH * 1, view);
} FOR_EACH_END
width - SSD_BUTTON_WIDTH * 3, 0);
}
continue;
- case LAB_SSD_BUTTON_MAXIMIZE:
+ case LAB_SSD_BUTTON_MAXIMIZE:
if (is_direct_child(part->node, subtree)) {
wlr_scene_node_set_position(part->node,
width - SSD_BUTTON_WIDTH * 2, 0);
+ struct ssd_button *button =
+ node_ssd_button_from_node(part->node);
+ if (button->toggled) {
+ wlr_scene_node_set_enabled(button->normal,
+ !maximized);
+ wlr_scene_node_set_enabled(button->toggled,
+ maximized);
+ wlr_scene_node_set_enabled(button->hover,
+ false);
+ wlr_scene_node_set_enabled(button->toggled_hover,
+ false);
+ }
}
continue;
case LAB_SSD_PART_CORNER_TOP_RIGHT:
disable_old_hover:
if (hover_state->node) {
wlr_scene_node_set_enabled(hover_state->node, false);
+ if (hover_state->maximized == (int)hover_state->view->maximized
+ || hover_state->maximized == -1) {
+ wlr_scene_node_set_enabled(hover_state->old_node, true);
+ }
hover_state->view = NULL;
hover_state->node = NULL;
}
if (button) {
- wlr_scene_node_set_enabled(button->hover, true);
+ bool maximized = button->view->maximized;
+ if (maximized && !button->toggled) {
+ maximized = false;
+ }
+ wlr_scene_node_set_enabled(maximized ? button->toggled_hover : button->hover, true);
hover_state->view = button->view;
- hover_state->node = button->hover;
+ hover_state->node = maximized ? button->toggled_hover : button->hover;
+ hover_state->old_node = maximized ? button->toggled : button->normal;
+ hover_state->maximized = button->toggled ? (int)button->view->maximized : -1;
+ wlr_scene_node_set_enabled(maximized ? button->toggled : button->normal, false);
}
}
struct button {
const char *name;
+ const char *alt_name;
char fallback_button[6]; /* built-in 6x6 button */
struct {
struct lab_data_buffer **buffer;
{
struct button buttons[] = {
{
- "menu",
+ "menu", NULL,
{ 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00 },
{
&theme->button_menu_active_unpressed,
},
},
{
- "iconify",
+ "iconify", NULL,
{ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f },
{
&theme->button_iconify_active_unpressed,
},
},
{
- "max",
+ "max", NULL,
{ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f },
{
&theme->button_maximize_active_unpressed,
},
},
{
- "close",
+ "max_toggled", NULL,
+ { 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f },
+ {
+ &theme->button_restore_active_unpressed,
+ theme->window_active_button_max_unpressed_image_color,
+ },
+ {
+ &theme->button_restore_inactive_unpressed,
+ theme->window_inactive_button_max_unpressed_image_color,
+ },
+ },
+ {
+ "close", NULL,
{ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 },
{
&theme->button_close_active_unpressed,
theme->window_inactive_button_close_unpressed_image_color,
},
},
+ {
+ "menu_hover", NULL,
+ { 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00 },
+ {
+ &theme->button_menu_active_hover,
+ theme->window_active_button_menu_unpressed_image_color,
+ },
+ {
+ &theme->button_menu_inactive_hover,
+ theme->window_inactive_button_menu_unpressed_image_color,
+ },
+ },
+ {
+ "iconify_hover", NULL,
+ { 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f },
+ {
+ &theme->button_iconify_active_hover,
+ theme->window_active_button_iconify_unpressed_image_color,
+ },
+ {
+ &theme->button_iconify_inactive_hover,
+ theme->window_inactive_button_iconify_unpressed_image_color,
+ },
+ },
+ {
+ "max_hover", NULL,
+ { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f },
+ {
+ &theme->button_maximize_active_hover,
+ theme->window_active_button_max_unpressed_image_color,
+ },
+ {
+ &theme->button_maximize_inactive_hover,
+ theme->window_inactive_button_max_unpressed_image_color,
+ },
+ },
+ {
+ "max_hover_toggled", "max_toggled_hover",
+ { 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f },
+ {
+ &theme->button_restore_active_hover,
+ theme->window_active_button_max_unpressed_image_color,
+ },
+ {
+ &theme->button_restore_inactive_hover,
+ theme->window_inactive_button_max_unpressed_image_color,
+ },
+ },
+ {
+ "close_hover", NULL,
+ { 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 },
+ {
+ &theme->button_close_active_hover,
+ theme->window_active_button_close_unpressed_image_color,
+ },
+ {
+ &theme->button_close_inactive_hover,
+ theme->window_inactive_button_close_unpressed_image_color,
+ },
+ },
};
char filename[4096] = {0};
+ char alt_filename[4096] = {0};
for (size_t i = 0; i < ARRAY_SIZE(buttons); ++i) {
struct button *b = &buttons[i];
/* Try png icon first */
snprintf(filename, sizeof(filename), "%s-active.png", b->name);
button_png_load(filename, b->active.buffer);
+ if (!*b->active.buffer && b->alt_name) {
+ snprintf(filename, sizeof(filename), "%s-active.png", b->alt_name);
+ button_png_load(filename, b->active.buffer);
+ }
snprintf(filename, sizeof(filename), "%s-inactive.png", b->name);
button_png_load(filename, b->inactive.buffer);
+ if (!*b->inactive.buffer && b->alt_name) {
+ snprintf(filename, sizeof(filename), "%s-inactive.png", b->alt_name);
+ button_png_load(filename, b->inactive.buffer);
+ }
#if HAVE_RSVG
/* Then try svg icon */
snprintf(filename, sizeof(filename), "%s-active.svg", b->name);
button_svg_load(filename, b->active.buffer, size);
}
+ if (!*b->active.buffer && b->alt_name) {
+ snprintf(filename, sizeof(filename), "%s-active.svg", b->alt_name);
+ button_svg_load(filename, b->active.buffer, size);
+ }
if (!*b->inactive.buffer) {
snprintf(filename, sizeof(filename), "%s-inactive.svg", b->name);
button_svg_load(filename, b->inactive.buffer, size);
}
+ if (!*b->active.buffer && b->alt_name) {
+ snprintf(filename, sizeof(filename), "%s-inactive.svg", b->alt_name);
+ button_svg_load(filename, b->inactive.buffer, size);
+ }
#endif
/* If there were no png/svg buttons, use xbm */
snprintf(filename, sizeof(filename), "%s.xbm", b->name);
+ if (b->alt_name) {
+ snprintf(alt_filename, sizeof(alt_filename), "%s.xbm", b->alt_name);
+ }
if (!*b->active.buffer) {
- button_xbm_load(filename, b->active.buffer,
+ button_xbm_load(filename, alt_filename, b->active.buffer,
b->fallback_button, b->active.rgba);
}
if (!*b->inactive.buffer) {
- button_xbm_load(filename, b->inactive.buffer,
+ button_xbm_load(filename, alt_filename, b->inactive.buffer,
b->fallback_button, b->inactive.rgba);
}
}