mirror of
https://github.com/labwc/labwc.git
synced 2026-02-07 04:07:42 -05:00
cycle: implement scrollable OSD
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.
This commit is contained in:
parent
97b31429a0
commit
742c2b53fd
5 changed files with 185 additions and 13 deletions
|
|
@ -8,6 +8,7 @@
|
|||
#include "config/types.h"
|
||||
|
||||
struct output;
|
||||
struct wlr_box;
|
||||
|
||||
enum lab_cycle_dir {
|
||||
LAB_CYCLE_DIR_NONE,
|
||||
|
|
@ -67,8 +68,21 @@ struct cycle_osd_output {
|
|||
struct output *output;
|
||||
struct wl_listener tree_destroy;
|
||||
|
||||
/* set by cycle_osd_impl->init() */
|
||||
struct wl_list items; /* struct cycle_osd_item.link */
|
||||
struct wlr_scene_tree *tree;
|
||||
/* set by cycle_osd_impl->init() and moved by cycle_osd_scroll_update() */
|
||||
struct wlr_scene_tree *items_tree;
|
||||
|
||||
/* used in osd-scroll.c */
|
||||
struct cycle_osd_scroll_context {
|
||||
int top_row_idx;
|
||||
int nr_rows, nr_cols, nr_visible_rows;
|
||||
int delta_y;
|
||||
struct wlr_box bar_area;
|
||||
struct wlr_scene_tree *bar_tree;
|
||||
struct lab_scene_rect *bar;
|
||||
} scroll;
|
||||
};
|
||||
|
||||
struct buf;
|
||||
|
|
@ -124,6 +138,28 @@ struct cycle_osd_impl {
|
|||
void (*update)(struct cycle_osd_output *osd_output);
|
||||
};
|
||||
|
||||
#define SCROLLBAR_W 10
|
||||
|
||||
/**
|
||||
* Initialize the context and scene for scrolling OSD items.
|
||||
*
|
||||
* @output: Output of the OSD
|
||||
* @bar_area: Area where the scrollbar is drawn
|
||||
* @delta_y: The vertical delta by which items are scrolled (usually item height)
|
||||
* @nr_cols: Number of columns in the OSD
|
||||
* @nr_rows: Number of rows in the OSD
|
||||
* @nr_visible_rows: Number of visible rows in the OSD
|
||||
* @border_color: Border color of the scrollbar
|
||||
* @bg_color: Background color of the scrollbar
|
||||
*/
|
||||
void cycle_osd_scroll_init(struct cycle_osd_output *osd_output,
|
||||
struct wlr_box bar_area, int delta_y,
|
||||
int nr_cols, int nr_rows, int nr_visible_rows,
|
||||
float *border_color, float *bg_color);
|
||||
|
||||
/* Scroll the OSD to show server->cycle.selected_view if needed */
|
||||
void cycle_osd_scroll_update(struct cycle_osd_output *osd_output);
|
||||
|
||||
extern struct cycle_osd_impl cycle_osd_classic_impl;
|
||||
extern struct cycle_osd_impl cycle_osd_thumbnail_impl;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@ labwc_sources += files(
|
|||
'cycle.c',
|
||||
'osd-classic.c',
|
||||
'osd-field.c',
|
||||
'osd-scroll.c',
|
||||
'osd-thumbnail.c',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -97,11 +97,16 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
|
|||
if (switcher_theme->width_is_percent) {
|
||||
w = output_box.width * switcher_theme->width / 100;
|
||||
}
|
||||
int h = nr_views * switcher_theme->item_height + 2 * padding;
|
||||
int workspace_name_h = 0;
|
||||
if (show_workspace) {
|
||||
/* workspace indicator */
|
||||
h += switcher_theme->item_height;
|
||||
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);
|
||||
|
||||
|
|
@ -154,13 +159,17 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
|
|||
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->tree);
|
||||
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);
|
||||
/*
|
||||
|
|
@ -186,9 +195,6 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
|
|||
item->active_tree = wlr_scene_tree_create(item->base.tree);
|
||||
wlr_scene_node_set_enabled(&item->active_tree->node, false);
|
||||
|
||||
float *active_bg_color = switcher_theme->item_active_bg_color;
|
||||
float *active_border_color = switcher_theme->item_active_border_color;
|
||||
|
||||
/* Highlight around selected window's item */
|
||||
struct lab_scene_rect_options highlight_opts = {
|
||||
.border_colors = (float *[1]) {active_border_color},
|
||||
|
|
@ -215,6 +221,17 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
|
|||
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,
|
||||
|
|
@ -226,6 +243,8 @@ 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;
|
||||
|
|
|
|||
95
src/cycle/osd-scroll.c
Normal file
95
src/cycle/osd-scroll.c
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <assert.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include "common/lab-scene-rect.h"
|
||||
#include "labwc.h"
|
||||
#include "cycle.h"
|
||||
#include "output.h"
|
||||
|
||||
void
|
||||
cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_area,
|
||||
int delta_y, int nr_cols, int nr_rows, int nr_visible_rows,
|
||||
float *border_color, float *bg_color)
|
||||
{
|
||||
if (nr_visible_rows >= nr_rows) {
|
||||
/* OSD doesn't have so many windows to scroll through */
|
||||
return;
|
||||
}
|
||||
|
||||
struct cycle_osd_scroll_context *scroll = &osd_output->scroll;
|
||||
scroll->nr_cols = nr_cols;
|
||||
scroll->nr_rows = nr_rows;
|
||||
scroll->nr_visible_rows = nr_visible_rows;
|
||||
scroll->top_row_idx = 0;
|
||||
scroll->bar_area = bar_area;
|
||||
scroll->delta_y = delta_y;
|
||||
scroll->bar_tree = wlr_scene_tree_create(osd_output->tree);
|
||||
wlr_scene_node_set_position(&scroll->bar_tree->node,
|
||||
bar_area.x, bar_area.y);
|
||||
|
||||
struct lab_scene_rect_options scrollbar_opts = {
|
||||
.border_colors = (float *[1]) { border_color },
|
||||
.nr_borders = 1,
|
||||
.border_width = 1,
|
||||
.bg_color = bg_color,
|
||||
.width = bar_area.width,
|
||||
.height = bar_area.height * nr_visible_rows / nr_rows,
|
||||
};
|
||||
scroll->bar = lab_scene_rect_create(scroll->bar_tree, &scrollbar_opts);
|
||||
}
|
||||
|
||||
static int
|
||||
get_cycle_idx(struct cycle_osd_output *osd_output)
|
||||
{
|
||||
struct server *server = osd_output->output->server;
|
||||
|
||||
int idx = 0;
|
||||
struct cycle_osd_item *item;
|
||||
wl_list_for_each(item, &osd_output->items, link) {
|
||||
if (item->view == server->cycle.selected_view) {
|
||||
return idx;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
assert(false && "selected view not found in items");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
cycle_osd_scroll_update(struct cycle_osd_output *osd_output)
|
||||
{
|
||||
struct cycle_osd_scroll_context *scroll = &osd_output->scroll;
|
||||
if (!scroll->bar) {
|
||||
return;
|
||||
}
|
||||
|
||||
int cycle_idx = get_cycle_idx(osd_output);
|
||||
|
||||
/* Update the range of visible rows */
|
||||
int bottom_row_idx = scroll->top_row_idx + scroll->nr_visible_rows;
|
||||
while (cycle_idx < scroll->top_row_idx * scroll->nr_cols) {
|
||||
scroll->top_row_idx--;
|
||||
bottom_row_idx--;
|
||||
}
|
||||
while (cycle_idx >= bottom_row_idx * scroll->nr_cols) {
|
||||
scroll->top_row_idx++;
|
||||
bottom_row_idx++;
|
||||
}
|
||||
|
||||
/* Vertically move scrollbar by (bar height) / (# of total rows) */
|
||||
wlr_scene_node_set_position(&scroll->bar->tree->node, 0,
|
||||
scroll->bar_area.height * scroll->top_row_idx / scroll->nr_rows);
|
||||
/* Vertically move items */
|
||||
wlr_scene_node_set_position(&osd_output->items_tree->node, 0,
|
||||
-scroll->delta_y * scroll->top_row_idx);
|
||||
|
||||
/* Hide items outside of visible area */
|
||||
int idx = 0;
|
||||
struct cycle_osd_item *item;
|
||||
wl_list_for_each(item, &osd_output->items, link) {
|
||||
bool visible = idx >= scroll->top_row_idx * scroll->nr_cols
|
||||
&& idx < bottom_row_idx * scroll->nr_cols;
|
||||
wlr_scene_node_set_enabled(&item->tree->node, visible);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
|
@ -194,9 +194,10 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
|
|||
}
|
||||
|
||||
static void
|
||||
get_items_geometry(struct output *output, struct theme *theme,
|
||||
int nr_thumbs, int *nr_rows, int *nr_cols)
|
||||
get_items_geometry(struct output *output, int nr_thumbs,
|
||||
int *nr_cols, int *nr_rows, int *nr_visible_rows)
|
||||
{
|
||||
struct theme *theme = output->server->theme;
|
||||
struct window_switcher_thumbnail_theme *switcher_theme =
|
||||
&theme->osd_window_switcher_thumbnail;
|
||||
int output_width, output_height;
|
||||
|
|
@ -223,6 +224,9 @@ get_items_geometry(struct output *output, struct theme *theme,
|
|||
(*nr_rows)++;
|
||||
*nr_cols = ceilf((float)nr_thumbs / *nr_rows);
|
||||
}
|
||||
|
||||
*nr_visible_rows = MIN(*nr_rows,
|
||||
(output_height - 2 * padding) / switcher_theme->item_height);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -236,18 +240,19 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
|
|||
int padding = theme->osd_border_width + switcher_theme->padding;
|
||||
|
||||
osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree);
|
||||
osd_output->items_tree = wlr_scene_tree_create(osd_output->tree);
|
||||
|
||||
int nr_views = wl_list_length(&server->cycle.views);
|
||||
assert(nr_views > 0);
|
||||
int nr_rows, nr_cols;
|
||||
get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols);
|
||||
int nr_cols, nr_rows, nr_visible_rows;
|
||||
get_items_geometry(output, nr_views, &nr_cols, &nr_rows, &nr_visible_rows);
|
||||
|
||||
/* items */
|
||||
struct view *view;
|
||||
int index = 0;
|
||||
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
||||
struct cycle_osd_thumbnail_item *item = create_item_scene(
|
||||
osd_output->tree, view, osd_output);
|
||||
osd_output->items_tree, view, osd_output);
|
||||
if (!item) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -257,14 +262,28 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
|
|||
index++;
|
||||
}
|
||||
|
||||
int items_width = switcher_theme->item_width * nr_cols;
|
||||
int items_height = switcher_theme->item_height * nr_visible_rows;
|
||||
|
||||
struct wlr_box scrollbar_area = {
|
||||
.x = padding + items_width - SCROLLBAR_W,
|
||||
.y = padding,
|
||||
.width = SCROLLBAR_W,
|
||||
.height = items_height,
|
||||
};
|
||||
cycle_osd_scroll_init(osd_output, scrollbar_area,
|
||||
switcher_theme->item_height, nr_cols, nr_rows, nr_visible_rows,
|
||||
switcher_theme->item_active_border_color,
|
||||
switcher_theme->item_active_bg_color);
|
||||
|
||||
/* 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 = theme->osd_bg_color,
|
||||
.width = nr_cols * switcher_theme->item_width + 2 * padding,
|
||||
.height = nr_rows * switcher_theme->item_height + 2 * padding,
|
||||
.width = items_width + 2 * padding,
|
||||
.height = items_height + 2 * padding,
|
||||
};
|
||||
struct lab_scene_rect *bg =
|
||||
lab_scene_rect_create(osd_output->tree, &bg_opts);
|
||||
|
|
@ -283,6 +302,8 @@ static void
|
|||
cycle_osd_thumbnail_update(struct cycle_osd_output *osd_output)
|
||||
{
|
||||
struct server *server = osd_output->output->server;
|
||||
cycle_osd_scroll_update(osd_output);
|
||||
|
||||
struct cycle_osd_thumbnail_item *item;
|
||||
wl_list_for_each(item, &osd_output->items, base.link) {
|
||||
bool active = (item->base.view == server->cycle.selected_view);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue