feat(bar): 实现 bar 布局排版和渲染绘制流程

This commit is contained in:
2026-06-09 20:01:11 +08:00
parent fa2f058032
commit 4535e171a0
13 changed files with 318 additions and 109 deletions

View File

@@ -68,16 +68,10 @@ typedef struct zdwm_bar_item_type_t {
void *state
);
void (*destroy_state)(void *state);
zdwm_bar_item_t *instance;
uint32_t update_interval_ms;
} zdwm_bar_item_type_t;
typedef enum zdwm_bar_side_type_t {
ZDWM_BAR_SIDE_LEFT,
ZDWM_BAR_SIDE_RIGHT,
ZDWM_BAR_SIDE_CENTER_REST, /* 中央剩余部分,用于显示窗口列表 */
ZDWM_BAR_SIDE_COUNT,
} zdwm_bar_side_type_t;
#if defined(__cplusplus)
}
#endif

View File

@@ -755,3 +755,5 @@ backend_bar_window_t backend_create_bar_window(
return (backend_bar_window_t){.window_id = window_id, .cr = cr};
}
void backend_flush(backend_t *backend) { xcb_flush(backend->conn); }

View File

@@ -1,29 +1,32 @@
#include "bar/bar.h"
#include <assert.h>
#include <cairo.h>
#include <stddef.h>
#include <stdint.h>
#include <zdwm/bar.h>
#include <zdwm/types.h>
#include "bar/cell.h"
#include "bar/text.h"
#include "bar/types.h"
#include "bar/workspaces.h"
#include "base/array.h"
#include "base/macros.h"
#include "base/memory.h"
#include "core/listeners.h"
static zdwm_bar_item_t *bar_add_item(
bar_output_t *bar_output,
zdwm_bar_side_type_t side_type,
zdwm_output_id_t output_id,
bar_side_t *side,
zdwm_bar_item_type_t item_type,
void *config
) {
auto side = &bar_output->sides[side_type];
auto item = array_push(side->items, side->count, side->capacity);
item->api = item_type;
auto create_state = item_type.create_state;
if (create_state) item->state = create_state(bar_output->output_id, config);
if (create_state) item->state = create_state(output_id, config);
return item;
}
@@ -46,10 +49,9 @@ static void bar_item_cleanup(zdwm_bar_item_t *item) {
void bar_output_add_workspace(
bar_output_t *bar_output,
zdwm_bar_config_t *config
zdwm_bar_config_t *config,
listeners_t *listeners
) {
auto side = ZDWM_BAR_SIDE_LEFT;
bar_workspace_config_t workspace_config = {
.cell_padding = VALUE(config->tag_cell_padding_x, config->padding_x),
.indicator_width = VALUE(config->tag_indicator_width, 4),
@@ -60,18 +62,27 @@ void bar_output_add_workspace(
.urgent_bg = config->tag_urgent_bg,
.urgent_fg = config->tag_urgent_fg,
};
auto item = bar_add_item(bar_output, side, bar_workspace, &workspace_config);
auto item = bar_add_item(
bar_output->output_id,
&bar_output->left,
bar_workspace,
&workspace_config
);
item->cell_padding = workspace_config.cell_padding;
item->indicator_width = workspace_config.indicator_width;
bar_workspace_add_listeners(listeners, item->state);
}
void bar_init(bar_t *bar) {
void bar_init(bar_t *bar, listeners_t *listeners) {
auto c = &bar->config;
bar->ctx = text_context_create(c->font_family, c->font_size, c->dpi);
for (size_t i = 0; i < bar->count; ++i) {
auto bar_output = &bar->bars[i];
bar_output_add_workspace(bar_output, &bar->config);
bar_output->height = bar->config.height;
bar_output_add_workspace(bar_output, &bar->config, listeners);
}
}
@@ -88,25 +99,170 @@ void bar_cleanup(bar_t *bar) {
for (size_t i = 0; i < bar->count; ++i) {
auto bar_output = &bar->bars[i];
for (size_t j = 0; j < ZDWM_BAR_SIDE_COUNT; ++j) {
auto side = &bar_output->sides[j];
bar_side_cleanup(side);
}
bar_side_cleanup(&bar_output->left);
bar_side_cleanup(&bar_output->right);
bar_item_cleanup(&bar_output->center);
cairo_destroy(bar_output->cr);
bar_output->cr = nullptr;
}
p_delete(&bar->bars);
bar->count = 0;
bar_cell_clean_color_cache();
}
static void bar_draw_item(
zdwm_bar_item_t *item,
text_context_t *context,
static inline void bar_item_update(zdwm_bar_item_t *item) {
auto update = item->api.update;
if (update) update(item, &bar_cell_api, item->state);
}
static inline void bar_side_update(bar_side_t *side) {
for (size_t i = 0; i < side->count; ++i) {
bar_item_update(&side->items[i]);
}
}
void bar_update(bar_t *bar) {
for (size_t i = 0; i < bar->count; ++i) {
auto bar_output = &bar->bars[i];
bar_side_update(&bar_output->left);
bar_side_update(&bar_output->right);
bar_item_update(&bar_output->center);
}
}
static void bar_output_layout(bar_output_t *bar_output, text_context_t *ctx) {
int32_t start = 0;
auto end = bar_output->width;
auto left = &bar_output->left;
for (size_t i = 0; i < left->count; ++i) {
auto item = &left->items[i];
item->region.start = start;
for (size_t j = 0; j < item->count; ++j) {
auto cell = &item->cells[j];
int32_t width = 0;
text_context_get_text_size(ctx, cell->text, &width, nullptr);
bar_x_region_t region = {
.start = start,
.end = start + width + item->cell_padding * 2,
};
bar_cell_set_region(item, j, region);
start = region.end;
}
item->region.end = start;
}
auto right = &bar_output->right;
for (size_t i = right->count; i > 0; --i) {
auto item_index = i - 1;
auto item = &right->items[item_index];
item->region.end = end;
for (size_t j = item->count; j > 0; --j) {
auto cell_index = j - 1;
auto cell = &item->cells[cell_index];
int32_t width = 0;
text_context_get_text_size(ctx, cell->text, &width, nullptr);
bar_x_region_t region = {
.start = end - width - item->cell_padding * 2,
.end = end,
};
bar_cell_set_region(item, cell_index, region);
end = region.start;
}
item->region.start = end;
}
auto center = &bar_output->center;
if (center->count == 0) return;
center->region = (bar_x_region_t){.start = start, .end = end};
auto width = (end - start) / (int32_t)center->count;
for (size_t i = 0; i < center->count; ++i) {
bar_x_region_t region = {
.start = start,
.end = start + width + center->cell_padding * 2,
};
bar_cell_set_region(center, i, region);
}
}
static void bar_item_draw(
cairo_t *cr,
int32_t width,
zdwm_bar_item_t *item,
text_context_t *ctx,
int32_t height
) {
/* TODO: */
for (size_t i = 0; i < item->count; ++i) {
auto cell = &item->cells[i];
auto region = &cell->region;
zdwm_rect_t cell_area = {
.x = region->start,
.y = 0,
.width = region->end - region->start,
.height = height,
};
if (cell_area.width <= 0) {
cell->dirty = false;
continue;
}
draw_background(cr, cell->bg, cell_area);
if (cell->show_indicator) {
auto size = MIN(item->indicator_width, cell_area.width);
size = MIN(size, height);
if (size > 0) {
zdwm_rect_t indicator_area = {
.x = region->start,
.y = 0,
.width = size,
.height = size,
};
draw_background(cr, cell->fg, indicator_area);
}
}
zdwm_rect_t text_area = {
.x = cell_area.x + item->cell_padding,
.y = 0,
.width = cell_area.width - item->cell_padding * 2,
.height = height,
};
if (text_area.width > 0) {
draw_text(cr, ctx, cell->text, cell->fg, text_area);
}
cell->dirty = false;
}
item->dirty = false;
}
static void bar_output_draw(bar_output_t *bar_output, text_context_t *ctx) {
auto cr = bar_output->cr;
auto left = &bar_output->left;
for (size_t i = 0; i < left->count; ++i) {
bar_item_draw(cr, &left->items[i], ctx, bar_output->height);
}
auto right = &bar_output->right;
for (size_t i = 0; i < right->count; ++i) {
bar_item_draw(cr, &right->items[i], ctx, bar_output->height);
}
bar_item_draw(cr, &bar_output->center, ctx, bar_output->height);
}
void bar_draw(bar_t *bar) {
@@ -114,16 +270,7 @@ void bar_draw(bar_t *bar) {
for (size_t i = 0; i < bar->count; ++i) {
auto bar_output = &bar->bars[i];
auto cr = bar_output->cr;
auto width = bar_output->width;
auto height = bar_output->height;
for (size_t j = 0; j < ZDWM_BAR_SIDE_COUNT; ++j) {
auto side = &bar_output->sides[j];
for (size_t k = 0; k < side->count; ++k) {
auto item = &side->items[k];
bar_draw_item(item, ctx, cr, width, height);
}
}
bar_output_layout(bar_output, ctx);
bar_output_draw(bar_output, ctx);
}
}

View File

@@ -9,6 +9,7 @@
#include "bar/text.h"
#include "bar/types.h"
#include "base/color.h"
#include "core/listeners.h"
typedef struct bar_output_t {
cairo_t *cr;
@@ -16,7 +17,9 @@ typedef struct bar_output_t {
zdwm_output_id_t output_id;
int32_t width;
int32_t height;
bar_side_t sides[ZDWM_BAR_SIDE_COUNT];
bar_side_t left;
bar_side_t right;
zdwm_bar_item_t center;
} bar_output_t;
typedef struct bar_palette_t {
@@ -34,6 +37,7 @@ typedef struct bar_t {
text_context_t *ctx;
} bar_t;
void bar_init(bar_t *bar);
void bar_init(bar_t *bar, listeners_t *listeners);
void bar_cleanup(bar_t *bar);
void bar_update(bar_t *bar);
void bar_draw(bar_t *bar);

View File

@@ -53,18 +53,21 @@ void bar_cell_clean_color_cache(void) {
cache.capacity = 0;
}
size_t bar_cell_get_count(zdwm_bar_item_t *item) { return item->count; }
static size_t bar_cell_get_count(zdwm_bar_item_t *item) { return item->count; }
void bar_cell_set_count(zdwm_bar_item_t *item, size_t count) {
static void bar_cell_set_count(zdwm_bar_item_t *item, size_t count) {
if (item->count == count) return;
for (size_t i = 0; i < item->count; ++i) p_delete(&item->cells[i].text);
item->count = count;
p_realloc(&item->cells, count);
p_clear(item->cells, count);
item->dirty = true;
}
void bar_cell_set_text(zdwm_bar_item_t *item, size_t index, const char *text) {
static void
bar_cell_set_text(zdwm_bar_item_t *item, size_t index, const char *text) {
if (index >= item->count) return;
auto cell = &item->cells[index];
@@ -78,7 +81,8 @@ void bar_cell_set_text(zdwm_bar_item_t *item, size_t index, const char *text) {
item->dirty = true;
}
void bar_cell_set_bg(zdwm_bar_item_t *item, size_t index, const char *color) {
static void
bar_cell_set_bg(zdwm_bar_item_t *item, size_t index, const char *color) {
if (index >= item->count) return;
auto cell = &item->cells[index];
@@ -91,7 +95,8 @@ void bar_cell_set_bg(zdwm_bar_item_t *item, size_t index, const char *color) {
item->dirty = true;
}
void bar_cell_set_fg(zdwm_bar_item_t *item, size_t index, const char *color) {
static void
bar_cell_set_fg(zdwm_bar_item_t *item, size_t index, const char *color) {
if (index >= item->count) return;
auto cell = &item->cells[index];
@@ -104,7 +109,8 @@ void bar_cell_set_fg(zdwm_bar_item_t *item, size_t index, const char *color) {
item->dirty = true;
}
void bar_cell_set_icon(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon) {
static void
bar_cell_set_icon(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon) {
if (index >= item->count) return;
auto cell = &item->cells[index];
@@ -131,7 +137,8 @@ void bar_cell_set_icon(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon) {
item->dirty = true;
}
void bar_cell_set_indicator(zdwm_bar_item_t *item, size_t index, bool show) {
static void
bar_cell_set_indicator(zdwm_bar_item_t *item, size_t index, bool show) {
if (index >= item->count) return;
auto cell = &item->cells[index];
@@ -142,3 +149,31 @@ void bar_cell_set_indicator(zdwm_bar_item_t *item, size_t index, bool show) {
cell->dirty = true;
item->dirty = true;
}
zdwm_bar_cell_api_t bar_cell_api = {
.get_cell_count = bar_cell_get_count,
.set_cell_count = bar_cell_set_count,
.cell_set_text = bar_cell_set_text,
.cell_set_bg = bar_cell_set_bg,
.cell_set_fg = bar_cell_set_fg,
.cell_set_icon = bar_cell_set_icon,
.cell_set_indicator = bar_cell_set_indicator,
};
void bar_cell_set_region(
zdwm_bar_item_t *item,
size_t index,
bar_x_region_t region
) {
if (index >= item->count) return;
auto cell = &item->cells[index];
auto r = &cell->region;
if (r->start == region.start && r->end == region.end) return;
*r = region;
cell->dirty = true;
item->dirty = true;
}

View File

@@ -4,11 +4,15 @@
#include <zdwm/bar.h>
#include <zdwm/types.h>
size_t bar_cell_get_count(zdwm_bar_item_t *item);
void bar_cell_set_count(zdwm_bar_item_t *item, size_t count);
#include "bar/types.h"
void bar_cell_set_text(zdwm_bar_item_t *item, size_t index, const char *text);
void bar_cell_set_bg(zdwm_bar_item_t *item, size_t index, const char *color);
void bar_cell_set_fg(zdwm_bar_item_t *item, size_t index, const char *color);
void bar_cell_set_icon(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon);
void bar_cell_set_indicator(zdwm_bar_item_t *item, size_t index, bool show);
void bar_cell_reset_color_cache(void);
void bar_cell_clean_color_cache(void);
void bar_cell_set_region(
zdwm_bar_item_t *item,
size_t index,
bar_x_region_t region
);
extern zdwm_bar_cell_api_t bar_cell_api;

View File

@@ -98,7 +98,7 @@ void draw_text(
cairo_t *cr,
text_context_t *context,
const char *text,
color_t *color,
const color_t *color,
zdwm_rect_t area
) {
assert(cr);
@@ -127,7 +127,7 @@ void draw_text(
pango_cairo_show_layout(cr, layout);
}
void draw_background(cairo_t *cr, color_t *color, zdwm_rect_t area) {
void draw_background(cairo_t *cr, const color_t *color, zdwm_rect_t area) {
cairo_move_to(cr, area.x, area.y);
cairo_set_source_rgba(
cr,

View File

@@ -21,7 +21,7 @@ void draw_text(
cairo_t *cr,
text_context_t *context,
const char *text,
color_t *color,
const color_t *color,
zdwm_rect_t area
);
void draw_background(cairo_t *cr, color_t *color, zdwm_rect_t area);
void draw_background(cairo_t *cr, const color_t *color, zdwm_rect_t area);

View File

@@ -26,8 +26,8 @@ struct zdwm_bar_item_t {
bar_cell_t *cells;
size_t count;
bar_x_region_t region;
uint32_t cell_padding;
uint32_t indicator_width;
int32_t cell_padding;
int32_t indicator_width;
void *state;
zdwm_bar_item_type_t api;
@@ -35,7 +35,6 @@ struct zdwm_bar_item_t {
};
typedef struct bar_side_t {
zdwm_bar_side_type_t type;
zdwm_bar_item_t *items;
size_t count;
size_t capacity;

View File

@@ -85,9 +85,10 @@ bar_workspaces_create_state(zdwm_output_id_t output_id, void *config) {
const bar_workspace_config_t *workspace_config = config;
assert(workspace_config);
auto state = p_new(bar_workspace_state_t, 1);
state->output_id = output_id;
state->config = *workspace_config;
auto state = p_new(bar_workspace_state_t, 1);
state->output_id = output_id;
state->workspace_id = ZDWM_WORKSPACE_ID_INVALID;
state->config = *workspace_config;
return state;
}
@@ -171,6 +172,11 @@ zdwm_bar_item_type_t bar_workspace = {
.update_interval_ms = 0,
};
typedef struct bar_workspace_listener_user_data_t {
zdwm_bar_item_t *bar;
bar_workspace_state_t *state;
} bar_workspace_listener_user_data_t;
static void bar_workspace_list_filter(
const zdwm_workspace_t *list,
size_t count,

View File

@@ -178,6 +178,15 @@ bool config_defaults_build(
.dpi = 144,
.bg = "#222222",
.tag_cell_padding_x = 10,
.tag_indicator_width = 4,
.tag_bg = "#222222",
.tag_fg = "#bbbbbb",
.tag_active_bg = "#005577",
.tag_active_fg = "#eeeeee",
.tag_urgent_bg = "#222222",
.tag_urgent_fg = "#ff0000",
};
api->set_bar_config(builder, bar_config);

View File

@@ -63,3 +63,4 @@ backend_bar_window_t backend_create_bar_window(
rect_t geometry,
uint32_t bg_pixel
);
void backend_flush(backend_t *backend);

View File

@@ -77,6 +77,50 @@ static bool runtime_init_desc_valid(const runtime_init_desc_t *desc) {
return true;
}
static void runtime_init_bar(runtime_t *runtime) {
auto bar_height = runtime->bar.config.height;
if (bar_height <= 0) return;
auto show_top = runtime->bar.config.show_top;
inset_t inset = {
.top = show_top ? bar_height : 0,
.bottom = show_top ? 0 : bar_height,
};
auto state = &runtime->state;
auto count = state_output_count(state);
auto bar = &runtime->bar;
bar->bars = p_new(bar_output_t, count);
bar->count = count;
bar->visible = true;
color_parse(bar->config.bg, &bar->palette.bg);
auto backend = runtime->backend;
for (size_t i = 0; i < count; ++i) {
auto output = state_output_at(state, i);
state_output_inset_workarea(state, output->id, inset);
auto geometry = output->geometry;
rect_t bar_rect = {
.x = geometry.x,
.y = show_top ? 0 : geometry.y + geometry.height - bar_height,
.width = geometry.width,
.height = bar_height,
};
auto color = bar->palette.bg.argb;
auto bar_window = backend_create_bar_window(backend, bar_rect, color);
auto bar_output = &bar->bars[i];
bar_output->cr = bar_window.cr;
bar_output->window_id = bar_window.window_id;
bar_output->output_id = output->id;
bar_output->width = bar_rect.width;
bar_output->height = bar_rect.height;
}
bar_init(&runtime->bar, &runtime->listeners);
}
static bool runtime_init(runtime_t *runtime, runtime_init_desc_t *desc) {
if (!runtime || !runtime_init_desc_valid(desc)) return false;
@@ -189,51 +233,7 @@ static void runtime_notify_initial_state(runtime_t *runtime) {
listeners_notify_binding_mode(listeners, runtime->binding_table);
}
static void runtime_init_bar(runtime_t *runtime) {
auto bar_height = runtime->bar.config.height;
if (bar_height <= 0) return;
auto show_top = runtime->bar.config.show_top;
inset_t inset = {
.top = show_top ? bar_height : 0,
.bottom = show_top ? 0 : bar_height,
};
auto state = &runtime->state;
auto count = state_output_count(state);
auto bar = &runtime->bar;
bar->bars = p_new(bar_output_t, count);
bar->count = count;
bar->visible = true;
color_parse(bar->config.bg, &bar->palette.bg);
auto backend = runtime->backend;
for (size_t i = 0; i < count; ++i) {
auto output = state_output_at(state, i);
state_output_inset_workarea(state, output->id, inset);
auto geometry = output->geometry;
rect_t bar_rect = {
.x = geometry.x,
.y = show_top ? 0 : geometry.y + geometry.height - bar_height,
.width = geometry.width,
.height = bar_height,
};
auto color = bar->palette.bg.argb;
auto bar_window = backend_create_bar_window(backend, bar_rect, color);
auto bar_output = &bar->bars[i];
bar_output->cr = bar_window.cr;
bar_output->window_id = bar_window.window_id;
bar_output->output_id = output->id;
bar_output->width = bar_rect.width;
bar_output->height = bar_rect.height;
}
bar_init(&runtime->bar);
}
static void runtime_setup(runtime_t *runtime) {
static void runtime_setup_bindings(runtime_t *runtime) {
size_t bind_count = 0;
auto bindings =
binding_table_get_current_bindings(runtime->binding_table, &bind_count);
@@ -257,9 +257,13 @@ static void runtime_setup(runtime_t *runtime) {
backend_apply_effect(backend, plan->effects, plan->count);
plan_reset(plan);
runtime_notify_initial_state(runtime);
}
static void runtime_setup(runtime_t *runtime) {
runtime_setup_bindings(runtime);
runtime_init_bar(runtime);
runtime_notify_initial_state(runtime);
}
static const layout_result_t *runtime_layout_calc(runtime_t *runtime) {
@@ -448,6 +452,10 @@ static void runtime_run_event_loop(runtime_t *runtime) {
if (plan->quit) {
runtime->running = false;
runtime->will_restart = plan->will_restart;
} else {
bar_update(&runtime->bar);
bar_draw(&runtime->bar);
backend_flush(backend);
}
event_cleanup(&event);