mirror of
https://github.com/labwc/labwc.git
synced 2026-02-09 10:07:31 -05:00
Before this commit, the OSD could overflow the screen when displaying many window items. In this commit, we hide the overflowed items and show a scrollbar to fit the OSD within the screen.
259 lines
8.4 KiB
C
259 lines
8.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
#include <assert.h>
|
|
#include <wlr/types/wlr_output_layout.h>
|
|
#include <wlr/types/wlr_scene.h>
|
|
#include <wlr/util/box.h>
|
|
#include <wlr/util/log.h>
|
|
#include "common/buf.h"
|
|
#include "common/font.h"
|
|
#include "common/lab-scene-rect.h"
|
|
#include "common/list.h"
|
|
#include "common/mem.h"
|
|
#include "common/string-helpers.h"
|
|
#include "config/rcxml.h"
|
|
#include "cycle.h"
|
|
#include "labwc.h"
|
|
#include "node.h"
|
|
#include "output.h"
|
|
#include "scaled-buffer/scaled-font-buffer.h"
|
|
#include "scaled-buffer/scaled-icon-buffer.h"
|
|
#include "theme.h"
|
|
#include "view.h"
|
|
#include "workspaces.h"
|
|
|
|
struct cycle_osd_classic_item {
|
|
struct cycle_osd_item base;
|
|
struct wlr_scene_tree *normal_tree, *active_tree;
|
|
};
|
|
|
|
static void
|
|
create_fields_scene(struct server *server, struct view *view,
|
|
struct wlr_scene_tree *parent, const float *text_color,
|
|
const float *bg_color, int field_widths_sum, int x, int y)
|
|
{
|
|
struct theme *theme = server->theme;
|
|
struct window_switcher_classic_theme *switcher_theme =
|
|
&theme->osd_window_switcher_classic;
|
|
|
|
struct cycle_osd_field *field;
|
|
wl_list_for_each(field, &rc.window_switcher.osd.fields, link) {
|
|
int field_width = field_widths_sum * field->width / 100.0;
|
|
struct wlr_scene_node *node = NULL;
|
|
int height = -1;
|
|
|
|
if (field->content == LAB_FIELD_ICON) {
|
|
int icon_size = MIN(field_width,
|
|
switcher_theme->item_icon_size);
|
|
struct scaled_icon_buffer *icon_buffer =
|
|
scaled_icon_buffer_create(parent,
|
|
server, icon_size, icon_size);
|
|
scaled_icon_buffer_set_view(icon_buffer, view);
|
|
node = &icon_buffer->scene_buffer->node;
|
|
height = icon_size;
|
|
} else {
|
|
struct buf buf = BUF_INIT;
|
|
cycle_osd_field_get_content(field, &buf, view);
|
|
|
|
if (!string_null_or_empty(buf.data)) {
|
|
struct scaled_font_buffer *font_buffer =
|
|
scaled_font_buffer_create(parent);
|
|
scaled_font_buffer_update(font_buffer,
|
|
buf.data, field_width,
|
|
&rc.font_osd, text_color, bg_color);
|
|
node = &font_buffer->scene_buffer->node;
|
|
height = font_height(&rc.font_osd);
|
|
}
|
|
|
|
buf_reset(&buf);
|
|
}
|
|
|
|
if (node) {
|
|
int item_height = switcher_theme->item_height;
|
|
wlr_scene_node_set_position(node,
|
|
x, y + (item_height - height) / 2);
|
|
}
|
|
x += field_width + switcher_theme->item_padding_x;
|
|
}
|
|
}
|
|
|
|
static void
|
|
cycle_osd_classic_init(struct cycle_osd_output *osd_output)
|
|
{
|
|
struct output *output = osd_output->output;
|
|
struct server *server = output->server;
|
|
struct theme *theme = server->theme;
|
|
struct window_switcher_classic_theme *switcher_theme =
|
|
&theme->osd_window_switcher_classic;
|
|
int padding = theme->osd_border_width + switcher_theme->padding;
|
|
bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
|
|
const char *workspace_name = server->workspaces.current->name;
|
|
int nr_views = wl_list_length(&server->cycle.views);
|
|
|
|
struct wlr_box output_box;
|
|
wlr_output_layout_get_box(server->output_layout, output->wlr_output,
|
|
&output_box);
|
|
|
|
int w = switcher_theme->width;
|
|
if (switcher_theme->width_is_percent) {
|
|
w = output_box.width * switcher_theme->width / 100;
|
|
}
|
|
int workspace_name_h = 0;
|
|
if (show_workspace) {
|
|
/* workspace indicator */
|
|
workspace_name_h = switcher_theme->item_height;
|
|
}
|
|
int nr_visible_views = (output_box.height - workspace_name_h - 2 * padding)
|
|
/ switcher_theme->item_height;
|
|
nr_visible_views = MIN(nr_visible_views, nr_views);
|
|
int h = workspace_name_h + nr_visible_views * switcher_theme->item_height
|
|
+ 2 * padding;
|
|
|
|
osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree);
|
|
|
|
float *text_color = theme->osd_label_text_color;
|
|
float *bg_color = theme->osd_bg_color;
|
|
|
|
/* Draw background */
|
|
struct lab_scene_rect_options bg_opts = {
|
|
.border_colors = (float *[1]) {theme->osd_border_color},
|
|
.nr_borders = 1,
|
|
.border_width = theme->osd_border_width,
|
|
.bg_color = bg_color,
|
|
.width = w,
|
|
.height = h,
|
|
};
|
|
lab_scene_rect_create(osd_output->tree, &bg_opts);
|
|
|
|
int y = padding;
|
|
|
|
/* Draw workspace indicator */
|
|
if (show_workspace) {
|
|
struct font font = rc.font_osd;
|
|
font.weight = PANGO_WEIGHT_BOLD;
|
|
|
|
/* Center workspace indicator on the x axis */
|
|
int x = (w - font_width(&font, workspace_name)) / 2;
|
|
if (x < 0) {
|
|
wlr_log(WLR_ERROR,
|
|
"not enough space for workspace name in osd");
|
|
goto error;
|
|
}
|
|
|
|
struct scaled_font_buffer *font_buffer =
|
|
scaled_font_buffer_create(osd_output->tree);
|
|
wlr_scene_node_set_position(&font_buffer->scene_buffer->node,
|
|
x, y + (switcher_theme->item_height - font_height(&font)) / 2);
|
|
scaled_font_buffer_update(font_buffer, workspace_name, 0,
|
|
&font, text_color, bg_color);
|
|
y += switcher_theme->item_height;
|
|
}
|
|
|
|
int nr_fields = wl_list_length(&rc.window_switcher.osd.fields);
|
|
|
|
/* This is the width of the area available for text fields */
|
|
int field_widths_sum = w - 2 * padding
|
|
- 2 * switcher_theme->item_active_border_width
|
|
- (nr_fields + 1) * switcher_theme->item_padding_x;
|
|
if (field_widths_sum <= 0) {
|
|
wlr_log(WLR_ERROR, "Not enough spaces for osd contents");
|
|
goto error;
|
|
}
|
|
|
|
float *active_bg_color = switcher_theme->item_active_bg_color;
|
|
float *active_border_color = switcher_theme->item_active_border_color;
|
|
osd_output->items_tree = wlr_scene_tree_create(osd_output->tree);
|
|
|
|
/* Draw text for each node */
|
|
struct view *view;
|
|
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
|
struct cycle_osd_classic_item *item = znew(*item);
|
|
wl_list_append(&osd_output->items, &item->base.link);
|
|
item->base.view = view;
|
|
item->base.tree = wlr_scene_tree_create(osd_output->items_tree);
|
|
node_descriptor_create(&item->base.tree->node,
|
|
LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
|
|
/*
|
|
* OSD border
|
|
* +---------------------------------+
|
|
* | |
|
|
* | item border |
|
|
* |+-------------------------------+|
|
|
* || ||
|
|
* ||padding between each field ||
|
|
* ||| field-1 | field-2 | field-n |||
|
|
* || ||
|
|
* || ||
|
|
* |+-------------------------------+|
|
|
* | |
|
|
* | |
|
|
* +---------------------------------+
|
|
*/
|
|
int x = padding
|
|
+ switcher_theme->item_active_border_width
|
|
+ switcher_theme->item_padding_x;
|
|
item->normal_tree = wlr_scene_tree_create(item->base.tree);
|
|
item->active_tree = wlr_scene_tree_create(item->base.tree);
|
|
wlr_scene_node_set_enabled(&item->active_tree->node, false);
|
|
|
|
/* Highlight around selected window's item */
|
|
struct lab_scene_rect_options highlight_opts = {
|
|
.border_colors = (float *[1]) {active_border_color},
|
|
.nr_borders = 1,
|
|
.border_width = switcher_theme->item_active_border_width,
|
|
.bg_color = active_bg_color,
|
|
.width = w - 2 * padding,
|
|
.height = switcher_theme->item_height,
|
|
};
|
|
struct lab_scene_rect *highlight_rect = lab_scene_rect_create(
|
|
item->active_tree, &highlight_opts);
|
|
wlr_scene_node_set_position(&highlight_rect->tree->node, padding, y);
|
|
|
|
/* hitbox for mouse clicks */
|
|
struct wlr_scene_rect *hitbox = wlr_scene_rect_create(item->base.tree,
|
|
w - 2 * padding, switcher_theme->item_height, (float[4]) {0});
|
|
wlr_scene_node_set_position(&hitbox->node, padding, y);
|
|
|
|
create_fields_scene(server, view, item->normal_tree,
|
|
text_color, bg_color, field_widths_sum, x, y);
|
|
create_fields_scene(server, view, item->active_tree,
|
|
text_color, active_bg_color, field_widths_sum, x, y);
|
|
|
|
y += switcher_theme->item_height;
|
|
}
|
|
|
|
struct wlr_box scrollbar_area = {
|
|
.x = w - padding - SCROLLBAR_W,
|
|
.y = padding,
|
|
.width = SCROLLBAR_W,
|
|
.height = h - 2 * padding,
|
|
};
|
|
cycle_osd_scroll_init(osd_output, scrollbar_area,
|
|
switcher_theme->item_height,
|
|
/*nr_cols*/ 1, /*nr_rows*/ nr_views, nr_visible_views,
|
|
active_border_color, active_bg_color);
|
|
|
|
error:;
|
|
/* Center OSD */
|
|
wlr_scene_node_set_position(&osd_output->tree->node,
|
|
output_box.x + (output_box.width - w) / 2,
|
|
output_box.y + (output_box.height - h) / 2);
|
|
}
|
|
|
|
static void
|
|
cycle_osd_classic_update(struct cycle_osd_output *osd_output)
|
|
{
|
|
struct server *server = osd_output->output->server;
|
|
cycle_osd_scroll_update(osd_output);
|
|
|
|
struct cycle_osd_classic_item *item;
|
|
wl_list_for_each(item, &osd_output->items, base.link) {
|
|
bool active = item->base.view == server->cycle.selected_view;
|
|
wlr_scene_node_set_enabled(&item->normal_tree->node, !active);
|
|
wlr_scene_node_set_enabled(&item->active_tree->node, active);
|
|
}
|
|
}
|
|
|
|
struct cycle_osd_impl cycle_osd_classic_impl = {
|
|
.init = cycle_osd_classic_init,
|
|
.update = cycle_osd_classic_update,
|
|
};
|