mirror of
https://github.com/labwc/labwc.git
synced 2026-03-17 05:33:47 -04: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"
|
#include "config/types.h"
|
||||||
|
|
||||||
struct output;
|
struct output;
|
||||||
|
struct wlr_box;
|
||||||
|
|
||||||
enum lab_cycle_dir {
|
enum lab_cycle_dir {
|
||||||
LAB_CYCLE_DIR_NONE,
|
LAB_CYCLE_DIR_NONE,
|
||||||
|
|
@ -67,8 +68,21 @@ struct cycle_osd_output {
|
||||||
struct output *output;
|
struct output *output;
|
||||||
struct wl_listener tree_destroy;
|
struct wl_listener tree_destroy;
|
||||||
|
|
||||||
|
/* set by cycle_osd_impl->init() */
|
||||||
struct wl_list items; /* struct cycle_osd_item.link */
|
struct wl_list items; /* struct cycle_osd_item.link */
|
||||||
struct wlr_scene_tree *tree;
|
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;
|
struct buf;
|
||||||
|
|
@ -124,6 +138,28 @@ struct cycle_osd_impl {
|
||||||
void (*update)(struct cycle_osd_output *osd_output);
|
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_classic_impl;
|
||||||
extern struct cycle_osd_impl cycle_osd_thumbnail_impl;
|
extern struct cycle_osd_impl cycle_osd_thumbnail_impl;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@ labwc_sources += files(
|
||||||
'cycle.c',
|
'cycle.c',
|
||||||
'osd-classic.c',
|
'osd-classic.c',
|
||||||
'osd-field.c',
|
'osd-field.c',
|
||||||
|
'osd-scroll.c',
|
||||||
'osd-thumbnail.c',
|
'osd-thumbnail.c',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -97,11 +97,16 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
|
||||||
if (switcher_theme->width_is_percent) {
|
if (switcher_theme->width_is_percent) {
|
||||||
w = output_box.width * switcher_theme->width / 100;
|
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) {
|
if (show_workspace) {
|
||||||
/* workspace indicator */
|
/* 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);
|
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;
|
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 */
|
/* Draw text for each node */
|
||||||
struct view *view;
|
struct view *view;
|
||||||
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
||||||
struct cycle_osd_classic_item *item = znew(*item);
|
struct cycle_osd_classic_item *item = znew(*item);
|
||||||
wl_list_append(&osd_output->items, &item->base.link);
|
wl_list_append(&osd_output->items, &item->base.link);
|
||||||
item->base.view = view;
|
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,
|
node_descriptor_create(&item->base.tree->node,
|
||||||
LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
|
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);
|
item->active_tree = wlr_scene_tree_create(item->base.tree);
|
||||||
wlr_scene_node_set_enabled(&item->active_tree->node, false);
|
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 */
|
/* Highlight around selected window's item */
|
||||||
struct lab_scene_rect_options highlight_opts = {
|
struct lab_scene_rect_options highlight_opts = {
|
||||||
.border_colors = (float *[1]) {active_border_color},
|
.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;
|
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:;
|
error:;
|
||||||
/* Center OSD */
|
/* Center OSD */
|
||||||
wlr_scene_node_set_position(&osd_output->tree->node,
|
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)
|
cycle_osd_classic_update(struct cycle_osd_output *osd_output)
|
||||||
{
|
{
|
||||||
struct server *server = osd_output->output->server;
|
struct server *server = osd_output->output->server;
|
||||||
|
cycle_osd_scroll_update(osd_output);
|
||||||
|
|
||||||
struct cycle_osd_classic_item *item;
|
struct cycle_osd_classic_item *item;
|
||||||
wl_list_for_each(item, &osd_output->items, base.link) {
|
wl_list_for_each(item, &osd_output->items, base.link) {
|
||||||
bool active = item->base.view == server->cycle.selected_view;
|
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
|
static void
|
||||||
get_items_geometry(struct output *output, struct theme *theme,
|
get_items_geometry(struct output *output, int nr_thumbs,
|
||||||
int nr_thumbs, int *nr_rows, int *nr_cols)
|
int *nr_cols, int *nr_rows, int *nr_visible_rows)
|
||||||
{
|
{
|
||||||
|
struct theme *theme = output->server->theme;
|
||||||
struct window_switcher_thumbnail_theme *switcher_theme =
|
struct window_switcher_thumbnail_theme *switcher_theme =
|
||||||
&theme->osd_window_switcher_thumbnail;
|
&theme->osd_window_switcher_thumbnail;
|
||||||
int output_width, output_height;
|
int output_width, output_height;
|
||||||
|
|
@ -223,6 +224,9 @@ get_items_geometry(struct output *output, struct theme *theme,
|
||||||
(*nr_rows)++;
|
(*nr_rows)++;
|
||||||
*nr_cols = ceilf((float)nr_thumbs / *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
|
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;
|
int padding = theme->osd_border_width + switcher_theme->padding;
|
||||||
|
|
||||||
osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree);
|
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);
|
int nr_views = wl_list_length(&server->cycle.views);
|
||||||
assert(nr_views > 0);
|
assert(nr_views > 0);
|
||||||
int nr_rows, nr_cols;
|
int nr_cols, nr_rows, nr_visible_rows;
|
||||||
get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols);
|
get_items_geometry(output, nr_views, &nr_cols, &nr_rows, &nr_visible_rows);
|
||||||
|
|
||||||
/* items */
|
/* items */
|
||||||
struct view *view;
|
struct view *view;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
||||||
struct cycle_osd_thumbnail_item *item = create_item_scene(
|
struct cycle_osd_thumbnail_item *item = create_item_scene(
|
||||||
osd_output->tree, view, osd_output);
|
osd_output->items_tree, view, osd_output);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -257,14 +262,28 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
|
||||||
index++;
|
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 */
|
/* background */
|
||||||
struct lab_scene_rect_options bg_opts = {
|
struct lab_scene_rect_options bg_opts = {
|
||||||
.border_colors = (float *[1]) { theme->osd_border_color },
|
.border_colors = (float *[1]) { theme->osd_border_color },
|
||||||
.nr_borders = 1,
|
.nr_borders = 1,
|
||||||
.border_width = theme->osd_border_width,
|
.border_width = theme->osd_border_width,
|
||||||
.bg_color = theme->osd_bg_color,
|
.bg_color = theme->osd_bg_color,
|
||||||
.width = nr_cols * switcher_theme->item_width + 2 * padding,
|
.width = items_width + 2 * padding,
|
||||||
.height = nr_rows * switcher_theme->item_height + 2 * padding,
|
.height = items_height + 2 * padding,
|
||||||
};
|
};
|
||||||
struct lab_scene_rect *bg =
|
struct lab_scene_rect *bg =
|
||||||
lab_scene_rect_create(osd_output->tree, &bg_opts);
|
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)
|
cycle_osd_thumbnail_update(struct cycle_osd_output *osd_output)
|
||||||
{
|
{
|
||||||
struct server *server = osd_output->output->server;
|
struct server *server = osd_output->output->server;
|
||||||
|
cycle_osd_scroll_update(osd_output);
|
||||||
|
|
||||||
struct cycle_osd_thumbnail_item *item;
|
struct cycle_osd_thumbnail_item *item;
|
||||||
wl_list_for_each(item, &osd_output->items, base.link) {
|
wl_list_for_each(item, &osd_output->items, base.link) {
|
||||||
bool active = (item->base.view == server->cycle.selected_view);
|
bool active = (item->base.view == server->cycle.selected_view);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue