From 83e5fafd65e892bcc20d074860386ed4b630aff7 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 16 Jun 2026 23:00:24 +0800 Subject: [PATCH 01/13] feat: add overview jump mode --- docs/bindings/keys.md | 1 + docs/installation.md | 1 + docs/visuals/theming.md | 12 ++ mangowm.scm | 1 + meson.build | 3 + nix/default.nix | 2 + src/config/parse_config.h | 82 +++++++++- src/dispatch/bind_declare.h | 1 + src/dispatch/bind_define.h | 23 +++ src/draw/text-node.c | 308 ++++++++++++++++++++++++++++++++++++ src/draw/text-node.h | 52 ++++++ src/layout/overview.h | 55 +++++++ src/mango.c | 35 ++++ 13 files changed, 575 insertions(+), 1 deletion(-) create mode 100644 src/draw/text-node.c create mode 100644 src/draw/text-node.h diff --git a/docs/bindings/keys.md b/docs/bindings/keys.md index be8757c2..a7694d74 100644 --- a/docs/bindings/keys.md +++ b/docs/bindings/keys.md @@ -161,6 +161,7 @@ bindr=Super,Super_L,spawn,rofi -show run | `reload_config` | - | Hot-reload configuration. | | `quit` | - | Exit mangowm. | | `toggleoverview` | - | Toggle overview mode. | +| `togglejump` | - | Toggle overview with jump mode. | | `create_virtual_output` | - | Create a headless monitor (for VNC/Sunshine). | | `destroy_all_virtual_output` | - | Destroy all virtual monitors. | | `toggleoverlay` | - | Toggle overlay state for the focused window. | diff --git a/docs/installation.md b/docs/installation.md index 1fed83d8..8cb7eafe 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -274,6 +274,7 @@ If your distribution isn't listed above, or you want the latest unreleased chang > - `hwdata` > - `seatd` > - `pcre2` +> - `pango` > - `cjson` > - `pixman` > - `xorg-xwayland` diff --git a/docs/visuals/theming.md b/docs/visuals/theming.md index 676c575b..71d3a818 100644 --- a/docs/visuals/theming.md +++ b/docs/visuals/theming.md @@ -52,6 +52,18 @@ You can also color-code windows based on their state: > **Tip:** For scratchpad window sizing, see [Scratchpad](/docs/window-management/scratchpad) configuration. +### Overview Jump Mode +| Setting | Default | Description | +| :--- | :--- | :--- | +| `jump_hit_fg_color` | `0xc4939dff` | label text color. | +| `jump_hit_bg_color` | `0x201b14ff` | label background color. +| `jump_hit_border_color` | `0x8BAA9Bff` | label border color. +| `jump_hit_border_width` | `4` | label border width. +| `jump_hit_corner_radius` | `5` | label corner radius. +| `jump_hit_padding_x` | `10` | label horizontal padding. +| `jump_hit_padding_y` | `10` | label vertical padding. +| `jump_hit_font_desc` | `monospace Bold 24` | label font set.| + ## Cursor Theme Set the size and theme of your mouse cursor. diff --git a/mangowm.scm b/mangowm.scm index fcd7f583..e2b0287a 100644 --- a/mangowm.scm +++ b/mangowm.scm @@ -54,6 +54,7 @@ hwdata seatd pcre2 + pango cjson libxcb pixman diff --git a/meson.build b/meson.build index c68b5b13..53201a28 100644 --- a/meson.build +++ b/meson.build @@ -36,6 +36,7 @@ libwayland_client_dep = dependency('wayland-client') pcre2_dep = dependency('libpcre2-8') pixman_dep = dependency('pixman-1') cjson_dep = dependency('libcjson') +pangocairo_dep = dependency('pangocairo') # version info git = find_program('git', required : false) @@ -93,6 +94,7 @@ endif executable('mango', 'src/mango.c', 'src/common/util.c', + 'src/draw/text-node.c', wayland_sources, dependencies : [ libm, @@ -106,6 +108,7 @@ executable('mango', pcre2_dep, pixman_dep, cjson_dep, + pangocairo_dep, ], install : true, c_args : c_args, diff --git a/nix/default.nix b/nix/default.nix index d7bcab16..cbfc917a 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -5,6 +5,7 @@ libxcb, libxkbcommon, pcre2, + pango, cjson, pixman, pkg-config, @@ -49,6 +50,7 @@ stdenv.mkDerivation { libxcb libxkbcommon pcre2 + pango cjson pixman wayland diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 8c86525c..e01d750c 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -256,6 +256,7 @@ typedef struct { int32_t enable_hotarea; int32_t ov_tab_mode; int32_t ov_no_resize; + int32_t overviewgappi; int32_t overviewgappo; uint32_t cursor_hide_timeout; @@ -386,6 +387,7 @@ typedef struct { struct xkb_context *ctx; struct xkb_keymap *keymap; + JumphitData jumhitdata; } Config; typedef int32_t (*FuncType)(const Arg *); @@ -1009,6 +1011,9 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "toggleoverview") == 0) { func = toggleoverview; (*arg).i = atoi(arg_value); + } else if (strcmp(func_name, "togglejump") == 0) { + func = togglejump; + (*arg).i = atoi(arg_value); } else if (strcmp(func_name, "set_proportion") == 0) { func = set_proportion; (*arg).f = atof(arg_value); @@ -1701,6 +1706,50 @@ bool parse_option(Config *config, char *key, char *value) { config->cursor_size = atoi(value); } else if (strcmp(key, "cursor_theme") == 0) { config->cursor_theme = strdup(value); + } else if (strcmp(key, "jump_hit_fg_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_fg_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumhitdata.fg_color, color); + } + } else if (strcmp(key, "jump_hit_font_desc") == 0) { + config->jumhitdata.font_desc = strdup(value); + } else if (strcmp(key, "jump_hit_bg_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_bg_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumhitdata.bg_color, color); + } + } else if (strcmp(key, "jump_hit_border_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf( + stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_border_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumhitdata.border_color, color); + } + } else if (strcmp(key, "jump_hit_border_width") == 0) { + config->jumhitdata.border_width = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "jump_hit_corner_radius") == 0) { + config->jumhitdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "jump_hit_padding_x") == 0) { + config->jumhitdata.padding_x = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "jump_hit_padding_y") == 0) { + config->jumhitdata.padding_y = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "disable_while_typing") == 0) { config->disable_while_typing = atoi(value); } else if (strcmp(key, "left_handed") == 0) { @@ -3160,6 +3209,11 @@ void free_config(void) { config.cursor_theme = NULL; } + if (config.jumhitdata.font_desc) { + free((void *)config.jumhitdata.font_desc); + config.jumhitdata.font_desc = NULL; + } + if (config.tablet_map_to_mon) { free(config.tablet_map_to_mon); config.tablet_map_to_mon = NULL; @@ -3325,6 +3379,15 @@ void override_config(void) { config.focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f); config.unfocused_opacity = CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f); + + config.jumhitdata.border_width = + CLAMP_INT(config.jumhitdata.border_width, 0, 100); + config.jumhitdata.corner_radius = + CLAMP_INT(config.jumhitdata.corner_radius, 0, 100); + config.jumhitdata.padding_x = + CLAMP_INT(config.jumhitdata.padding_x, 0, 100); + config.jumhitdata.padding_y = + CLAMP_INT(config.jumhitdata.padding_y, 0, 100); } void set_value_default() { @@ -3363,7 +3426,6 @@ void set_value_default() { config.log_level = WLR_ERROR; config.numlockon = 0; config.capslock = 0; - config.ov_tab_mode = 1; config.ov_no_resize = 1; config.hotarea_size = 10; @@ -3477,6 +3539,23 @@ void set_value_default() { config.animation_curve_opafadeout[2] = 0.5; config.animation_curve_opafadeout[3] = 0.5; + config.jumhitdata.fg_color[0] = 0xc4 / 255.0f; + config.jumhitdata.fg_color[1] = 0x93 / 255.0f; + config.jumhitdata.fg_color[2] = 0x9d / 255.0f; + config.jumhitdata.fg_color[3] = 1.0f; + config.jumhitdata.bg_color[0] = 0x32 / 255.0f; + config.jumhitdata.bg_color[1] = 0x32 / 255.0f; + config.jumhitdata.bg_color[2] = 0x32 / 255.0f; + config.jumhitdata.bg_color[3] = 1.0f; + config.jumhitdata.border_color[0] = 0x8b / 255.0f; + config.jumhitdata.border_color[1] = 0xaa / 255.0f; + config.jumhitdata.border_color[2] = 0x9b / 255.0f; + config.jumhitdata.border_color[3] = 1.0f; + config.jumhitdata.border_width = 4; + config.jumhitdata.corner_radius = 5; + config.jumhitdata.padding_x = 10; + config.jumhitdata.padding_y = 10; + config.rootcolor[0] = 0x32 / 255.0f; config.rootcolor[1] = 0x32 / 255.0f; config.rootcolor[2] = 0x32 / 255.0f; @@ -3589,6 +3668,7 @@ bool parse_config(void) { config.tag_rules = NULL; config.tag_rules_count = 0; config.cursor_theme = NULL; + config.jumhitdata.font_desc = NULL; config.tablet_map_to_mon = NULL; strcpy(config.keymode, "default"); diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index a04ba0c1..6960b001 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -3,6 +3,7 @@ int32_t restore_minimized(const Arg *arg); int32_t toggle_scratchpad(const Arg *arg); int32_t focusdir(const Arg *arg); int32_t toggleoverview(const Arg *arg); +int32_t togglejump(const Arg *arg); int32_t set_proportion(const Arg *arg); int32_t switch_proportion_preset(const Arg *arg); int32_t zoom(const Arg *arg); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index de51c0a0..8602d244 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1738,6 +1738,10 @@ int32_t toggleoverview(const Arg *arg) { uint32_t target; uint32_t visible_client_number = 0; + if (!selmon->isoverview && selmon->is_jump_mode) { + finish_jump_mode(selmon); + } + if (selmon->isoverview) { wl_list_for_each(c, &clients, link) if (c && c->mon == selmon && !client_is_unmanaged(c) && @@ -1782,6 +1786,7 @@ int32_t toggleoverview(const Arg *arg) { } } } else { + selmon->tagset[selmon->seltags] = target; wl_list_for_each(c, &clients, link) { if (c && c->mon == selmon && !c->iskilling && @@ -1795,6 +1800,24 @@ int32_t toggleoverview(const Arg *arg) { view(&(Arg){.ui = target}, false); fix_mon_tagset_from_overview(selmon); refresh_monitors_workspaces_status(selmon); + + return 0; +} + +int32_t togglejump(const Arg *arg) { + if (!selmon) + return 0; + + if (!selmon->isoverview) { + begin_jump_mode(selmon); + toggleoverview(arg); + return 0; + } + + if (selmon->isoverview) { + toggleoverview(arg); + } + return 0; } diff --git a/src/draw/text-node.c b/src/draw/text-node.c new file mode 100644 index 00000000..b96cd4ac --- /dev/null +++ b/src/draw/text-node.c @@ -0,0 +1,308 @@ +#include "text-node.h" +#include +#include +#include +#include +#include +#include +#include + +// 自定义 wlr_buffer,用于将 Cairo Surface 包装并注入 wlr_scene +struct mango_text_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; +}; + +// 全局字体描述缓存 +static GHashTable *font_desc_cache = NULL; + +static PangoFontDescription *get_cached_font_desc(const char *font_desc) { + if (!font_desc_cache) { + font_desc_cache = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)pango_font_description_free); + } + + PangoFontDescription *desc = + g_hash_table_lookup(font_desc_cache, font_desc); + if (!desc) { + desc = pango_font_description_from_string(font_desc); + g_hash_table_insert(font_desc_cache, g_strdup(font_desc), desc); + } + return desc; +} + +void mango_text_global_finish(void) { + if (font_desc_cache) { + g_hash_table_destroy(font_desc_cache); + font_desc_cache = NULL; + } +} + +// wlr_buffer 实现 +static void text_buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base); + cairo_surface_destroy(buf->surface); + free(buf); +} + +static bool text_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, void **data, + uint32_t *format, + size_t *stride) { + (void)flags; + struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base); + *data = cairo_image_surface_get_data(buf->surface); + *format = DRM_FORMAT_ARGB8888; + *stride = cairo_image_surface_get_stride(buf->surface); + return true; +} + +static void text_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {} + +static const struct wlr_buffer_impl text_buffer_impl = { + .destroy = text_buffer_destroy, + .begin_data_ptr_access = text_buffer_begin_data_ptr_access, + .end_data_ptr_access = text_buffer_end_data_ptr_access, +}; + +struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, + JumphitData data) { + struct mango_text_node *node = calloc(1, sizeof(*node)); + if (!node) + return NULL; + + node->scene_buffer = wlr_scene_buffer_create(parent, NULL); + if (!node->scene_buffer) { + free(node); + return NULL; + } + + node->fg_color[0] = data.fg_color[0]; + node->fg_color[1] = data.fg_color[1]; + node->fg_color[2] = data.fg_color[2]; + node->fg_color[3] = data.fg_color[3]; + node->bg_color[0] = data.bg_color[0]; + node->bg_color[1] = data.bg_color[1]; + node->bg_color[2] = data.bg_color[2]; + node->bg_color[3] = data.bg_color[3]; + node->border_color[0] = data.border_color[0]; + node->border_color[1] = data.border_color[1]; + node->border_color[2] = data.border_color[2]; + node->border_color[3] = data.border_color[3]; + node->border_width = data.border_width; + node->corner_radius = data.corner_radius; + node->padding_x = data.padding_x; + node->padding_y = data.padding_y; + node->font_desc = data.font_desc ? data.font_desc : "monospace Bold 24"; + + return node; +} + +void mango_text_node_destroy(struct mango_text_node *node) { + if (!node) + return; + + wlr_scene_node_destroy(&node->scene_buffer->node); + free(node); +} + +void mango_text_node_set_background(struct mango_text_node *node, float r, + float g, float b, float a) { + if (!node) + return; + node->bg_color[0] = r; + node->bg_color[1] = g; + node->bg_color[2] = b; + node->bg_color[3] = a; +} + +void mango_text_node_set_border(struct mango_text_node *node, float r, float g, + float b, float a, float width, float radius) { + if (!node) + return; + node->border_color[0] = r; + node->border_color[1] = g; + node->border_color[2] = b; + node->border_color[3] = a; + node->border_width = width > 0.0f ? width : 0.0f; + node->corner_radius = radius >= 0.0f ? radius : 0.0f; +} + +void mango_text_node_set_padding(struct mango_text_node *node, float pad_x, + float pad_y) { + if (!node) + return; + node->padding_x = pad_x >= 0.0f ? pad_x : 0.0f; + node->padding_y = pad_y >= 0.0f ? pad_y : 0.0f; +} + +static void draw_rounded_rect(cairo_t *cr, double x, double y, double w, + double h, double r) { + // 使用 Cairo 的标准圆角矩形路径 + double degrees = G_PI / 180.0; + cairo_new_sub_path(cr); + cairo_arc(cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); + cairo_arc(cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); + cairo_arc(cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); + cairo_arc(cr, x + r, y + r, r, 180 * degrees, 270 * degrees); + cairo_close_path(cr); +} + +void mango_text_node_update(struct mango_text_node *node, const char *text, + + float scale) { + if (!node || !text) + return; + if (scale <= 0.0f) + scale = 1.0f; + + const char *font_desc = node->font_desc; + PangoFontDescription *desc = get_cached_font_desc(font_desc); + + // 测量文字像素尺寸(无缩放的实际像素) + cairo_surface_t *dummy_surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); + cairo_t *dummy_cr = cairo_create(dummy_surface); + PangoContext *dummy_ctx = pango_cairo_create_context(dummy_cr); + pango_cairo_context_set_resolution(dummy_ctx, 96.0 * scale); + PangoLayout *dummy_layout = pango_layout_new(dummy_ctx); + pango_layout_set_font_description(dummy_layout, desc); + pango_layout_set_text(dummy_layout, text, -1); + + int text_pixel_w, text_pixel_h; + pango_layout_get_pixel_size(dummy_layout, &text_pixel_w, &text_pixel_h); + + g_object_unref(dummy_layout); + g_object_unref(dummy_ctx); + cairo_destroy(dummy_cr); + cairo_surface_destroy(dummy_surface); + + if (text_pixel_w <= 0 || text_pixel_h <= 0) { + wlr_scene_buffer_set_buffer(node->scene_buffer, NULL); + node->logical_width = 0; + node->logical_height = 0; + return; + } + + float logical_text_w = text_pixel_w / scale; + float logical_text_h = text_pixel_h / scale; + + float pad_x = node->padding_x; + float pad_y = node->padding_y; + float box_logical_w = logical_text_w + 2.0f * pad_x; + float box_logical_h = logical_text_h + 2.0f * pad_y; + + float border = node->border_width; + bool draw_border = (border > 0.0f) && (node->border_color[3] > 0.0f); + bool draw_bg = (node->bg_color[3] > 0.0f); + + int surface_pixel_w = (int)((box_logical_w + 2.0f * border) * scale + 0.5f); + int surface_pixel_h = (int)((box_logical_h + 2.0f * border) * scale + 0.5f); + + if (surface_pixel_w < 1) + surface_pixel_w = 1; + if (surface_pixel_h < 1) + surface_pixel_h = 1; + + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, surface_pixel_w, surface_pixel_h); + cairo_t *cr = cairo_create(surface); + + // 清空为全透明 + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + double bg_x = border * scale; + double bg_y = border * scale; + double bg_w = box_logical_w * scale; + double bg_h = box_logical_h * scale; + + double radius = node->corner_radius * scale; + + if (radius < 0) { + radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0; + } + if (radius > bg_w / 2.0) + radius = bg_w / 2.0; + if (radius > bg_h / 2.0) + radius = bg_h / 2.0; + + if (draw_bg) { + cairo_set_source_rgba(cr, node->bg_color[0], node->bg_color[1], + node->bg_color[2], node->bg_color[3]); + if (radius > 0.0) { + draw_rounded_rect(cr, bg_x, bg_y, bg_w, bg_h, radius); + cairo_fill(cr); + } else { + cairo_rectangle(cr, bg_x, bg_y, bg_w, bg_h); + cairo_fill(cr); + } + } + + cairo_save(cr); + cairo_translate(cr, (border + pad_x) * scale, (border + pad_y) * scale); + + PangoContext *ctx = pango_cairo_create_context(cr); + pango_cairo_context_set_resolution(ctx, 96.0 * scale); + PangoLayout *layout = pango_layout_new(ctx); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, text, -1); + + cairo_set_source_rgba(cr, node->fg_color[0], node->fg_color[1], + node->fg_color[2], node->fg_color[3]); + pango_cairo_show_layout(cr, layout); + + g_object_unref(layout); + g_object_unref(ctx); + cairo_restore(cr); + + if (draw_border) { + cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1], + node->border_color[2], node->border_color[3]); + cairo_set_line_width(cr, border * scale); + + double half_lw = border * scale * 0.5; + double bx = bg_x - half_lw; + double by = bg_y - half_lw; + double bw = bg_w + border * scale; + double bh = bg_h + border * scale; + + if (radius > 0.0) { + double outer_radius = radius + half_lw; + if (outer_radius < 0.0) + outer_radius = 0.0; + draw_rounded_rect(cr, bx, by, bw, bh, outer_radius); + cairo_stroke(cr); + } else { + cairo_rectangle(cr, bx, by, bw, bh); + cairo_stroke(cr); + } + } + + cairo_surface_flush(surface); + + // 包装成 wlr_buffer + struct mango_text_buffer *buf = calloc(1, sizeof(*buf)); + if (!buf) { + cairo_surface_destroy(surface); + cairo_destroy(cr); + return; + } + wlr_buffer_init(&buf->base, &text_buffer_impl, surface_pixel_w, + surface_pixel_h); + buf->surface = surface; + + wlr_scene_buffer_set_buffer(node->scene_buffer, &buf->base); + wlr_buffer_drop(&buf->base); + + // 最终逻辑大小 = 背景框逻辑尺寸 + 边框逻辑尺寸 + node->logical_width = (int)(box_logical_w + 2.0f * border); + node->logical_height = (int)(box_logical_h + 2.0f * border); + wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width, + node->logical_height); + + cairo_destroy(cr); +} \ No newline at end of file diff --git a/src/draw/text-node.h b/src/draw/text-node.h new file mode 100644 index 00000000..b8c4900d --- /dev/null +++ b/src/draw/text-node.h @@ -0,0 +1,52 @@ +#ifndef MANGO_TEXT_NODE_H +#define MANGO_TEXT_NODE_H + +#include +#include + +struct mango_text_node { + struct wlr_scene_buffer *scene_buffer; + int logical_width; + int logical_height; + const char *font_desc; + + // 外观属性 + float fg_color[4]; // 文本颜色 RGBA,默认白色 + float bg_color[4]; // 背景色 RGBA,默认透明 + float border_color[4]; // 边框色 RGBA,默认透明 + int32_t border_width; // 边框宽度(逻辑像素),<=0 则不绘制 + int32_t corner_radius; // 圆角半径(逻辑像素),<0 时自动取 + // min(width,height)/2 形成胶囊 + int32_t padding_x; // 文本左右内边距(逻辑像素) + int32_t padding_y; // 文本上下内边距(逻辑像素) +}; + +typedef struct { + float fg_color[4]; + float bg_color[4]; + float border_color[4]; + int32_t border_width; + int32_t corner_radius; + int32_t padding_x; + int32_t padding_y; + const char *font_desc; +} JumphitData; + +struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, + JumphitData data); +void mango_text_node_destroy(struct mango_text_node *node); +void mango_text_node_update(struct mango_text_node *node, const char *text, + float scale); + +// 外观设置接口 +void mango_text_node_set_background(struct mango_text_node *node, float r, + float g, float b, float a); +void mango_text_node_set_border(struct mango_text_node *node, float r, float g, + float b, float a, float width, float radius); +void mango_text_node_set_padding(struct mango_text_node *node, float pad_x, + float pad_y); + +void mango_text_node_destroy(struct mango_text_node *node); +void mango_text_global_finish(void); + +#endif \ No newline at end of file diff --git a/src/layout/overview.h b/src/layout/overview.h index 88fc4258..06e2213f 100644 --- a/src/layout/overview.h +++ b/src/layout/overview.h @@ -353,10 +353,65 @@ void overview_resize(Monitor *m) { free(c_arr); } +void create_jump_hints(Monitor *m) { + const char jump_labels[] = "HJKLASDFGQWERTYUIOPZXCVBNM"; + int label_idx = 0; + Client *c; + + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m) && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + if (label_idx >= 26) + break; + char c_char = jump_labels[label_idx]; + c->jump_char = c_char; + + // 把字符变成字符串 + char label_text[2] = {c_char, '\0'}; + + mango_text_node_update(c->text_node, label_text, 1.0f); + wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, true); + wlr_scene_node_raise_to_top(&c->text_node->scene_buffer->node); + wlr_scene_node_set_position( + &c->text_node->scene_buffer->node, + c->geom.width / 2 - c->text_node->logical_width / 2, + c->geom.height / 2 - c->text_node->logical_height / 2); + label_idx++; + } + } +} + +void begin_jump_mode(Monitor *m) { m->is_jump_mode = 1; } + +void finish_jump_mode(Monitor *m) { + Client *c = NULL; + + if (!m->is_jump_mode) { + return; + } + + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m)) { + if (c->text_node->scene_buffer->node.enabled) { + c->jump_char = '\0'; + wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, + false); + } + } + } + + m->is_jump_mode = 0; +} + void overview(Monitor *m) { + if (config.ov_no_resize) { overview_scale(m); } else { overview_resize(m); } + + if (m->is_jump_mode) { + create_jump_hints(m); + } } \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index b5e0b3ea..8b9e375d 100644 --- a/src/mango.c +++ b/src/mango.c @@ -94,6 +94,7 @@ #include #endif #include "common/util.h" +#include "draw/text-node.h" /* macros */ #define MAX(A, B) ((A) > (B) ? (A) : (B)) @@ -329,6 +330,7 @@ struct Client { struct wlr_ext_image_capture_source_v1 *image_capture_source; struct wlr_scene_tree *overview_scene_surface; + struct mango_text_node *text_node; struct wl_list link; struct wl_list flink; struct wl_list fadeout_link; @@ -434,6 +436,7 @@ struct Client { int32_t allow_shortcuts_inhibit; float scroller_proportion_single; bool isfocusing; + char jump_char; bool enable_drop_area_draw; int32_t drop_direction; struct wlr_box drag_tile_float_backup_geom; @@ -552,6 +555,7 @@ struct Monitor { uint32_t ovbk_prev_tagset; Client *sel, *prevsel; int32_t isoverview; + int32_t is_jump_mode; int32_t is_in_hotarea; int32_t asleep; uint32_t visible_clients; @@ -911,6 +915,10 @@ Client *scroll_get_stack_tail_client(Client *c); static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c); static void overview_backup_surface(Client *c); +static void create_jump_hints(Monitor *m); +static void finish_jump_mode(Monitor *m); +static void begin_jump_mode(Monitor *m); + #include "data/static_keymap.h" #include "dispatch/bind_declare.h" #include "layout/layout.h" @@ -2581,6 +2589,8 @@ void cleanup(void) { /* Destroy after the wayland display (when the monitors are already destroyed) to avoid destroying them with an invalid scene output. */ wlr_scene_node_destroy(&scene->tree.node); + + mango_text_global_finish(); } void cleanupmon(struct wl_listener *listener, void *data) { @@ -4207,6 +4217,27 @@ void keypress(struct wl_listener *listener, void *data) { if (handled) return; + if (selmon && selmon->is_jump_mode && + event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + for (i = 0; i < nsyms; i++) { + xkb_keysym_t sym = xkb_keysym_to_lower(syms[i]); + if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) { + char c_char = 'A' + (sym - XKB_KEY_a); + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->mon == selmon && c->jump_char == c_char) { + focusclient(c, 1); + toggleoverview(&(Arg){.i = 1}); + return; + } + } + } else if (sym == XKB_KEY_Escape) { + togglejump(&(Arg){.i = 0}); + return; + } + } + } + /* don't pass when popup is focused * this is better than having popups (like fuzzel or wmenu) closing * while typing in a passed keybind */ @@ -4459,6 +4490,9 @@ mapnotify(struct wl_listener *listener, void *data) { : config.bordercolor); c->border[i]->node.data = c; } + c->text_node = mango_text_node_create(c->scene, config.jumhitdata); + wlr_scene_node_lower_to_bottom(&c->text_node->scene_buffer->node); + wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, false); for (i = 0; i < 2; i++) { c->splitindicator[i] = wlr_scene_rect_create( @@ -6534,6 +6568,7 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->stack_proportion = 0.0f; + mango_text_node_destroy(c->text_node); wlr_scene_node_destroy(&c->scene->node); printstatus(IPC_WATCH_ARRANGGE); motionnotify(0, NULL, 0, 0, 0, 0); From 1f4f2dd5f0ad8d8dc2c9bfe3f9486c0cb0e50228 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 17 Jun 2026 08:41:22 +0800 Subject: [PATCH 02/13] opt: Reduce unnecessary text drawing --- src/animation/client.h | 6 +- src/animation/layer.h | 4 +- src/animation/tag.h | 16 +-- src/dispatch/bind_define.h | 30 ++-- src/draw/text-node.c | 286 ++++++++++++++++++++++++------------- src/draw/text-node.h | 60 +++++--- src/layout/dwindle.h | 15 +- src/layout/horizontal.h | 10 +- src/layout/vertical.h | 6 +- src/mango.c | 18 +-- 10 files changed, 282 insertions(+), 169 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 91920c4b..e39a7d94 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -898,7 +898,7 @@ void fadeout_client_animation_next_tick(Client *c) { double percent = config.fadeout_begin_opacity - (opacity_eased_progress * config.fadeout_begin_opacity); - double opacity = MAX(percent, 0); + double opacity = MANGO_MAX(percent, 0); if (config.animation_fade_out && !c->nofadeout) wlr_scene_node_for_each_buffer(&c->scene->node, @@ -1177,8 +1177,8 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { if (is_scroller_layout(c->mon) && (!c->isfloating || c == grabc)) { c->geom = geo; - c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width); - c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height); + c->geom.width = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.width); + c->geom.height = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.height); } else { // 这里会限制不允许窗口划出屏幕 c->geom = geo; applybounds( diff --git a/src/animation/layer.h b/src/animation/layer.h index 414cf2bd..993cfd12 100644 --- a/src/animation/layer.h +++ b/src/animation/layer.h @@ -233,7 +233,7 @@ void fadeout_layer_animation_next_tick(LayerSurface *l) { double percent = config.fadeout_begin_opacity - (opacity_eased_progress * config.fadeout_begin_opacity); - double opacity = MAX(percent, 0.0f); + double opacity = MANGO_MAX(percent, 0.0f); if (config.animation_fade_out) wlr_scene_node_for_each_buffer(&l->scene->node, @@ -278,7 +278,7 @@ void layer_animation_next_tick(LayerSurface *l) { find_animation_curve_at(animation_passed, OPAFADEIN); double opacity = - MIN(config.fadein_begin_opacity + + MANGO_MIN(config.fadein_begin_opacity + opacity_eased_progress * (1.0 - config.fadein_begin_opacity), 1.0f); diff --git a/src/animation/tag.h b/src/animation/tag.h index 893d4b0b..23986a44 100644 --- a/src/animation/tag.h +++ b/src/animation/tag.h @@ -23,10 +23,10 @@ void set_tagin_animation(Monitor *m, Client *c) { c->animainit_geom.x = config.tag_animation_direction == VERTICAL ? c->animation.current.x - : MAX(c->mon->m.x + c->mon->m.width, + : MANGO_MAX(c->mon->m.x + c->mon->m.width, c->geom.x + c->mon->m.width); c->animainit_geom.y = config.tag_animation_direction == VERTICAL - ? MAX(c->mon->m.y + c->mon->m.height, + ? MANGO_MAX(c->mon->m.y + c->mon->m.height, c->geom.y + c->mon->m.height) : c->animation.current.y; @@ -35,10 +35,10 @@ void set_tagin_animation(Monitor *m, Client *c) { c->animainit_geom.x = config.tag_animation_direction == VERTICAL ? c->animation.current.x - : MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); + : MANGO_MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); c->animainit_geom.y = config.tag_animation_direction == VERTICAL - ? MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) + ? MANGO_MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) : c->animation.current.y; } } @@ -87,9 +87,9 @@ void set_tagout_animation(Monitor *m, Client *c) { c->pending.x = config.tag_animation_direction == VERTICAL ? c->animation.current.x - : MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); + : MANGO_MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); c->pending.y = config.tag_animation_direction == VERTICAL - ? MIN(c->mon->m.y - c->geom.height, + ? MANGO_MIN(c->mon->m.y - c->geom.height, c->geom.y - c->mon->m.height) : c->animation.current.y; @@ -98,10 +98,10 @@ void set_tagout_animation(Monitor *m, Client *c) { c->pending = c->geom; c->pending.x = config.tag_animation_direction == VERTICAL ? c->animation.current.x - : MAX(c->mon->m.x + c->mon->m.width, + : MANGO_MAX(c->mon->m.x + c->mon->m.width, c->geom.x + c->mon->m.width); c->pending.y = config.tag_animation_direction == VERTICAL - ? MAX(c->mon->m.y + c->mon->m.height, + ? MANGO_MAX(c->mon->m.y + c->mon->m.height, c->geom.y + c->mon->m.height) : c->animation.current.y; resize(c, c->geom, 0); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 8602d244..a199a92e 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -265,7 +265,7 @@ int32_t incnmaster(const Arg *arg) { if (!arg || !selmon) return 0; selmon->pertag->nmasters[selmon->pertag->curtag] = - MAX(selmon->pertag->nmasters[selmon->pertag->curtag] + arg->i, 0); + MANGO_MAX(selmon->pertag->nmasters[selmon->pertag->curtag] + arg->i, 0); arrange(selmon, false, false); return 0; } @@ -737,12 +737,12 @@ int32_t smartmovewin(const Arg *arg) { continue; buttom = tc->geom.y + tc->geom.height + config.gappiv; if (top > buttom && ny < buttom) { - tar = MAX(tar, buttom); + tar = MANGO_MAX(tar, buttom); }; } ny = tar == -99999 ? ny : tar; - ny = MAX(ny, c->mon->w.y + c->mon->gappov); + ny = MANGO_MAX(ny, c->mon->w.y + c->mon->gappov); break; case DOWN: tar = 99999; @@ -757,11 +757,11 @@ int32_t smartmovewin(const Arg *arg) { continue; top = tc->geom.y - config.gappiv; if (buttom < top && (ny + c->geom.height) > top) { - tar = MIN(tar, top - c->geom.height); + tar = MANGO_MIN(tar, top - c->geom.height); }; } ny = tar == 99999 ? ny : tar; - ny = MIN(ny, c->mon->w.y + c->mon->w.height - c->geom.height - + ny = MANGO_MIN(ny, c->mon->w.y + c->mon->w.height - c->geom.height - c->mon->gappov); break; case LEFT: @@ -777,12 +777,12 @@ int32_t smartmovewin(const Arg *arg) { continue; right = tc->geom.x + tc->geom.width + config.gappih; if (left > right && nx < right) { - tar = MAX(tar, right); + tar = MANGO_MAX(tar, right); }; } nx = tar == -99999 ? nx : tar; - nx = MAX(nx, c->mon->w.x + c->mon->gappoh); + nx = MANGO_MAX(nx, c->mon->w.x + c->mon->gappoh); break; case RIGHT: tar = 99999; @@ -796,11 +796,11 @@ int32_t smartmovewin(const Arg *arg) { continue; left = tc->geom.x - config.gappih; if (right < left && (nx + c->geom.width) > left) { - tar = MIN(tar, left - c->geom.width); + tar = MANGO_MIN(tar, left - c->geom.width); }; } nx = tar == 99999 ? nx : tar; - nx = MIN(nx, c->mon->w.x + c->mon->w.width - c->geom.width - + nx = MANGO_MIN(nx, c->mon->w.x + c->mon->w.width - c->geom.width - c->mon->gappoh); break; } @@ -829,7 +829,7 @@ int32_t smartresizewin(const Arg *arg) { switch (arg->i) { case UP: nh -= selmon->w.height / 8; - nh = MAX(nh, selmon->w.height / 10); + nh = MANGO_MAX(nh, selmon->w.height / 10); break; case DOWN: tar = -99999; @@ -844,7 +844,7 @@ int32_t smartresizewin(const Arg *arg) { continue; top = tc->geom.y - config.gappiv; if (buttom < top && (nh + c->geom.y) > top) { - tar = MAX(tar, top - c->geom.y); + tar = MANGO_MAX(tar, top - c->geom.y); }; } nh = tar == -99999 ? nh : tar; @@ -853,7 +853,7 @@ int32_t smartresizewin(const Arg *arg) { break; case LEFT: nw -= selmon->w.width / 16; - nw = MAX(nw, selmon->w.width / 10); + nw = MANGO_MAX(nw, selmon->w.width / 10); break; case RIGHT: tar = 99999; @@ -867,7 +867,7 @@ int32_t smartresizewin(const Arg *arg) { continue; left = tc->geom.x - config.gappih; if (right < left && (nw + c->geom.x) > left) { - tar = MIN(tar, left - c->geom.x); + tar = MANGO_MIN(tar, left - c->geom.x); }; } @@ -1064,7 +1064,7 @@ int32_t switch_layout(const Arg *arg) { if (config.circle_layout_count != 0) { for (jk = 0; jk < config.circle_layout_count; jk++) { - len = MAX( + len = MANGO_MAX( strlen(config.circle_layout[jk]), strlen(selmon->pertag->ltidxs[selmon->pertag->curtag]->name)); @@ -1083,7 +1083,7 @@ int32_t switch_layout(const Arg *arg) { } for (ji = 0; ji < LENGTH(layouts); ji++) { - len = MAX(strlen(layouts[ji].name), strlen(target_layout_name)); + len = MANGO_MAX(strlen(layouts[ji].name), strlen(target_layout_name)); if (strncmp(layouts[ji].name, target_layout_name, len) == 0) { selmon->pertag->ltidxs[selmon->pertag->curtag] = &layouts[ji]; diff --git a/src/draw/text-node.c b/src/draw/text-node.c index b96cd4ac..40cd2ff0 100644 --- a/src/draw/text-node.c +++ b/src/draw/text-node.c @@ -1,19 +1,14 @@ #include "text-node.h" + #include #include #include #include #include +#include #include #include -// 自定义 wlr_buffer,用于将 Cairo Surface 包装并注入 wlr_scene -struct mango_text_buffer { - struct wlr_buffer base; - cairo_surface_t *surface; -}; - -// 全局字体描述缓存 static GHashTable *font_desc_cache = NULL; static PangoFontDescription *get_cached_font_desc(const char *font_desc) { @@ -39,10 +34,8 @@ void mango_text_global_finish(void) { } } -// wlr_buffer 实现 static void text_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base); - cairo_surface_destroy(buf->surface); free(buf); } @@ -78,23 +71,26 @@ struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, return NULL; } - node->fg_color[0] = data.fg_color[0]; - node->fg_color[1] = data.fg_color[1]; - node->fg_color[2] = data.fg_color[2]; - node->fg_color[3] = data.fg_color[3]; - node->bg_color[0] = data.bg_color[0]; - node->bg_color[1] = data.bg_color[1]; - node->bg_color[2] = data.bg_color[2]; - node->bg_color[3] = data.bg_color[3]; - node->border_color[0] = data.border_color[0]; - node->border_color[1] = data.border_color[1]; - node->border_color[2] = data.border_color[2]; - node->border_color[3] = data.border_color[3]; + memcpy(node->fg_color, data.fg_color, sizeof(node->fg_color)); + memcpy(node->bg_color, data.bg_color, sizeof(node->bg_color)); + memcpy(node->border_color, data.border_color, sizeof(node->border_color)); node->border_width = data.border_width; node->corner_radius = data.corner_radius; node->padding_x = data.padding_x; node->padding_y = data.padding_y; - node->font_desc = data.font_desc ? data.font_desc : "monospace Bold 24"; + node->font_desc = + g_strdup(data.font_desc ? data.font_desc : "monospace Bold 24"); + + node->cached_text = NULL; + node->cached_scale = -1.0f; + node->cached_font_desc = NULL; + + node->measure_surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); + node->measure_cr = cairo_create(node->measure_surface); + node->measure_context = pango_cairo_create_context(node->measure_cr); + node->measure_layout = pango_layout_new(node->measure_context); + node->measure_scale = 1.0f; return node; } @@ -103,7 +99,31 @@ void mango_text_node_destroy(struct mango_text_node *node) { if (!node) return; + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } + + if (node->surface) { + cairo_surface_destroy(node->surface); + node->surface = NULL; + } + + if (node->measure_layout) + g_object_unref(node->measure_layout); + if (node->measure_context) + g_object_unref(node->measure_context); + if (node->measure_cr) + cairo_destroy(node->measure_cr); + if (node->measure_surface) + cairo_surface_destroy(node->measure_surface); + wlr_scene_node_destroy(&node->scene_buffer->node); + + g_free(node->font_desc); + g_free(node->cached_text); + g_free(node->cached_font_desc); + free(node); } @@ -118,28 +138,42 @@ void mango_text_node_set_background(struct mango_text_node *node, float r, } void mango_text_node_set_border(struct mango_text_node *node, float r, float g, - float b, float a, float width, float radius) { + float b, float a, int32_t width, + int32_t radius) { if (!node) return; node->border_color[0] = r; node->border_color[1] = g; node->border_color[2] = b; node->border_color[3] = a; - node->border_width = width > 0.0f ? width : 0.0f; - node->corner_radius = radius >= 0.0f ? radius : 0.0f; + node->border_width = width > 0 ? width : 0; + node->corner_radius = radius; } -void mango_text_node_set_padding(struct mango_text_node *node, float pad_x, - float pad_y) { +void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x, + int32_t pad_y) { if (!node) return; - node->padding_x = pad_x >= 0.0f ? pad_x : 0.0f; - node->padding_y = pad_y >= 0.0f ? pad_y : 0.0f; + node->padding_x = pad_x >= 0 ? pad_x : 0; + node->padding_y = pad_y >= 0 ? pad_y : 0; +} + +static void get_text_pixel_size(struct mango_text_node *node, const char *text, + float scale, int32_t *out_w, int32_t *out_h) { + if (node->measure_scale != scale) { + pango_cairo_context_set_resolution(node->measure_context, 96.0 * scale); + node->measure_scale = scale; + } + + PangoFontDescription *desc = get_cached_font_desc(node->font_desc); + pango_layout_set_font_description(node->measure_layout, desc); + pango_layout_set_text(node->measure_layout, text, -1); + + pango_layout_get_pixel_size(node->measure_layout, out_w, out_h); } static void draw_rounded_rect(cairo_t *cr, double x, double y, double w, double h, double r) { - // 使用 Cairo 的标准圆角矩形路径 double degrees = G_PI / 180.0; cairo_new_sub_path(cr); cairo_arc(cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); @@ -150,86 +184,133 @@ static void draw_rounded_rect(cairo_t *cr, double x, double y, double w, } void mango_text_node_update(struct mango_text_node *node, const char *text, - float scale) { if (!node || !text) return; if (scale <= 0.0f) scale = 1.0f; - const char *font_desc = node->font_desc; - PangoFontDescription *desc = get_cached_font_desc(font_desc); - - // 测量文字像素尺寸(无缩放的实际像素) - cairo_surface_t *dummy_surface = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); - cairo_t *dummy_cr = cairo_create(dummy_surface); - PangoContext *dummy_ctx = pango_cairo_create_context(dummy_cr); - pango_cairo_context_set_resolution(dummy_ctx, 96.0 * scale); - PangoLayout *dummy_layout = pango_layout_new(dummy_ctx); - pango_layout_set_font_description(dummy_layout, desc); - pango_layout_set_text(dummy_layout, text, -1); - - int text_pixel_w, text_pixel_h; - pango_layout_get_pixel_size(dummy_layout, &text_pixel_w, &text_pixel_h); - - g_object_unref(dummy_layout); - g_object_unref(dummy_ctx); - cairo_destroy(dummy_cr); - cairo_surface_destroy(dummy_surface); - - if (text_pixel_w <= 0 || text_pixel_h <= 0) { - wlr_scene_buffer_set_buffer(node->scene_buffer, NULL); - node->logical_width = 0; - node->logical_height = 0; + /* 脏检查 */ + if (node->cached_scale == scale && node->cached_font_desc && + strcmp(node->cached_font_desc, node->font_desc) == 0 && + node->cached_text && strcmp(node->cached_text, text) == 0 && + memcmp(node->cached_fg_color, node->fg_color, sizeof(node->fg_color)) == + 0 && + memcmp(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)) == + 0 && + memcmp(node->cached_border_color, node->border_color, + sizeof(node->border_color)) == 0 && + node->cached_border_width == node->border_width && + node->cached_corner_radius == node->corner_radius && + node->cached_padding_x == node->padding_x && + node->cached_padding_y == node->padding_y) { return; } - float logical_text_w = text_pixel_w / scale; - float logical_text_h = text_pixel_h / scale; + /* 更新缓存 */ + g_free(node->cached_text); + node->cached_text = g_strdup(text); + g_free(node->cached_font_desc); + node->cached_font_desc = g_strdup(node->font_desc); + node->cached_scale = scale; + memcpy(node->cached_fg_color, node->fg_color, sizeof(node->fg_color)); + memcpy(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)); + memcpy(node->cached_border_color, node->border_color, + sizeof(node->border_color)); + node->cached_border_width = node->border_width; + node->cached_corner_radius = node->corner_radius; + node->cached_padding_x = node->padding_x; + node->cached_padding_y = node->padding_y; - float pad_x = node->padding_x; - float pad_y = node->padding_y; - float box_logical_w = logical_text_w + 2.0f * pad_x; - float box_logical_h = logical_text_h + 2.0f * pad_y; + int32_t text_pixel_w, text_pixel_h; + get_text_pixel_size(node, text, scale, &text_pixel_w, &text_pixel_h); - float border = node->border_width; - bool draw_border = (border > 0.0f) && (node->border_color[3] > 0.0f); - bool draw_bg = (node->bg_color[3] > 0.0f); + if (text_pixel_w <= 0 || text_pixel_h <= 0) { + wlr_scene_buffer_set_buffer(node->scene_buffer, NULL); + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } + if (node->surface) { + cairo_surface_destroy(node->surface); + node->surface = NULL; + } + node->logical_width = 0; + node->logical_height = 0; + wlr_scene_buffer_set_dest_size(node->scene_buffer, 0, 0); + return; + } - int surface_pixel_w = (int)((box_logical_w + 2.0f * border) * scale + 0.5f); - int surface_pixel_h = (int)((box_logical_h + 2.0f * border) * scale + 0.5f); + /* 逻辑尺寸:文本 + 内边距 + 边框(整数计算) */ + int32_t logical_text_w = (int32_t)(text_pixel_w / scale + 0.5f); + int32_t logical_text_h = (int32_t)(text_pixel_h / scale + 0.5f); + int32_t box_logical_w = logical_text_w + 2 * node->padding_x; + int32_t box_logical_h = logical_text_h + 2 * node->padding_y; - if (surface_pixel_w < 1) - surface_pixel_w = 1; - if (surface_pixel_h < 1) - surface_pixel_h = 1; + /* 加上边框后,乘以 scale 得到物理像素(表面尺寸),向上取整 */ + int32_t required_pixel_w = + (int32_t)((box_logical_w + 2 * node->border_width) * scale + 0.5f); + int32_t required_pixel_h = + (int32_t)((box_logical_h + 2 * node->border_width) * scale + 0.5f); + if (required_pixel_w < 1) + required_pixel_w = 1; + if (required_pixel_h < 1) + required_pixel_h = 1; - cairo_surface_t *surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, surface_pixel_w, surface_pixel_h); - cairo_t *cr = cairo_create(surface); + bool surface_size_changed = (!node->surface) || + (node->surface_pixel_w != required_pixel_w) || + (node->surface_pixel_h != required_pixel_h); - // 清空为全透明 + if (surface_size_changed) { + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } + if (node->surface) { + cairo_surface_destroy(node->surface); + node->surface = NULL; + } + + node->surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, required_pixel_w, required_pixel_h); + node->surface_pixel_w = required_pixel_w; + node->surface_pixel_h = required_pixel_h; + } + + cairo_t *cr = cairo_create(node->surface); + + /* 清空为透明 */ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - double bg_x = border * scale; - double bg_y = border * scale; + /* 计算背景矩形(物理像素) */ + double border = node->border_width * scale; + double bg_x = border; + double bg_y = border; double bg_w = box_logical_w * scale; double bg_h = box_logical_h * scale; - double radius = node->corner_radius * scale; - - if (radius < 0) { + /* 圆角半径(物理像素) */ + double radius; + if (node->corner_radius < 0) { + /* 负数表示自动取半宽/半高作为圆角 */ radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0; + } else { + radius = node->corner_radius * scale; } + /* 限制最大圆角 */ if (radius > bg_w / 2.0) radius = bg_w / 2.0; if (radius > bg_h / 2.0) radius = bg_h / 2.0; + bool draw_bg = (node->bg_color[3] > 0.0f); + bool draw_border = + (node->border_width > 0) && (node->border_color[3] > 0.0f); + + /* 绘制背景 */ if (draw_bg) { cairo_set_source_rgba(cr, node->bg_color[0], node->bg_color[1], node->bg_color[2], node->bg_color[3]); @@ -242,12 +323,16 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, } } + /* 绘制文本 */ cairo_save(cr); - cairo_translate(cr, (border + pad_x) * scale, (border + pad_y) * scale); + double text_x = (node->border_width + node->padding_x) * scale; + double text_y = (node->border_width + node->padding_y) * scale; + cairo_translate(cr, text_x, text_y); PangoContext *ctx = pango_cairo_create_context(cr); pango_cairo_context_set_resolution(ctx, 96.0 * scale); PangoLayout *layout = pango_layout_new(ctx); + PangoFontDescription *desc = get_cached_font_desc(node->font_desc); pango_layout_set_font_description(layout, desc); pango_layout_set_text(layout, text, -1); @@ -259,50 +344,51 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, g_object_unref(ctx); cairo_restore(cr); + /* 绘制边框 */ if (draw_border) { cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1], node->border_color[2], node->border_color[3]); - cairo_set_line_width(cr, border * scale); + cairo_set_line_width(cr, border); - double half_lw = border * scale * 0.5; + double half_lw = border * 0.5; double bx = bg_x - half_lw; double by = bg_y - half_lw; - double bw = bg_w + border * scale; - double bh = bg_h + border * scale; + double bw = bg_w + border; + double bh = bg_h + border; if (radius > 0.0) { double outer_radius = radius + half_lw; if (outer_radius < 0.0) outer_radius = 0.0; draw_rounded_rect(cr, bx, by, bw, bh, outer_radius); - cairo_stroke(cr); } else { cairo_rectangle(cr, bx, by, bw, bh); - cairo_stroke(cr); } + cairo_stroke(cr); } - cairo_surface_flush(surface); + cairo_surface_flush(node->surface); + cairo_destroy(cr); + + /* 更新 wlr_buffer */ + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } - // 包装成 wlr_buffer struct mango_text_buffer *buf = calloc(1, sizeof(*buf)); if (!buf) { - cairo_surface_destroy(surface); - cairo_destroy(cr); return; } - wlr_buffer_init(&buf->base, &text_buffer_impl, surface_pixel_w, - surface_pixel_h); - buf->surface = surface; + wlr_buffer_init(&buf->base, &text_buffer_impl, node->surface_pixel_w, + node->surface_pixel_h); + buf->surface = node->surface; + node->buffer = buf; wlr_scene_buffer_set_buffer(node->scene_buffer, &buf->base); - wlr_buffer_drop(&buf->base); - // 最终逻辑大小 = 背景框逻辑尺寸 + 边框逻辑尺寸 - node->logical_width = (int)(box_logical_w + 2.0f * border); - node->logical_height = (int)(box_logical_h + 2.0f * border); + node->logical_width = box_logical_w + 2 * node->border_width; + node->logical_height = box_logical_h + 2 * node->border_width; wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width, node->logical_height); - - cairo_destroy(cr); } \ No newline at end of file diff --git a/src/draw/text-node.h b/src/draw/text-node.h index b8c4900d..8979a774 100644 --- a/src/draw/text-node.h +++ b/src/draw/text-node.h @@ -1,24 +1,50 @@ #ifndef MANGO_TEXT_NODE_H #define MANGO_TEXT_NODE_H +#include +#include #include #include +// 自定义 wlr_buffer,仅用于包装 cairo surface,不负责 surface 的销毁 +struct mango_text_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; +}; + struct mango_text_node { struct wlr_scene_buffer *scene_buffer; - int logical_width; - int logical_height; - const char *font_desc; - // 外观属性 - float fg_color[4]; // 文本颜色 RGBA,默认白色 - float bg_color[4]; // 背景色 RGBA,默认透明 - float border_color[4]; // 边框色 RGBA,默认透明 - int32_t border_width; // 边框宽度(逻辑像素),<=0 则不绘制 - int32_t corner_radius; // 圆角半径(逻辑像素),<0 时自动取 - // min(width,height)/2 形成胶囊 - int32_t padding_x; // 文本左右内边距(逻辑像素) - int32_t padding_y; // 文本上下内边距(逻辑像素) + float fg_color[4]; + float bg_color[4]; + float border_color[4]; + int32_t border_width; + int32_t corner_radius; + int32_t padding_x, padding_y; + char *font_desc; + + char *cached_text; + float cached_scale; + float cached_fg_color[4]; + float cached_bg_color[4]; + float cached_border_color[4]; + float cached_border_width; + float cached_corner_radius; + float cached_padding_x, cached_padding_y; + char *cached_font_desc; + + cairo_surface_t *surface; + struct mango_text_buffer *buffer; + int32_t surface_pixel_w, surface_pixel_h; + + cairo_surface_t *measure_surface; + cairo_t *measure_cr; + PangoContext *measure_context; + PangoLayout *measure_layout; + float measure_scale; + + int32_t logical_width; + int32_t logical_height; }; typedef struct { @@ -38,15 +64,15 @@ void mango_text_node_destroy(struct mango_text_node *node); void mango_text_node_update(struct mango_text_node *node, const char *text, float scale); -// 外观设置接口 void mango_text_node_set_background(struct mango_text_node *node, float r, float g, float b, float a); void mango_text_node_set_border(struct mango_text_node *node, float r, float g, - float b, float a, float width, float radius); -void mango_text_node_set_padding(struct mango_text_node *node, float pad_x, - float pad_y); + float b, float a, int32_t width, + int32_t radius); + +void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x, + int32_t pad_y); -void mango_text_node_destroy(struct mango_text_node *node); void mango_text_global_finish(void); #endif \ No newline at end of file diff --git a/src/layout/dwindle.h b/src/layout/dwindle.h index 9562b5d8..5e56a6bf 100644 --- a/src/layout/dwindle.h +++ b/src/layout/dwindle.h @@ -259,7 +259,8 @@ static void dwindle_assign(DwindleNode *node, int32_t ax, int32_t ay, if (node->client) { if (!node->client->isfullscreen && !node->client->ismaximizescreen) { - struct wlr_box box = {ax, ay, MAX(1, aw), MAX(1, ah)}; + struct wlr_box box = {ax, ay, MANGO_MAX(1, aw), + MANGO_MAX(1, ah)}; resize(node->client, box, 0); } } @@ -273,12 +274,12 @@ static void dwindle_assign(DwindleNode *node, int32_t ax, int32_t ay, node->container_w = aw; node->container_h = ah; if (node->split_h) { - int32_t w1 = MAX(1, (int32_t)(aw * node->ratio) - gap_h / 2); + int32_t w1 = MANGO_MAX(1, (int32_t)(aw * node->ratio) - gap_h / 2); dwindle_assign(node->first, ax, ay, w1, ah, gap_h, gap_v); dwindle_assign(node->second, ax + w1 + gap_h, ay, aw - w1 - gap_h, ah, gap_h, gap_v); } else { - int32_t h1 = MAX(1, (int32_t)(ah * node->ratio) - gap_v / 2); + int32_t h1 = MANGO_MAX(1, (int32_t)(ah * node->ratio) - gap_v / 2); dwindle_assign(node->first, ax, ay, aw, h1, gap_h, gap_v); dwindle_assign(node->second, ax, ay + h1 + gap_v, aw, ah - h1 - gap_v, gap_h, gap_v); @@ -357,7 +358,7 @@ static void dwindle_resize_client(Monitor *m, Client *c) { return; if (dwindle_locked_h_node) { - float cw = (float)MAX(1, dwindle_locked_h_node->container_w); + float cw = (float)MANGO_MAX(1, dwindle_locked_h_node->container_w); float ox = (float)(cursor->x - drag_begin_cursorx); if (config.dwindle_smart_resize) { /* Move the boundary toward the cursor: invert direction when @@ -374,7 +375,7 @@ static void dwindle_resize_client(Monitor *m, Client *c) { } if (dwindle_locked_v_node) { - float ch = (float)MAX(1, dwindle_locked_v_node->container_h); + float ch = (float)MANGO_MAX(1, dwindle_locked_v_node->container_h); float oy = (float)(cursor->y - drag_begin_cursory); if (config.dwindle_smart_resize) { /* Same logic for the vertical split line. */ @@ -427,13 +428,13 @@ static void dwindle_resize_client_step(Monitor *m, Client *c, int32_t dx, return; if (h_node && dx) { - float cw = (float)MAX(1, h_node->container_w); + float cw = (float)MANGO_MAX(1, h_node->container_w); float delta = (float)dx / cw; h_node->ratio = CLAMP_FLOAT(h_node->ratio + delta, 0.05f, 0.95f); } if (v_node && dy) { - float ch = (float)MAX(1, v_node->container_h); + float ch = (float)MANGO_MAX(1, v_node->container_h); float delta = (float)dy / ch; v_node->ratio = CLAMP_FLOAT(v_node->ratio + delta, 0.05f, 0.95f); } diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 419e218a..8788c210 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -62,7 +62,7 @@ void tile(Monitor *m) { if (!VISIBLEON(c, m) || !ISFAKETILED(c)) continue; if (i < m->pertag->nmasters[m->pertag->curtag]) { - r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; + r = MANGO_MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; if (c->master_inner_per > 0.0f) { h = master_surplus_height * c->master_inner_per / master_surplus_ratio; @@ -179,7 +179,7 @@ void right_tile(Monitor *m) { if (!VISIBLEON(c, m) || !ISFAKETILED(c)) continue; if (i < m->pertag->nmasters[m->pertag->curtag]) { - r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; + r = MANGO_MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; if (c->master_inner_per > 0.0f) { h = master_surplus_height * c->master_inner_per / master_surplus_ratio; @@ -345,7 +345,7 @@ void center_tile(Monitor *m) { if (i < nmasters) { // 主区域窗口 - r = MIN(n, nmasters) - i; + r = MANGO_MIN(n, nmasters) - i; if (c->master_inner_per > 0.0f) { h = master_surplus_height * c->master_inner_per / master_surplus_ratio; @@ -518,8 +518,8 @@ void deck(Monitor *m) { continue; if (i < nmasters) { c->master_mfact_per = mfact; - int32_t h = - (m->w.height - 2 * cur_gappov - my) / (MIN(n, nmasters) - i); + int32_t h = (m->w.height - 2 * cur_gappov - my) / + (MANGO_MIN(n, nmasters) - i); client_tile_resize(c, (struct wlr_box){.x = m->w.x + cur_gappoh, .y = m->w.y + cur_gappov + my, diff --git a/src/layout/vertical.h b/src/layout/vertical.h index b1de212e..6c018399 100644 --- a/src/layout/vertical.h +++ b/src/layout/vertical.h @@ -58,7 +58,7 @@ void vertical_tile(Monitor *m) { if (!VISIBLEON(c, m) || !ISFAKETILED(c)) continue; if (i < m->pertag->nmasters[m->pertag->curtag]) { - r = MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; + r = MANGO_MIN(n, m->pertag->nmasters[m->pertag->curtag]) - i; if (c->master_inner_per > 0.0f) { w = master_surplus_width * c->master_inner_per / master_surplus_ratio; @@ -155,8 +155,8 @@ void vertical_deck(Monitor *m) { continue; if (i < nmasters) { c->master_mfact_per = mfact; - int32_t w = - (m->w.width - 2 * cur_gappoh - mx) / (MIN(n, nmasters) - i); + int32_t w = (m->w.width - 2 * cur_gappoh - mx) / + (MANGO_MIN(n, nmasters) - i); client_tile_resize(c, (struct wlr_box){.x = m->w.x + cur_gappoh + mx, .y = m->w.y + cur_gappov, diff --git a/src/mango.c b/src/mango.c index 8b9e375d..8da371d1 100644 --- a/src/mango.c +++ b/src/mango.c @@ -97,8 +97,8 @@ #include "draw/text-node.h" /* macros */ -#define MAX(A, B) ((A) > (B) ? (A) : (B)) -#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define MANGO_MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MANGO_MIN(A, B) ((A) < (B) ? (A) : (B)) #define GEZERO(A) ((A) >= 0 ? (A) : 0) #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) #define INSIDEMON(A) \ @@ -1151,8 +1151,8 @@ void client_change_mon(Client *c, Monitor *m) { void applybounds(Client *c, struct wlr_box *bbox) { /* set minimum possible */ - c->geom.width = MAX(1 + 2 * (int32_t)c->bw, c->geom.width); - c->geom.height = MAX(1 + 2 * (int32_t)c->bw, c->geom.height); + c->geom.width = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.width); + c->geom.height = MANGO_MAX(1 + 2 * (int32_t)c->bw, c->geom.height); if (c->geom.x >= bbox->x + bbox->width) c->geom.x = bbox->x + bbox->width - c->geom.width; @@ -1575,7 +1575,7 @@ void set_float_malposition(Client *tc) { y = tc->geom.y; xreverse = 1; yreverse = 1; - offset = MIN(tc->mon->w.width / 20, tc->mon->w.height / 20); + offset = MANGO_MIN(tc->mon->w.width / 20, tc->mon->w.height / 20); wl_list_for_each(c, &clients, link) { if (c->isfloating && c != tc && VISIBLEON(c, tc->mon) && @@ -5613,10 +5613,10 @@ void setfullscreen(Client *c, int32_t fullscreen, } void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv) { - selmon->gappoh = MAX(oh, 0); - selmon->gappov = MAX(ov, 0); - selmon->gappih = MAX(ih, 0); - selmon->gappiv = MAX(iv, 0); + selmon->gappoh = MANGO_MAX(oh, 0); + selmon->gappov = MANGO_MAX(ov, 0); + selmon->gappih = MANGO_MAX(ih, 0); + selmon->gappiv = MANGO_MAX(iv, 0); arrange(selmon, false, false); } From 251369b5a308a5a10417e0820f0f5297f043393d Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 17 Jun 2026 08:42:46 +0800 Subject: [PATCH 03/13] opt: optmize label create judge --- src/layout/overview.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/layout/overview.h b/src/layout/overview.h index 06e2213f..32a9a18d 100644 --- a/src/layout/overview.h +++ b/src/layout/overview.h @@ -359,8 +359,7 @@ void create_jump_hints(Monitor *m) { Client *c; wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && - ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + if (VISIBLEON(c, m) && !c->isunglobal && !client_is_x11_popup(c)) { if (label_idx >= 26) break; char c_char = jump_labels[label_idx]; From 99b696541aafcdc19eb467149685650a7ad814fb Mon Sep 17 00:00:00 2001 From: DarkGuibrine Date: Tue, 16 Jun 2026 14:54:04 -0300 Subject: [PATCH 04/13] nix: fix build dependency --- meson.build | 2 ++ nix/default.nix | 2 ++ 2 files changed, 4 insertions(+) diff --git a/meson.build b/meson.build index 53201a28..9126e0d6 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,7 @@ endif cc = meson.get_compiler('c') libm = cc.find_library('m') +libdrm = dependency('libdrm') xcb = dependency('xcb', required : get_option('xwayland')) xlibs = dependency('xcb-icccm', required : get_option('xwayland')) wlroots_dep = dependency('wlroots-0.20',version: '>=0.20.0') @@ -98,6 +99,7 @@ executable('mango', wayland_sources, dependencies : [ libm, + libdrm, xcb, xlibs, wayland_server_dep, diff --git a/nix/default.nix b/nix/default.nix index cbfc917a..8c35eb83 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -3,6 +3,7 @@ libX11, libinput, libxcb, + libdrm, libxkbcommon, pcre2, pango, @@ -58,6 +59,7 @@ stdenv.mkDerivation { wlroots_0_19 scenefx libGL + libdrm ] ++ lib.optionals enableXWayland [ libX11 From 604372cabff11e6f35646924c3312f7b3b0edbdd Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 17 Jun 2026 10:22:37 +0800 Subject: [PATCH 05/13] feat: monocle layout support title tab --- docs/visuals/theming.md | 19 +- src/action/client.h | 20 +- src/animation/client.h | 14 +- src/animation/layer.h | 8 +- src/animation/tag.h | 38 +-- src/config/parse_config.h | 145 +++++++---- src/dispatch/bind_define.h | 8 +- src/draw/text-node.c | 485 +++++++++++++++++++++++++++++++++++-- src/draw/text-node.h | 130 ++++++++-- src/fetch/monitor.h | 8 + src/layout/arrange.h | 29 +++ src/layout/horizontal.h | 68 ++++-- src/mango.c | 52 +++- 13 files changed, 868 insertions(+), 156 deletions(-) diff --git a/docs/visuals/theming.md b/docs/visuals/theming.md index 71d3a818..928b1d24 100644 --- a/docs/visuals/theming.md +++ b/docs/visuals/theming.md @@ -14,6 +14,7 @@ Control the sizing of window borders and gaps. | `gappiv` | `5` | Vertical inner gap. | | `gappoh` | `10` | Horizontal outer gap (between windows and screen edges). | | `gappov` | `10` | Vertical outer gap. | +| `tab_bar_height` | `50` | Height of the tab bar for monocle layout. | ## Colors @@ -55,14 +56,16 @@ You can also color-code windows based on their state: ### Overview Jump Mode | Setting | Default | Description | | :--- | :--- | :--- | -| `jump_hit_fg_color` | `0xc4939dff` | label text color. | -| `jump_hit_bg_color` | `0x201b14ff` | label background color. -| `jump_hit_border_color` | `0x8BAA9Bff` | label border color. -| `jump_hit_border_width` | `4` | label border width. -| `jump_hit_corner_radius` | `5` | label corner radius. -| `jump_hit_padding_x` | `10` | label horizontal padding. -| `jump_hit_padding_y` | `10` | label vertical padding. -| `jump_hit_font_desc` | `monospace Bold 24` | label font set.| +| `text_decorate_fg_color` | `0xc4939dff` | label text color. | +| `text_decorate_bg_color` | `0x201b14ff` | label background color.| +| `text_decorate_focus_fg_color` | `0x201b14ff` | label text color for focus. | +| `text_decorate_focus_bg_color` | `0xc4939dff` | label background color for focus.| +| `text_decorate_border_color` | `0x8BAA9Bff` | label border color.| +| `text_decorate_border_width` | `4` | label border width.| +| `text_decorate_corner_radius` | `5` | label corner radius.| +| `text_decorate_padding_x` | `10` | label horizontal padding.| +| `text_decorate_padding_y` | `10` | label vertical padding.| +| `text_decorate_font_desc` | `monospace Bold 16` | label font set.| ## Cursor Theme diff --git a/src/action/client.h b/src/action/client.h index 11602e70..d13cb5c6 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -95,4 +95,22 @@ void client_pending_force_kill(Client *c) { if (!c) return; kill(c->pid, SIGKILL); -} \ No newline at end of file +} + +void client_add_text_node(Client *c) { + c->text_node = mango_text_node_create(c->scene, config.textdata); + wlr_scene_node_lower_to_bottom(&c->text_node->scene_buffer->node); + wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, false); +} + +void client_add_titlebar_node(Client *c) { + MangoNodeData *mangonodedata = ecalloc(1, sizeof(MangoNodeData)); + mangonodedata->node_data = c; + mangonodedata->type = MANGO_TITLE_NODE; + + c->titlebar_node = mango_titlebar_node_create( + mangonodedata, layers[LyrDecorate], config.textdata, 0, 0); + wlr_scene_node_lower_to_bottom(&c->titlebar_node->scene_buffer->node); + wlr_scene_node_set_enabled(&c->titlebar_node->scene_buffer->node, false); + mango_titlebar_node_update(c->titlebar_node, client_get_title(c), 1.0); +} diff --git a/src/animation/client.h b/src/animation/client.h index e39a7d94..1c77ab3c 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -289,6 +289,16 @@ void apply_shield(Client *c, struct wlr_box clip_box) { } } +void global_draw_titlebar(Client *c, int32_t x, int32_t y, int32_t width, + int32_t height) { + if (!c->titlebar_node) + return; + + wlr_scene_node_set_position(&c->titlebar_node->scene_buffer->node, x, y); + wlr_scene_node_set_enabled(&c->titlebar_node->scene_buffer->node, true); + mango_titlebar_node_set_size(c->titlebar_node, width, height); +} + void apply_split_border(Client *c, bool hit_no_border) { if (c->iskilling || !c->mon || !client_surface(c)->mapped) @@ -544,8 +554,10 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) { c->is_clip_to_hide = true; wlr_scene_node_set_enabled(&c->scene->node, false); - } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon)) { + } else if (c->is_clip_to_hide && VISIBLEON(c, c->mon) && + (!c->is_monocle_hide || !is_monocle_layout(c->mon))) { c->is_clip_to_hide = false; + c->is_monocle_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); } diff --git a/src/animation/layer.h b/src/animation/layer.h index 993cfd12..476cba63 100644 --- a/src/animation/layer.h +++ b/src/animation/layer.h @@ -277,10 +277,10 @@ void layer_animation_next_tick(LayerSurface *l) { double opacity_eased_progress = find_animation_curve_at(animation_passed, OPAFADEIN); - double opacity = - MANGO_MIN(config.fadein_begin_opacity + - opacity_eased_progress * (1.0 - config.fadein_begin_opacity), - 1.0f); + double opacity = MANGO_MIN(config.fadein_begin_opacity + + opacity_eased_progress * + (1.0 - config.fadein_begin_opacity), + 1.0f); if (config.animation_fade_in) wlr_scene_node_for_each_buffer(&l->scene->node, diff --git a/src/animation/tag.h b/src/animation/tag.h index 23986a44..859ee1b8 100644 --- a/src/animation/tag.h +++ b/src/animation/tag.h @@ -24,29 +24,31 @@ void set_tagin_animation(Monitor *m, Client *c) { c->animainit_geom.x = config.tag_animation_direction == VERTICAL ? c->animation.current.x : MANGO_MAX(c->mon->m.x + c->mon->m.width, - c->geom.x + c->mon->m.width); + c->geom.x + c->mon->m.width); c->animainit_geom.y = config.tag_animation_direction == VERTICAL ? MANGO_MAX(c->mon->m.y + c->mon->m.height, - c->geom.y + c->mon->m.height) + c->geom.y + c->mon->m.height) : c->animation.current.y; } else { - c->animainit_geom.x = - config.tag_animation_direction == VERTICAL - ? c->animation.current.x - : MANGO_MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); - c->animainit_geom.y = - config.tag_animation_direction == VERTICAL - ? MANGO_MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) - : c->animation.current.y; + c->animainit_geom.x = config.tag_animation_direction == VERTICAL + ? c->animation.current.x + : MANGO_MIN(m->m.x - c->geom.width, + c->geom.x - c->mon->m.width); + c->animainit_geom.y = config.tag_animation_direction == VERTICAL + ? MANGO_MIN(m->m.y - c->geom.height, + c->geom.y - c->mon->m.height) + : c->animation.current.y; } } void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { - if (!c->is_clip_to_hide || !ISTILED(c) || !is_scroller_layout(c->mon)) { + if (!ISTILED(c) || ((!c->is_clip_to_hide || !is_scroller_layout(c->mon)) && + (!c->is_monocle_hide || !is_monocle_layout(c->mon)))) { c->is_clip_to_hide = false; + c->is_monocle_hide = false; wlr_scene_node_set_enabled(&c->scene->node, true); wlr_scene_node_set_enabled(&c->scene_surface->node, true); } @@ -84,13 +86,13 @@ void set_tagout_animation(Monitor *m, Client *c) { : m->pertag->curtag > m->pertag->prevtag; if (going_forward) { c->pending = c->geom; - c->pending.x = - config.tag_animation_direction == VERTICAL - ? c->animation.current.x - : MANGO_MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); + c->pending.x = config.tag_animation_direction == VERTICAL + ? c->animation.current.x + : MANGO_MIN(c->mon->m.x - c->geom.width, + c->geom.x - c->mon->m.width); c->pending.y = config.tag_animation_direction == VERTICAL ? MANGO_MIN(c->mon->m.y - c->geom.height, - c->geom.y - c->mon->m.height) + c->geom.y - c->mon->m.height) : c->animation.current.y; resize(c, c->geom, 0); @@ -99,10 +101,10 @@ void set_tagout_animation(Monitor *m, Client *c) { c->pending.x = config.tag_animation_direction == VERTICAL ? c->animation.current.x : MANGO_MAX(c->mon->m.x + c->mon->m.width, - c->geom.x + c->mon->m.width); + c->geom.x + c->mon->m.width); c->pending.y = config.tag_animation_direction == VERTICAL ? MANGO_MAX(c->mon->m.y + c->mon->m.height, - c->geom.y + c->mon->m.height) + c->geom.y + c->mon->m.height) : c->animation.current.y; resize(c, c->geom, 0); } diff --git a/src/config/parse_config.h b/src/config/parse_config.h index e01d750c..a34ca2e9 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -311,6 +311,7 @@ typedef struct { uint32_t gappoh; uint32_t gappov; uint32_t borderpx; + uint32_t tab_bar_height; float scratchpad_width_ratio; float scratchpad_height_ratio; float rootcolor[4]; @@ -387,7 +388,7 @@ typedef struct { struct xkb_context *ctx; struct xkb_keymap *keymap; - JumphitData jumhitdata; + TextDrawData textdata; } Config; typedef int32_t (*FuncType)(const Arg *); @@ -1706,50 +1707,76 @@ bool parse_option(Config *config, char *key, char *value) { config->cursor_size = atoi(value); } else if (strcmp(key, "cursor_theme") == 0) { config->cursor_theme = strdup(value); - } else if (strcmp(key, "jump_hit_fg_color") == 0) { + } else if (strcmp(key, "text_decorate_font_desc") == 0) { + config->textdata.font_desc = strdup(value); + } else if (strcmp(key, "text_decorate_fg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_fg_color " + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "text_decorate_fg_color " "format: %s\n", value); return false; } else { - convert_hex_to_rgba(config->jumhitdata.fg_color, color); + convert_hex_to_rgba(config->textdata.fg_color, color); } - } else if (strcmp(key, "jump_hit_font_desc") == 0) { - config->jumhitdata.font_desc = strdup(value); - } else if (strcmp(key, "jump_hit_bg_color") == 0) { + } else if (strcmp(key, "text_decorate_bg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_bg_color " + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "text_decorate_bg_color " "format: %s\n", value); return false; } else { - convert_hex_to_rgba(config->jumhitdata.bg_color, color); + convert_hex_to_rgba(config->textdata.bg_color, color); } - } else if (strcmp(key, "jump_hit_border_color") == 0) { + } else if (strcmp(key, "text_decorate_focus_fg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { - fprintf( - stderr, - "\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_border_color " - "format: %s\n", - value); + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "text_decorate_focus_fg_color " + "format: %s\n", + value); return false; } else { - convert_hex_to_rgba(config->jumhitdata.border_color, color); + convert_hex_to_rgba(config->textdata.focus_fg_color, color); } - } else if (strcmp(key, "jump_hit_border_width") == 0) { - config->jumhitdata.border_width = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "jump_hit_corner_radius") == 0) { - config->jumhitdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "jump_hit_padding_x") == 0) { - config->jumhitdata.padding_x = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "jump_hit_padding_y") == 0) { - config->jumhitdata.padding_y = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "text_decorate_focus_bg_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "text_decorate_focus_bg_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->textdata.focus_bg_color, color); + } + } else if (strcmp(key, "text_decorate_border_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "text_decorate_border_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->textdata.border_color, color); + } + } else if (strcmp(key, "text_decorate_border_width") == 0) { + config->textdata.border_width = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "text_decorate_corner_radius") == 0) { + config->textdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "text_decorate_padding_x") == 0) { + config->textdata.padding_x = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "text_decorate_padding_y") == 0) { + config->textdata.padding_y = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "disable_while_typing") == 0) { config->disable_while_typing = atoi(value); } else if (strcmp(key, "left_handed") == 0) { @@ -1796,6 +1823,8 @@ bool parse_option(Config *config, char *key, char *value) { config->scratchpad_height_ratio = atof(value); } else if (strcmp(key, "borderpx") == 0) { config->borderpx = atoi(value); + } else if (strcmp(key, "tab_bar_height") == 0) { + config->tab_bar_height = atoi(value); } else if (strcmp(key, "rootcolor") == 0) { int64_t color = parse_color(value); if (color == -1) { @@ -3209,9 +3238,9 @@ void free_config(void) { config.cursor_theme = NULL; } - if (config.jumhitdata.font_desc) { - free((void *)config.jumhitdata.font_desc); - config.jumhitdata.font_desc = NULL; + if (config.textdata.font_desc) { + free((void *)config.textdata.font_desc); + config.textdata.font_desc = NULL; } if (config.tablet_map_to_mon) { @@ -3375,19 +3404,18 @@ void override_config(void) { config.scratchpad_height_ratio = CLAMP_FLOAT(config.scratchpad_height_ratio, 0.1f, 1.0f); config.borderpx = CLAMP_INT(config.borderpx, 0, 200); + config.tab_bar_height = CLAMP_INT(config.tab_bar_height, 5, 500); config.smartgaps = CLAMP_INT(config.smartgaps, 0, 1); config.focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f); config.unfocused_opacity = CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f); - config.jumhitdata.border_width = - CLAMP_INT(config.jumhitdata.border_width, 0, 100); - config.jumhitdata.corner_radius = - CLAMP_INT(config.jumhitdata.corner_radius, 0, 100); - config.jumhitdata.padding_x = - CLAMP_INT(config.jumhitdata.padding_x, 0, 100); - config.jumhitdata.padding_y = - CLAMP_INT(config.jumhitdata.padding_y, 0, 100); + config.textdata.border_width = + CLAMP_INT(config.textdata.border_width, 0, 100); + config.textdata.corner_radius = + CLAMP_INT(config.textdata.corner_radius, 0, 100); + config.textdata.padding_x = CLAMP_INT(config.textdata.padding_x, 0, 100); + config.textdata.padding_y = CLAMP_INT(config.textdata.padding_y, 0, 100); } void set_value_default() { @@ -3475,6 +3503,7 @@ void set_value_default() { config.idleinhibit_ignore_visible = 0; config.borderpx = 4; + config.tab_bar_height = 50; config.overviewgappi = 5; config.overviewgappo = 30; config.cursor_hide_timeout = 0; @@ -3539,22 +3568,30 @@ void set_value_default() { config.animation_curve_opafadeout[2] = 0.5; config.animation_curve_opafadeout[3] = 0.5; - config.jumhitdata.fg_color[0] = 0xc4 / 255.0f; - config.jumhitdata.fg_color[1] = 0x93 / 255.0f; - config.jumhitdata.fg_color[2] = 0x9d / 255.0f; - config.jumhitdata.fg_color[3] = 1.0f; - config.jumhitdata.bg_color[0] = 0x32 / 255.0f; - config.jumhitdata.bg_color[1] = 0x32 / 255.0f; - config.jumhitdata.bg_color[2] = 0x32 / 255.0f; - config.jumhitdata.bg_color[3] = 1.0f; - config.jumhitdata.border_color[0] = 0x8b / 255.0f; - config.jumhitdata.border_color[1] = 0xaa / 255.0f; - config.jumhitdata.border_color[2] = 0x9b / 255.0f; - config.jumhitdata.border_color[3] = 1.0f; - config.jumhitdata.border_width = 4; - config.jumhitdata.corner_radius = 5; - config.jumhitdata.padding_x = 10; - config.jumhitdata.padding_y = 10; + config.textdata.fg_color[0] = 0xc4 / 255.0f; + config.textdata.fg_color[1] = 0x93 / 255.0f; + config.textdata.fg_color[2] = 0x9d / 255.0f; + config.textdata.fg_color[3] = 1.0f; + config.textdata.bg_color[0] = 0x32 / 255.0f; + config.textdata.bg_color[1] = 0x32 / 255.0f; + config.textdata.bg_color[2] = 0x32 / 255.0f; + config.textdata.bg_color[3] = 1.0f; + config.textdata.focus_fg_color[0] = 0xed / 255.0f; + config.textdata.focus_fg_color[1] = 0xa6 / 255.0f; + config.textdata.focus_fg_color[2] = 0xb4 / 255.0f; + config.textdata.focus_fg_color[3] = 1.0f; + config.textdata.focus_bg_color[0] = 0x4e / 255.0f; + config.textdata.focus_bg_color[1] = 0x45 / 255.0f; + config.textdata.focus_bg_color[2] = 0x3c / 255.0f; + config.textdata.focus_bg_color[3] = 1.0f; + config.textdata.border_color[0] = 0x8b / 255.0f; + config.textdata.border_color[1] = 0xaa / 255.0f; + config.textdata.border_color[2] = 0x9b / 255.0f; + config.textdata.border_color[3] = 1.0f; + config.textdata.border_width = 4; + config.textdata.corner_radius = 5; + config.textdata.padding_x = 10; + config.textdata.padding_y = 10; config.rootcolor[0] = 0x32 / 255.0f; config.rootcolor[1] = 0x32 / 255.0f; @@ -3668,7 +3705,7 @@ bool parse_config(void) { config.tag_rules = NULL; config.tag_rules_count = 0; config.cursor_theme = NULL; - config.jumhitdata.font_desc = NULL; + config.textdata.font_desc = NULL; config.tablet_map_to_mon = NULL; strcpy(config.keymode, "default"); @@ -3806,6 +3843,10 @@ void reapply_property(void) { c->bw = config.borderpx; } + mango_text_node_apply_config(c->text_node, &config.textdata); + mango_titlebar_node_apply_config(c->titlebar_node, + &config.textdata); + wlr_scene_rect_set_color(c->droparea, config.dropcolor); wlr_scene_rect_set_color(c->splitindicator[0], config.splitcolor); wlr_scene_rect_set_color(c->splitindicator[1], config.splitcolor); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index a199a92e..61762271 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -137,6 +137,7 @@ int32_t focusdir(const Arg *arg) { Client *c = NULL; c = direction_select(arg); + if (!selmon->isoverview) c = get_focused_stack_client(c, arg->tc); if (c) { @@ -762,7 +763,7 @@ int32_t smartmovewin(const Arg *arg) { } ny = tar == 99999 ? ny : tar; ny = MANGO_MIN(ny, c->mon->w.y + c->mon->w.height - c->geom.height - - c->mon->gappov); + c->mon->gappov); break; case LEFT: tar = -99999; @@ -801,7 +802,7 @@ int32_t smartmovewin(const Arg *arg) { } nx = tar == 99999 ? nx : tar; nx = MANGO_MIN(nx, c->mon->w.x + c->mon->w.width - c->geom.width - - c->mon->gappoh); + c->mon->gappoh); break; } @@ -1083,7 +1084,8 @@ int32_t switch_layout(const Arg *arg) { } for (ji = 0; ji < LENGTH(layouts); ji++) { - len = MANGO_MAX(strlen(layouts[ji].name), strlen(target_layout_name)); + len = + MANGO_MAX(strlen(layouts[ji].name), strlen(target_layout_name)); if (strncmp(layouts[ji].name, target_layout_name, len) == 0) { selmon->pertag->ltidxs[selmon->pertag->curtag] = &layouts[ji]; diff --git a/src/draw/text-node.c b/src/draw/text-node.c index 40cd2ff0..32164fbe 100644 --- a/src/draw/text-node.c +++ b/src/draw/text-node.c @@ -60,7 +60,7 @@ static const struct wlr_buffer_impl text_buffer_impl = { }; struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, - JumphitData data) { + TextDrawData data) { struct mango_text_node *node = calloc(1, sizeof(*node)); if (!node) return NULL; @@ -73,17 +73,23 @@ struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, memcpy(node->fg_color, data.fg_color, sizeof(node->fg_color)); memcpy(node->bg_color, data.bg_color, sizeof(node->bg_color)); + memcpy(node->focus_fg_color, data.focus_fg_color, + sizeof(node->focus_fg_color)); + memcpy(node->focus_bg_color, data.focus_bg_color, + sizeof(node->focus_bg_color)); memcpy(node->border_color, data.border_color, sizeof(node->border_color)); node->border_width = data.border_width; node->corner_radius = data.corner_radius; node->padding_x = data.padding_x; node->padding_y = data.padding_y; node->font_desc = - g_strdup(data.font_desc ? data.font_desc : "monospace Bold 24"); + g_strdup(data.font_desc ? data.font_desc : "monospace Bold 16"); node->cached_text = NULL; node->cached_scale = -1.0f; node->cached_font_desc = NULL; + node->focused = false; + node->cached_focused = false; node->measure_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); @@ -92,6 +98,8 @@ struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, node->measure_layout = pango_layout_new(node->measure_context); node->measure_scale = 1.0f; + node->scene_buffer->node.data = NULL; + return node; } @@ -190,7 +198,7 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, if (scale <= 0.0f) scale = 1.0f; - /* 脏检查 */ + /* 脏检查,加入 focused 状态 */ if (node->cached_scale == scale && node->cached_font_desc && strcmp(node->cached_font_desc, node->font_desc) == 0 && node->cached_text && strcmp(node->cached_text, text) == 0 && @@ -198,12 +206,17 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, 0 && memcmp(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)) == 0 && + memcmp(node->cached_focus_fg_color, node->focus_fg_color, + sizeof(node->focus_fg_color)) == 0 && + memcmp(node->cached_focus_bg_color, node->focus_bg_color, + sizeof(node->focus_bg_color)) == 0 && memcmp(node->cached_border_color, node->border_color, sizeof(node->border_color)) == 0 && node->cached_border_width == node->border_width && node->cached_corner_radius == node->corner_radius && node->cached_padding_x == node->padding_x && - node->cached_padding_y == node->padding_y) { + node->cached_padding_y == node->padding_y && + node->cached_focused == node->focused) { return; } @@ -215,12 +228,17 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, node->cached_scale = scale; memcpy(node->cached_fg_color, node->fg_color, sizeof(node->fg_color)); memcpy(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)); + memcpy(node->cached_focus_fg_color, node->focus_fg_color, + sizeof(node->focus_fg_color)); + memcpy(node->cached_focus_bg_color, node->focus_bg_color, + sizeof(node->focus_bg_color)); memcpy(node->cached_border_color, node->border_color, sizeof(node->border_color)); node->cached_border_width = node->border_width; node->cached_corner_radius = node->corner_radius; node->cached_padding_x = node->padding_x; node->cached_padding_y = node->padding_y; + node->cached_focused = node->focused; int32_t text_pixel_w, text_pixel_h; get_text_pixel_size(node, text, scale, &text_pixel_w, &text_pixel_h); @@ -241,13 +259,11 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, return; } - /* 逻辑尺寸:文本 + 内边距 + 边框(整数计算) */ int32_t logical_text_w = (int32_t)(text_pixel_w / scale + 0.5f); int32_t logical_text_h = (int32_t)(text_pixel_h / scale + 0.5f); int32_t box_logical_w = logical_text_w + 2 * node->padding_x; int32_t box_logical_h = logical_text_h + 2 * node->padding_y; - /* 加上边框后,乘以 scale 得到物理像素(表面尺寸),向上取整 */ int32_t required_pixel_w = (int32_t)((box_logical_w + 2 * node->border_width) * scale + 0.5f); int32_t required_pixel_h = @@ -279,41 +295,40 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, cairo_t *cr = cairo_create(node->surface); - /* 清空为透明 */ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - /* 计算背景矩形(物理像素) */ double border = node->border_width * scale; double bg_x = border; double bg_y = border; double bg_w = box_logical_w * scale; double bg_h = box_logical_h * scale; - /* 圆角半径(物理像素) */ double radius; if (node->corner_radius < 0) { - /* 负数表示自动取半宽/半高作为圆角 */ radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0; } else { radius = node->corner_radius * scale; } - /* 限制最大圆角 */ if (radius > bg_w / 2.0) radius = bg_w / 2.0; if (radius > bg_h / 2.0) radius = bg_h / 2.0; - bool draw_bg = (node->bg_color[3] > 0.0f); + const float *active_bg = + node->focused ? node->focus_bg_color : node->bg_color; + const float *active_fg = + node->focused ? node->focus_fg_color : node->fg_color; + + bool draw_bg = (active_bg[3] > 0.0f); // 使用 active_bg bool draw_border = (node->border_width > 0) && (node->border_color[3] > 0.0f); - /* 绘制背景 */ if (draw_bg) { - cairo_set_source_rgba(cr, node->bg_color[0], node->bg_color[1], - node->bg_color[2], node->bg_color[3]); + cairo_set_source_rgba(cr, active_bg[0], active_bg[1], active_bg[2], + active_bg[3]); if (radius > 0.0) { draw_rounded_rect(cr, bg_x, bg_y, bg_w, bg_h, radius); cairo_fill(cr); @@ -323,7 +338,6 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, } } - /* 绘制文本 */ cairo_save(cr); double text_x = (node->border_width + node->padding_x) * scale; double text_y = (node->border_width + node->padding_y) * scale; @@ -336,15 +350,14 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, pango_layout_set_font_description(layout, desc); pango_layout_set_text(layout, text, -1); - cairo_set_source_rgba(cr, node->fg_color[0], node->fg_color[1], - node->fg_color[2], node->fg_color[3]); + cairo_set_source_rgba(cr, active_fg[0], active_fg[1], active_fg[2], + active_fg[3]); pango_cairo_show_layout(cr, layout); g_object_unref(layout); g_object_unref(ctx); cairo_restore(cr); - /* 绘制边框 */ if (draw_border) { cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1], node->border_color[2], node->border_color[3]); @@ -370,7 +383,6 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, cairo_surface_flush(node->surface); cairo_destroy(cr); - /* 更新 wlr_buffer */ if (node->buffer) { wlr_buffer_drop(&node->buffer->base); node->buffer = NULL; @@ -391,4 +403,437 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, node->logical_height = box_logical_h + 2 * node->border_width; wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width, node->logical_height); +} + +void mango_text_node_set_focus(struct mango_text_node *node, bool focused) { + if (!node || node->focused == focused) + return; + node->focused = focused; + // 使用缓存的文本和缩放触发重绘(如果无文本则不重绘) + if (node->cached_text && node->cached_scale > 0.0f) { + mango_text_node_update(node, node->cached_text, node->cached_scale); + } +} + +struct mango_titlebar_node * +mango_titlebar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, + TextDrawData data, int32_t width, int32_t height) { + struct mango_titlebar_node *node = calloc(1, sizeof(*node)); + if (!node) + return NULL; + + node->scene_buffer = wlr_scene_buffer_create(parent, NULL); + if (!node->scene_buffer) { + free(node); + return NULL; + } + + memcpy(node->fg_color, data.fg_color, sizeof(node->fg_color)); + memcpy(node->bg_color, data.bg_color, sizeof(node->bg_color)); + memcpy(node->focus_fg_color, data.focus_fg_color, + sizeof(node->focus_fg_color)); + memcpy(node->focus_bg_color, data.focus_bg_color, + sizeof(node->focus_bg_color)); + memcpy(node->border_color, data.border_color, sizeof(node->border_color)); + node->border_width = data.border_width; + node->corner_radius = data.corner_radius; + node->padding_x = data.padding_x; + node->padding_y = data.padding_y; + node->font_desc = + g_strdup(data.font_desc ? data.font_desc : "monospace Bold 16"); + + node->target_width = width; + node->target_height = height; + node->focused = false; + node->cached_focused = false; + + node->measure_surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); + node->measure_cr = cairo_create(node->measure_surface); + node->measure_context = pango_cairo_create_context(node->measure_cr); + node->measure_layout = pango_layout_new(node->measure_context); + node->measure_scale = 1.0f; + + node->cached_scale = -1.0f; + node->last_text = NULL; + node->last_scale = 0.0f; + node->scene_buffer->node.data = mango_node_data; + + return node; +} + +void mango_titlebar_node_destroy(struct mango_titlebar_node *node) { + if (!node) + return; + + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } + + if (node->surface) { + cairo_surface_destroy(node->surface); + node->surface = NULL; + } + if (node->measure_surface) { + cairo_surface_destroy(node->measure_surface); + node->measure_surface = NULL; + } + + if (node->measure_layout) + g_object_unref(node->measure_layout); + if (node->measure_context) + g_object_unref(node->measure_context); + if (node->measure_cr) + cairo_destroy(node->measure_cr); + + void *data = node->scene_buffer->node.data; + wlr_scene_node_destroy(&node->scene_buffer->node); + + g_free(node->font_desc); + g_free(node->cached_text); + g_free(node->cached_font_desc); + g_free(node->last_text); + free(data); + free(node); +} + +void mango_titlebar_node_set_size(struct mango_titlebar_node *node, + int32_t width, int32_t height) { + if (!node) + return; + + if (width < 0) + width = 0; + if (height < 0) + height = 0; + + if (node->target_width == width && node->target_height == height) + return; + + node->target_width = width; + node->target_height = height; + + const char *redraw_text = node->last_text ? node->last_text : ""; + float redraw_scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; + + mango_titlebar_node_update(node, redraw_text, redraw_scale); +} + +void mango_titlebar_node_update(struct mango_titlebar_node *node, + const char *text, float scale) { + if (!node || !text) + return; + if (scale <= 0.0f) + scale = 1.0f; + + char *safe_text = g_strdup(text); + + g_free(node->last_text); + node->last_text = safe_text; // 所有权转移 + node->last_scale = scale; + + // 脏检查加入 focused + if (node->cached_scale == scale && node->cached_font_desc && + strcmp(node->cached_font_desc, node->font_desc) == 0 && + node->cached_text && strcmp(node->cached_text, safe_text) == 0 && + memcmp(node->cached_fg_color, node->fg_color, sizeof(node->fg_color)) == + 0 && + memcmp(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)) == + 0 && + memcmp(node->cached_focus_fg_color, node->focus_fg_color, + sizeof(node->focus_fg_color)) == 0 && + memcmp(node->cached_focus_bg_color, node->focus_bg_color, + sizeof(node->focus_bg_color)) == 0 && + memcmp(node->cached_border_color, node->border_color, + sizeof(node->border_color)) == 0 && + node->cached_border_width == node->border_width && + node->cached_corner_radius == node->corner_radius && + node->cached_padding_x == node->padding_x && + node->cached_padding_y == node->padding_y && + node->cached_target_width == node->target_width && + node->cached_target_height == node->target_height && + node->cached_focused == node->focused) { + return; + } + + // 更新缓存 + g_free(node->cached_text); + node->cached_text = g_strdup(safe_text); + + g_free(node->cached_font_desc); + node->cached_font_desc = g_strdup(node->font_desc); + node->cached_scale = scale; + memcpy(node->cached_fg_color, node->fg_color, sizeof(node->fg_color)); + memcpy(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)); + memcpy(node->cached_focus_fg_color, node->focus_fg_color, + sizeof(node->focus_fg_color)); + memcpy(node->cached_focus_bg_color, node->focus_bg_color, + sizeof(node->focus_bg_color)); + memcpy(node->cached_border_color, node->border_color, + sizeof(node->border_color)); + node->cached_border_width = node->border_width; + node->cached_corner_radius = node->corner_radius; + node->cached_padding_x = node->padding_x; + node->cached_padding_y = node->padding_y; + node->cached_target_width = node->target_width; + node->cached_target_height = node->target_height; + node->cached_focused = node->focused; + + if (node->target_width <= 0 || node->target_height <= 0) { + wlr_scene_buffer_set_buffer(node->scene_buffer, NULL); + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } + if (node->surface) { + cairo_surface_destroy(node->surface); + node->surface = NULL; + } + node->logical_width = 0; + node->logical_height = 0; + wlr_scene_buffer_set_dest_size(node->scene_buffer, 0, 0); + return; + } + + int32_t box_logical_w = node->target_width - 2 * node->border_width; + int32_t box_logical_h = node->target_height - 2 * node->border_width; + if (box_logical_w < 0) + box_logical_w = 0; + if (box_logical_h < 0) + box_logical_h = 0; + + int32_t required_pixel_w = (int32_t)(node->target_width * scale + 0.5f); + int32_t required_pixel_h = (int32_t)(node->target_height * scale + 0.5f); + if (required_pixel_w < 1) + required_pixel_w = 1; + if (required_pixel_h < 1) + required_pixel_h = 1; + + bool surface_size_changed = (!node->surface) || + (node->surface_pixel_w != required_pixel_w) || + (node->surface_pixel_h != required_pixel_h); + + if (surface_size_changed) { + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } + if (node->surface) { + cairo_surface_destroy(node->surface); + node->surface = NULL; + } + node->surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, required_pixel_w, required_pixel_h); + node->surface_pixel_w = required_pixel_w; + node->surface_pixel_h = required_pixel_h; + } + + cairo_t *cr = cairo_create(node->surface); + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + double border_phys = node->border_width * scale; + double bg_x = border_phys; + double bg_y = border_phys; + double bg_w = box_logical_w * scale; + double bg_h = box_logical_h * scale; + + double radius; + if (node->corner_radius < 0) { + radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0; + } else { + radius = node->corner_radius * scale; + } + if (radius > bg_w / 2.0) + radius = bg_w / 2.0; + if (radius > bg_h / 2.0) + radius = bg_h / 2.0; + + const float *active_bg = + node->focused ? node->focus_bg_color : node->bg_color; + const float *active_fg = + node->focused ? node->focus_fg_color : node->fg_color; + + bool draw_bg = (active_bg[3] > 0.0f); + bool draw_border = + (node->border_width > 0) && (node->border_color[3] > 0.0f); + + if (draw_bg) { + cairo_set_source_rgba(cr, active_bg[0], active_bg[1], active_bg[2], + active_bg[3]); + if (radius > 0.0) { + draw_rounded_rect(cr, bg_x, bg_y, bg_w, bg_h, radius); + cairo_fill(cr); + } else { + cairo_rectangle(cr, bg_x, bg_y, bg_w, bg_h); + cairo_fill(cr); + } + } + + int32_t text_area_logical_w = box_logical_w - 2 * node->padding_x; + int32_t text_area_logical_h = box_logical_h - 2 * node->padding_y; + if (text_area_logical_w > 0 && text_area_logical_h > 0) { + cairo_save(cr); + + double text_x = (node->border_width + node->padding_x) * scale; + double text_y = (node->border_width + node->padding_y) * scale; + double text_area_w = text_area_logical_w * scale; + double text_area_h = text_area_logical_h * scale; + + PangoContext *ctx = pango_cairo_create_context(cr); + pango_cairo_context_set_resolution(ctx, 96.0 * scale); + PangoLayout *layout = pango_layout_new(ctx); + PangoFontDescription *desc = get_cached_font_desc(node->font_desc); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, safe_text, -1); + + pango_layout_set_wrap(layout, PANGO_WRAP_NONE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + pango_layout_set_width(layout, (int)(text_area_w * PANGO_SCALE)); + + int text_pixel_w, text_pixel_h; + pango_layout_get_pixel_size(layout, &text_pixel_w, &text_pixel_h); + double y_offset = (text_area_h - text_pixel_h) / 2.0; + if (y_offset < 0) + y_offset = 0; + + cairo_translate(cr, text_x, text_y + y_offset); + + cairo_set_source_rgba(cr, active_fg[0], active_fg[1], active_fg[2], + active_fg[3]); + pango_cairo_show_layout(cr, layout); + + g_object_unref(layout); + g_object_unref(ctx); + cairo_restore(cr); + } + + if (draw_border) { + cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1], + node->border_color[2], node->border_color[3]); + cairo_set_line_width(cr, border_phys); + + double half_lw = border_phys * 0.5; + double bx = bg_x - half_lw; + double by = bg_y - half_lw; + double bw = bg_w + border_phys; + double bh = bg_h + border_phys; + + if (radius > 0.0) { + double outer_radius = radius + half_lw; + if (outer_radius < 0.0) + outer_radius = 0.0; + draw_rounded_rect(cr, bx, by, bw, bh, outer_radius); + } else { + cairo_rectangle(cr, bx, by, bw, bh); + } + cairo_stroke(cr); + } + + cairo_surface_flush(node->surface); + cairo_destroy(cr); + + if (node->buffer) { + wlr_buffer_drop(&node->buffer->base); + node->buffer = NULL; + } + + struct mango_text_buffer *buf = calloc(1, sizeof(*buf)); + if (!buf) + return; + + wlr_buffer_init(&buf->base, &text_buffer_impl, node->surface_pixel_w, + node->surface_pixel_h); + buf->surface = node->surface; + node->buffer = buf; + + wlr_scene_buffer_set_buffer(node->scene_buffer, &buf->base); + + node->logical_width = node->target_width; + node->logical_height = node->target_height; + wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width, + node->logical_height); +} + +void mango_titlebar_node_set_focus(struct mango_titlebar_node *node, + bool focused) { + if (!node || node->focused == focused) + return; + node->focused = focused; + if (node->last_text) { + float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; + mango_titlebar_node_update(node, node->last_text, scale); + } +} + +void mango_titlebar_node_set_colors(struct mango_titlebar_node *node, + const float fg[4], const float bg[4]) { + if (!node) + return; + + memcpy(node->fg_color, fg, sizeof(node->fg_color)); + memcpy(node->bg_color, bg, sizeof(node->bg_color)); + + if (!node->focused && node->last_text) { + float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; + mango_titlebar_node_update(node, node->last_text, scale); + } +} + +void mango_text_node_apply_config(struct mango_text_node *node, + const TextDrawData *data) { + if (!node || !data) + return; + + memcpy(node->fg_color, data->fg_color, sizeof(node->fg_color)); + memcpy(node->bg_color, data->bg_color, sizeof(node->bg_color)); + memcpy(node->focus_fg_color, data->focus_fg_color, + sizeof(node->focus_fg_color)); + memcpy(node->focus_bg_color, data->focus_bg_color, + sizeof(node->focus_bg_color)); + memcpy(node->border_color, data->border_color, sizeof(node->border_color)); + node->border_width = data->border_width; + node->corner_radius = data->corner_radius; + node->padding_x = data->padding_x; + node->padding_y = data->padding_y; + + g_free(node->font_desc); + node->font_desc = + g_strdup(data->font_desc ? data->font_desc : "monospace Bold 16"); + + if (node->cached_text && node->cached_scale > 0.0f) { + mango_text_node_update(node, node->cached_text, node->cached_scale); + } +} + +void mango_titlebar_node_apply_config(struct mango_titlebar_node *node, + const TextDrawData *data) { + if (!node || !data) + return; + + memcpy(node->fg_color, data->fg_color, sizeof(node->fg_color)); + memcpy(node->bg_color, data->bg_color, sizeof(node->bg_color)); + memcpy(node->focus_fg_color, data->focus_fg_color, + sizeof(node->focus_fg_color)); + memcpy(node->focus_bg_color, data->focus_bg_color, + sizeof(node->focus_bg_color)); + memcpy(node->border_color, data->border_color, sizeof(node->border_color)); + node->border_width = data->border_width; + node->corner_radius = data->corner_radius; + node->padding_x = data->padding_x; + node->padding_y = data->padding_y; + + g_free(node->font_desc); + node->font_desc = + g_strdup(data->font_desc ? data->font_desc : "monospace Bold 16"); + + if (node->last_text) { + float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; + mango_titlebar_node_update(node, node->last_text, scale); + } } \ No newline at end of file diff --git a/src/draw/text-node.h b/src/draw/text-node.h index 8979a774..32b937a1 100644 --- a/src/draw/text-node.h +++ b/src/draw/text-node.h @@ -1,12 +1,27 @@ -#ifndef MANGO_TEXT_NODE_H -#define MANGO_TEXT_NODE_H +#ifndef TEXT_NODE_H +#define TEXT_NODE_H #include +#include #include -#include +#include +#include #include -// 自定义 wlr_buffer,仅用于包装 cairo surface,不负责 surface 的销毁 +// 原有结构体,假设已存在 +typedef struct { + float fg_color[4]; + float bg_color[4]; + float focus_fg_color[4]; + float focus_bg_color[4]; + float border_color[4]; + int32_t border_width; + int32_t corner_radius; + int32_t padding_x; + int32_t padding_y; + const char *font_desc; +} TextDrawData; + struct mango_text_buffer { struct wlr_buffer base; cairo_surface_t *surface; @@ -14,29 +29,39 @@ struct mango_text_buffer { struct mango_text_node { struct wlr_scene_buffer *scene_buffer; + struct mango_text_buffer *buffer; + cairo_surface_t *surface; + int surface_pixel_w, surface_pixel_h; float fg_color[4]; float bg_color[4]; + float focus_fg_color[4]; + float focus_bg_color[4]; float border_color[4]; int32_t border_width; int32_t corner_radius; - int32_t padding_x, padding_y; + int32_t padding_x; + int32_t padding_y; char *font_desc; + // 缓存 char *cached_text; + char *cached_font_desc; float cached_scale; float cached_fg_color[4]; float cached_bg_color[4]; + float cached_focus_fg_color[4]; + float cached_focus_bg_color[4]; float cached_border_color[4]; - float cached_border_width; - float cached_corner_radius; - float cached_padding_x, cached_padding_y; - char *cached_font_desc; + int32_t cached_border_width; + int32_t cached_corner_radius; + int32_t cached_padding_x; + int32_t cached_padding_y; + bool cached_focused; - cairo_surface_t *surface; - struct mango_text_buffer *buffer; - int32_t surface_pixel_w, surface_pixel_h; + bool focused; + // 测量 cairo_surface_t *measure_surface; cairo_t *measure_cr; PangoContext *measure_context; @@ -47,32 +72,93 @@ struct mango_text_node { int32_t logical_height; }; -typedef struct { +struct mango_titlebar_node { + struct wlr_scene_buffer *scene_buffer; + struct mango_text_buffer *buffer; + cairo_surface_t *surface; + int surface_pixel_w, surface_pixel_h; + + // 初始配置 float fg_color[4]; float bg_color[4]; + float focus_fg_color[4]; + float focus_bg_color[4]; float border_color[4]; int32_t border_width; int32_t corner_radius; int32_t padding_x; int32_t padding_y; - const char *font_desc; -} JumphitData; + char *font_desc; + // 尺寸 + int32_t target_width; + int32_t target_height; + + // 缓存 + char *cached_text; + char *cached_font_desc; + float cached_scale; + float cached_fg_color[4]; + float cached_bg_color[4]; + float cached_focus_fg_color[4]; + float cached_focus_bg_color[4]; + float cached_border_color[4]; + int32_t cached_border_width; + int32_t cached_corner_radius; + int32_t cached_padding_x; + int32_t cached_padding_y; + int32_t cached_target_width; + int32_t cached_target_height; + bool cached_focused; + + bool focused; + + // 上次绘制参数(用于尺寸变化重绘) + char *last_text; + float last_scale; + + // 测量 + cairo_surface_t *measure_surface; + cairo_t *measure_cr; + PangoContext *measure_context; + PangoLayout *measure_layout; + float measure_scale; + + int32_t logical_width; + int32_t logical_height; +}; + +void mango_text_global_finish(void); struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, - JumphitData data); + TextDrawData data); void mango_text_node_destroy(struct mango_text_node *node); -void mango_text_node_update(struct mango_text_node *node, const char *text, - float scale); - void mango_text_node_set_background(struct mango_text_node *node, float r, float g, float b, float a); void mango_text_node_set_border(struct mango_text_node *node, float r, float g, float b, float a, int32_t width, int32_t radius); - void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x, int32_t pad_y); +void mango_text_node_update(struct mango_text_node *node, const char *text, + float scale); -void mango_text_global_finish(void); +struct mango_titlebar_node * +mango_titlebar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, + TextDrawData data, int32_t width, int32_t height); +void mango_titlebar_node_destroy(struct mango_titlebar_node *node); +void mango_titlebar_node_set_size(struct mango_titlebar_node *node, + int32_t width, int32_t height); +void mango_titlebar_node_update(struct mango_titlebar_node *node, + const char *text, float scale); -#endif \ No newline at end of file +void mango_text_node_set_focus(struct mango_text_node *node, bool focused); +void mango_titlebar_node_set_focus(struct mango_titlebar_node *node, + bool focused); + +void mango_titlebar_node_set_colors(struct mango_titlebar_node *node, + const float fg[4], const float bg[4]); +void mango_text_node_apply_config(struct mango_text_node *node, + const TextDrawData *data); +void mango_titlebar_node_apply_config(struct mango_titlebar_node *node, + const TextDrawData *data); +#endif // TEXT_NODE_H \ No newline at end of file diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h index 6144ee40..c5a5869a 100644 --- a/src/fetch/monitor.h +++ b/src/fetch/monitor.h @@ -26,6 +26,14 @@ bool is_scroller_layout(Monitor *m) { return false; } +bool is_monocle_layout(Monitor *m) { + + if (m->pertag->ltidxs[m->pertag->curtag]->id == MONOCLE) + return true; + + return false; +} + bool is_centertile_layout(Monitor *m) { if (m->pertag->ltidxs[m->pertag->curtag]->id == CENTER_TILE) diff --git a/src/layout/arrange.h b/src/layout/arrange.h index f1c97f3a..f1c4967a 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -37,6 +37,21 @@ void set_size_per(Monitor *m, Client *c) { } } +void monocle_set_focus(Client *c, bool focused) { + + if (!c || !c->mon) + return; + + c->is_monocle_hide = !focused; + mango_titlebar_node_set_focus(c->titlebar_node, focused); + wlr_scene_node_set_enabled(&c->scene->node, focused); + + if (!focused) { + c->animation.current = c->animainit_geom = c->animation.initial = + c->pending = c->current = c->geom; + } +} + void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx, int32_t offsety, uint32_t time, int32_t type) { @@ -1128,6 +1143,20 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, set_size_per(m, c); } + if (m->is_jump_mode && !c->text_node) { + client_add_text_node(c); + } + + if (m->pertag->ltidxs[m->pertag->curtag]->id == MONOCLE && + !c->titlebar_node) { + client_add_titlebar_node(c); + } + + if (c->titlebar_node && c->mon == m) { + wlr_scene_node_set_enabled(&c->titlebar_node->scene_buffer->node, + false); + } + if (c->mon == m && (c->isglobal || c->isunglobal)) { c->tags = m->tagset[m->seltags]; } diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 8788c210..4b8fa29d 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -545,32 +545,70 @@ void deck(Monitor *m) { } } -void // 17 -monocle(Monitor *m) { - Client *c = NULL; +void monocle(Monitor *m) { + Client *c, *fc; struct wlr_box geom; - int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + int32_t cur_gapiv = enablegaps ? m->gappiv : 0; + int32_t cur_gapih = enablegaps ? m->gappih : 0; - cur_gappoh = config.smartgaps && m->visible_fake_tiling_clients == 1 - ? 0 - : cur_gappoh; - cur_gappov = config.smartgaps && m->visible_fake_tiling_clients == 1 - ? 0 - : cur_gappov; + if (config.smartgaps && m->visible_fake_tiling_clients == 1) { + cur_gappov = cur_gappoh = cur_gapiv = cur_gapih = 0; + } - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, m) || !ISFAKETILED(c)) + int n = m->visible_fake_tiling_clients; + if (n == 0) + return; + + wl_list_for_each(c, &fstack, flink) { + if (c->iskilling || c->isunglobal || !ISFAKETILED(c)) continue; + if (VISIBLEON(c, m)) { + fc = c; + break; + } + } + + if (n == 1) { geom.x = m->w.x + cur_gappoh; geom.y = m->w.y + cur_gappov; geom.width = m->w.width - 2 * cur_gappoh; geom.height = m->w.height - 2 * cur_gappov; - client_tile_resize(c, geom, 0); + client_tile_resize(fc, geom, 0); + monocle_set_focus(fc, true); + return; + } + + int titlebar_height = config.tab_bar_height; + int title_y = m->w.y + cur_gappov; + int main_y = title_y + titlebar_height + cur_gapiv; + int main_height = + m->w.height - 2 * cur_gappov - 2 * cur_gapiv - titlebar_height; + + int title_area_width = m->w.width - 2 * cur_gappoh; + int tw = (title_area_width - (n - 1) * cur_gapih) / n; + int title_x = m->w.x + cur_gappoh; + + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || !ISFAKETILED(c)) + continue; + + if (c == fc) { + monocle_set_focus(c, true); + } else { + monocle_set_focus(c, false); + } + + geom.x = m->w.x + cur_gappoh; + geom.y = main_y; + geom.width = m->w.width - 2 * cur_gappoh; + geom.height = main_height; + client_tile_resize(c, geom, 0); + + global_draw_titlebar(c, title_x, title_y, tw, titlebar_height); + title_x += tw + cur_gapih; } - if ((c = focustop(m))) - wlr_scene_node_raise_to_top(&c->scene->node); } // 网格布局窗口大小和位置计算 diff --git a/src/mango.c b/src/mango.c index 8da371d1..d81eee58 100644 --- a/src/mango.c +++ b/src/mango.c @@ -166,6 +166,7 @@ enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向 enum { LyrBg, LyrBottom, + LyrDecorate, LyrTile, LyrTop, LyrFadeOut, @@ -174,6 +175,9 @@ enum { LyrBlock, NUM_LAYERS }; /* scene layers */ + +enum mango_node_type { MANGO_TITLE_NODE, MANGO_TEXT_NODE }; + #ifdef XWAYLAND enum { NetWMWindowTypeDialog, @@ -240,6 +244,11 @@ typedef struct { Client *tc; } Arg; +typedef struct { + enum mango_node_type type; + void *node_data; +} MangoNodeData; + typedef struct { uint32_t mod; uint32_t button; @@ -263,8 +272,8 @@ typedef struct { struct wl_list link; struct wlr_input_device *wlr_device; struct libinput_device *libinput_device; - struct wl_listener destroy_listener; // 用于监听设备销毁事件 - void *device_data; // 新增:指向设备特定数据(如 Switch) + struct wl_listener destroy_listener; + void *device_data; } InputDevice; typedef struct { @@ -331,6 +340,7 @@ struct Client { struct wlr_scene_tree *overview_scene_surface; struct mango_text_node *text_node; + struct mango_titlebar_node *titlebar_node; struct wl_list link; struct wl_list flink; struct wl_list fadeout_link; @@ -397,6 +407,7 @@ struct Client { int32_t istagswitching; int32_t isnamedscratchpad; int32_t shield_when_capture; + bool is_monocle_hide; bool is_pending_open_animation; bool is_restoring_from_ov; float scroller_proportion; @@ -851,6 +862,7 @@ static struct wlr_scene_tree * wlr_scene_tree_snapshot(struct wlr_scene_node *node, struct wlr_scene_tree *parent); static bool is_scroller_layout(Monitor *m); +static bool is_monocle_layout(Monitor *m); static bool is_centertile_layout(Monitor *m); static void create_output(struct wlr_backend *backend, void *data); static void get_layout_abbr(char *abbr, const char *full_name); @@ -1283,6 +1295,11 @@ void swallow(Client *c, Client *w) { overview_backup_surface(c); } + if (w->titlebar_node) { + wlr_scene_node_set_enabled(&w->titlebar_node->scene_buffer->node, + false); + } + /* 全局链表替换 */ wl_list_insert(&w->link, &c->link); wl_list_insert(&w->flink, &c->flink); @@ -2390,6 +2407,17 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) { return true; } + // handle click on tile node + struct wlr_scene_node *node = wlr_scene_node_at( + &layers[LyrDecorate]->node, cursor->x, cursor->y, NULL, NULL); + if (node && node->data) { + MangoNodeData *mangonodedata = (MangoNodeData *)node->data; + if (mangonodedata->type == MANGO_TITLE_NODE) { + Client *c = mangonodedata->node_data; + focusclient(c, 1); + } + } + // 当鼠标焦点在layer上的时候,不检测虚拟键盘的mod状态, // 避免layer虚拟键盘锁死mod按键状态 hard_keyboard = &kb_group->wlr_group->keyboard; @@ -3810,17 +3838,17 @@ void focusclient(Client *c, int32_t lift) { // decide whether need to re-arrange - if (c && selmon->prevsel && - (selmon->prevsel->tags & selmon->tagset[selmon->seltags]) && - (c->tags & selmon->tagset[selmon->seltags]) && !c->isfloating && - is_scroller_layout(selmon)) { - arrange(selmon, false, false); - } - // change focus link position wl_list_remove(&c->flink); wl_list_insert(&fstack, &c->flink); + if (c && selmon->prevsel && + (selmon->prevsel->tags & selmon->tagset[selmon->seltags]) && + (c->tags & selmon->tagset[selmon->seltags]) && !c->isfloating && + (is_scroller_layout(selmon) || is_monocle_layout(selmon))) { + arrange(selmon, false, false); + } + // change border color c->isurgent = 0; } @@ -4319,6 +4347,7 @@ void locksession(struct wl_listener *listener, void *data) { void init_client_properties(Client *c) { c->grid_col_per = 1.0f; c->grid_row_per = 1.0f; + c->is_monocle_hide = false; c->overview_scene_surface = NULL; c->drop_direction = UNDIR; c->enable_drop_area_draw = false; @@ -4490,9 +4519,6 @@ mapnotify(struct wl_listener *listener, void *data) { : config.bordercolor); c->border[i]->node.data = c; } - c->text_node = mango_text_node_create(c->scene, config.jumhitdata); - wlr_scene_node_lower_to_bottom(&c->text_node->scene_buffer->node); - wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, false); for (i = 0; i < 2; i++) { c->splitindicator[i] = wlr_scene_rect_create( @@ -6569,6 +6595,7 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->stack_proportion = 0.0f; mango_text_node_destroy(c->text_node); + mango_titlebar_node_destroy(c->titlebar_node); wlr_scene_node_destroy(&c->scene->node); printstatus(IPC_WATCH_ARRANGGE); motionnotify(0, NULL, 0, 0, 0, 0); @@ -6716,6 +6743,7 @@ void updatetitle(struct wl_listener *listener, void *data) { const char *title; title = client_get_title(c); + mango_titlebar_node_update(c->titlebar_node, title, 1.0); if (title && c->foreign_toplevel) wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title); if (title && c->ext_foreign_toplevel) { From 27df2ea469f9312288b12af39ac4be5c09cc530d Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 17 Jun 2026 23:21:01 +0800 Subject: [PATCH 06/13] fix: fix monocle window height --- src/layout/horizontal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 4b8fa29d..1634d849 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -584,7 +584,7 @@ void monocle(Monitor *m) { int title_y = m->w.y + cur_gappov; int main_y = title_y + titlebar_height + cur_gapiv; int main_height = - m->w.height - 2 * cur_gappov - 2 * cur_gapiv - titlebar_height; + m->w.height - 2 * cur_gappov - cur_gapiv - titlebar_height; int title_area_width = m->w.width - 2 * cur_gappoh; int tw = (title_area_width - (n - 1) * cur_gapih) / n; From 46f2dd293bdc4d580cd7d02bd6741c8edcc17db9 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 17 Jun 2026 23:24:55 +0800 Subject: [PATCH 07/13] opt: direction find exclude monocle hide client --- src/fetch/client.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fetch/client.h b/src/fetch/client.h index fb50c798..49b45490 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -190,6 +190,8 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, continue; if (c->isunglobal) continue; + if (c->is_monocle_hide) + continue; if (!config.focus_cross_monitor && c->mon != tc->mon) continue; if (!(c->tags & c->mon->tagset[c->mon->seltags])) @@ -310,7 +312,7 @@ Client *direction_select(const Arg *arg) { Client *focustop(Monitor *m) { Client *c = NULL; wl_list_for_each(c, &fstack, flink) { - if (c->iskilling || c->isunglobal) + if (c->iskilling || c->isunglobal || c->is_monocle_hide) continue; if (VISIBLEON(c, m)) return c; From 75e88f0faf7d90072fcc316340d786dd7593c79f Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 18 Jun 2026 10:18:16 +0800 Subject: [PATCH 08/13] opt: tab_bar_decorate and jump_label_decorate config separate --- docs/visuals/theming.md | 40 +++++-- src/action/client.h | 20 ++-- src/animation/client.h | 8 +- src/config/parse_config.h | 234 ++++++++++++++++++++++++++++---------- src/draw/text-node.c | 58 +++++----- src/draw/text-node.h | 52 ++++----- src/layout/arrange.h | 14 +-- src/layout/overview.h | 16 +-- src/mango.c | 16 +-- 9 files changed, 292 insertions(+), 166 deletions(-) diff --git a/docs/visuals/theming.md b/docs/visuals/theming.md index 928b1d24..2f9993be 100644 --- a/docs/visuals/theming.md +++ b/docs/visuals/theming.md @@ -14,7 +14,6 @@ Control the sizing of window borders and gaps. | `gappiv` | `5` | Vertical inner gap. | | `gappoh` | `10` | Horizontal outer gap (between windows and screen edges). | | `gappov` | `10` | Vertical outer gap. | -| `tab_bar_height` | `50` | Height of the tab bar for monocle layout. | ## Colors @@ -56,16 +55,35 @@ You can also color-code windows based on their state: ### Overview Jump Mode | Setting | Default | Description | | :--- | :--- | :--- | -| `text_decorate_fg_color` | `0xc4939dff` | label text color. | -| `text_decorate_bg_color` | `0x201b14ff` | label background color.| -| `text_decorate_focus_fg_color` | `0x201b14ff` | label text color for focus. | -| `text_decorate_focus_bg_color` | `0xc4939dff` | label background color for focus.| -| `text_decorate_border_color` | `0x8BAA9Bff` | label border color.| -| `text_decorate_border_width` | `4` | label border width.| -| `text_decorate_corner_radius` | `5` | label corner radius.| -| `text_decorate_padding_x` | `10` | label horizontal padding.| -| `text_decorate_padding_y` | `10` | label vertical padding.| -| `text_decorate_font_desc` | `monospace Bold 16` | label font set.| +| `jump_label_decorate_fg_color` | `0xc4939dff` | text color. | +| `jump_label_decorate_bg_color` | `0x201b14ff` | background color.| +| `jump_label_decorate_focus_fg_color` | `0x201b14ff` | text color for focus. | +| `jump_label_decorate_focus_bg_color` | `0xc4939dff` | background color for focus.| +| `jump_label_decorate_border_color` | `0x8BAA9Bff` | border color.| +| `jump_label_decorate_border_width` | `4` | border width.| +| `jump_label_decorate_corner_radius` | `5` | corner radius.| +| `jump_label_decorate_padding_x` | `10` | horizontal padding.| +| `jump_label_decorate_padding_y` | `10` | vertical padding.| +| `jump_label_decorate_font_desc` | `monospace Bold 16` | font set.| + +### Tab Bar For Monocle Layout +| Setting | Default | Description | +| :--- | :--- | :--- | +| `tab_bar_height` | `50` | Height of the tab bar for monocle layout. | +| `tab_bar_decorate_fg_color` | `0xc4939dff` | text color. +| `tab_bar_decorate_bg_color` | `0x201b14ff` | background color.| +| `tab_bar_decorate_focus_fg_color` | `0x201b14ff` | text color for focus. | +| `tab_bar_decorate_focus_bg_color` | `0xc4939dff` | background color for focus.| +| `tab_bar_decorate_border_color` | `0x8BAA9Bff` | border color.| +| `tab_bar_decorate_border_width` | `4` | border width.| +| `tab_bar_decorate_corner_radius` | `5` | corner radius.| +| `tab_bar_decorate_padding_x` | `0` | horizontal padding.| +| `tab_bar_decorate_padding_y` | `0` | vertical padding.| +| `tab_bar_decorate_font_desc` | `monospace Bold 16` | font set.| + +## Borders + +Control the appearance of window borders. ## Cursor Theme diff --git a/src/action/client.h b/src/action/client.h index d13cb5c6..80327487 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -97,20 +97,20 @@ void client_pending_force_kill(Client *c) { kill(c->pid, SIGKILL); } -void client_add_text_node(Client *c) { - c->text_node = mango_text_node_create(c->scene, config.textdata); - wlr_scene_node_lower_to_bottom(&c->text_node->scene_buffer->node); - wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, false); +void client_add_jump_label_node(Client *c) { + c->jump_label_node = mango_jump_label_node_create(c->scene, config.jumplabeldata); + wlr_scene_node_lower_to_bottom(&c->jump_label_node->scene_buffer->node); + wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, false); } -void client_add_titlebar_node(Client *c) { +void client_add_tab_bar_node(Client *c) { MangoNodeData *mangonodedata = ecalloc(1, sizeof(MangoNodeData)); mangonodedata->node_data = c; mangonodedata->type = MANGO_TITLE_NODE; - c->titlebar_node = mango_titlebar_node_create( - mangonodedata, layers[LyrDecorate], config.textdata, 0, 0); - wlr_scene_node_lower_to_bottom(&c->titlebar_node->scene_buffer->node); - wlr_scene_node_set_enabled(&c->titlebar_node->scene_buffer->node, false); - mango_titlebar_node_update(c->titlebar_node, client_get_title(c), 1.0); + c->tab_bar_node = mango_tab_bar_node_create( + mangonodedata, layers[LyrDecorate], config.tabdata, 0, 0); + wlr_scene_node_lower_to_bottom(&c->tab_bar_node->scene_buffer->node); + wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); + mango_tab_bar_node_update(c->tab_bar_node, client_get_title(c), 1.0); } diff --git a/src/animation/client.h b/src/animation/client.h index 1c77ab3c..0e21f236 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -291,12 +291,12 @@ void apply_shield(Client *c, struct wlr_box clip_box) { void global_draw_titlebar(Client *c, int32_t x, int32_t y, int32_t width, int32_t height) { - if (!c->titlebar_node) + if (!c->tab_bar_node) return; - wlr_scene_node_set_position(&c->titlebar_node->scene_buffer->node, x, y); - wlr_scene_node_set_enabled(&c->titlebar_node->scene_buffer->node, true); - mango_titlebar_node_set_size(c->titlebar_node, width, height); + wlr_scene_node_set_position(&c->tab_bar_node->scene_buffer->node, x, y); + wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, true); + mango_tab_bar_node_set_size(c->tab_bar_node, width, height); } void apply_split_border(Client *c, bool hit_no_border) { diff --git a/src/config/parse_config.h b/src/config/parse_config.h index a34ca2e9..ef204b11 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -388,7 +388,8 @@ typedef struct { struct xkb_context *ctx; struct xkb_keymap *keymap; - TextDrawData textdata; + DecorateDrawData jumplabeldata; + DecorateDrawData tabdata; } Config; typedef int32_t (*FuncType)(const Arg *); @@ -1707,76 +1708,146 @@ bool parse_option(Config *config, char *key, char *value) { config->cursor_size = atoi(value); } else if (strcmp(key, "cursor_theme") == 0) { config->cursor_theme = strdup(value); - } else if (strcmp(key, "text_decorate_font_desc") == 0) { - config->textdata.font_desc = strdup(value); - } else if (strcmp(key, "text_decorate_fg_color") == 0) { + } else if (strcmp(key, "tab_bar_decorate_font_desc") == 0) { + config->tabdata.font_desc = strdup(value); + } else if (strcmp(key, "tab_bar_decorate_fg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "text_decorate_fg_color " + "tab_bar_decorate_fg_color " "format: %s\n", value); return false; } else { - convert_hex_to_rgba(config->textdata.fg_color, color); + convert_hex_to_rgba(config->tabdata.fg_color, color); } - } else if (strcmp(key, "text_decorate_bg_color") == 0) { + } else if (strcmp(key, "tab_bar_decorate_bg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "text_decorate_bg_color " + "tab_bar_decorate_bg_color " "format: %s\n", value); return false; } else { - convert_hex_to_rgba(config->textdata.bg_color, color); + convert_hex_to_rgba(config->tabdata.bg_color, color); } - } else if (strcmp(key, "text_decorate_focus_fg_color") == 0) { + } else if (strcmp(key, "tab_bar_decorate_focus_fg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "text_decorate_focus_fg_color " + "tab_bar_decorate_focus_fg_color " "format: %s\n", value); return false; } else { - convert_hex_to_rgba(config->textdata.focus_fg_color, color); + convert_hex_to_rgba(config->tabdata.focus_fg_color, color); } - } else if (strcmp(key, "text_decorate_focus_bg_color") == 0) { + } else if (strcmp(key, "tab_bar_decorate_focus_bg_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "text_decorate_focus_bg_color " + "tab_bar_decorate_focus_bg_color " "format: %s\n", value); return false; } else { - convert_hex_to_rgba(config->textdata.focus_bg_color, color); + convert_hex_to_rgba(config->tabdata.focus_bg_color, color); } - } else if (strcmp(key, "text_decorate_border_color") == 0) { + } else if (strcmp(key, "tab_bar_decorate_border_color") == 0) { int64_t color = parse_color(value); if (color == -1) { fprintf(stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid " - "text_decorate_border_color " + "tab_bar_decorate_border_color " "format: %s\n", value); return false; } else { - convert_hex_to_rgba(config->textdata.border_color, color); + convert_hex_to_rgba(config->tabdata.border_color, color); } - } else if (strcmp(key, "text_decorate_border_width") == 0) { - config->textdata.border_width = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "text_decorate_corner_radius") == 0) { - config->textdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "text_decorate_padding_x") == 0) { - config->textdata.padding_x = CLAMP_INT(atoi(value), 0, 100); - } else if (strcmp(key, "text_decorate_padding_y") == 0) { - config->textdata.padding_y = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "tab_bar_decorate_border_width") == 0) { + config->tabdata.border_width = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "tab_bar_decorate_corner_radius") == 0) { + config->tabdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "tab_bar_decorate_padding_x") == 0) { + config->tabdata.padding_x = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "tab_bar_decorate_padding_y") == 0) { + config->tabdata.padding_y = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "jump_label_decorate_font_desc") == 0) { + config->jumplabeldata.font_desc = strdup(value); + } else if (strcmp(key, "jump_label_decorate_fg_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "jump_label_decorate_fg_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumplabeldata.fg_color, color); + } + } else if (strcmp(key, "jump_label_decorate_bg_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "jump_label_decorate_bg_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumplabeldata.bg_color, color); + } + } else if (strcmp(key, "jump_label_decorate_focus_fg_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "jump_label_decorate_focus_fg_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumplabeldata.focus_fg_color, color); + } + } else if (strcmp(key, "jump_label_decorate_focus_bg_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "jump_label_decorate_focus_bg_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumplabeldata.focus_bg_color, color); + } + } else if (strcmp(key, "jump_label_decorate_border_color") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid " + "jump_label_decorate_border_color " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->jumplabeldata.border_color, color); + } + } else if (strcmp(key, "jump_label_decorate_border_width") == 0) { + config->jumplabeldata.border_width = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "jump_label_decorate_corner_radius") == 0) { + config->jumplabeldata.corner_radius = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "jump_label_decorate_padding_x") == 0) { + config->jumplabeldata.padding_x = CLAMP_INT(atoi(value), 0, 100); + } else if (strcmp(key, "jump_label_decorate_padding_y") == 0) { + config->jumplabeldata.padding_y = CLAMP_INT(atoi(value), 0, 100); } else if (strcmp(key, "disable_while_typing") == 0) { config->disable_while_typing = atoi(value); } else if (strcmp(key, "left_handed") == 0) { @@ -3238,9 +3309,14 @@ void free_config(void) { config.cursor_theme = NULL; } - if (config.textdata.font_desc) { - free((void *)config.textdata.font_desc); - config.textdata.font_desc = NULL; + if (config.jumplabeldata.font_desc) { + free((void *)config.jumplabeldata.font_desc); + config.jumplabeldata.font_desc = NULL; + } + + if (config.tabdata.font_desc) { + free((void *)config.tabdata.font_desc); + config.tabdata.font_desc = NULL; } if (config.tablet_map_to_mon) { @@ -3410,12 +3486,19 @@ void override_config(void) { config.unfocused_opacity = CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f); - config.textdata.border_width = - CLAMP_INT(config.textdata.border_width, 0, 100); - config.textdata.corner_radius = - CLAMP_INT(config.textdata.corner_radius, 0, 100); - config.textdata.padding_x = CLAMP_INT(config.textdata.padding_x, 0, 100); - config.textdata.padding_y = CLAMP_INT(config.textdata.padding_y, 0, 100); + config.tabdata.border_width = + CLAMP_INT(config.tabdata.border_width, 0, 100); + config.tabdata.corner_radius = + CLAMP_INT(config.tabdata.corner_radius, 0, 100); + config.tabdata.padding_x = CLAMP_INT(config.tabdata.padding_x, 0, 100); + config.tabdata.padding_y = CLAMP_INT(config.tabdata.padding_y, 0, 100); + + config.jumplabeldata.border_width = + CLAMP_INT(config.jumplabeldata.border_width, 0, 100); + config.jumplabeldata.corner_radius = + CLAMP_INT(config.jumplabeldata.corner_radius, 0, 100); + config.jumplabeldata.padding_x = CLAMP_INT(config.jumplabeldata.padding_x, 0, 100); + config.jumplabeldata.padding_y = CLAMP_INT(config.jumplabeldata.padding_y, 0, 100); } void set_value_default() { @@ -3568,30 +3651,55 @@ void set_value_default() { config.animation_curve_opafadeout[2] = 0.5; config.animation_curve_opafadeout[3] = 0.5; - config.textdata.fg_color[0] = 0xc4 / 255.0f; - config.textdata.fg_color[1] = 0x93 / 255.0f; - config.textdata.fg_color[2] = 0x9d / 255.0f; - config.textdata.fg_color[3] = 1.0f; - config.textdata.bg_color[0] = 0x32 / 255.0f; - config.textdata.bg_color[1] = 0x32 / 255.0f; - config.textdata.bg_color[2] = 0x32 / 255.0f; - config.textdata.bg_color[3] = 1.0f; - config.textdata.focus_fg_color[0] = 0xed / 255.0f; - config.textdata.focus_fg_color[1] = 0xa6 / 255.0f; - config.textdata.focus_fg_color[2] = 0xb4 / 255.0f; - config.textdata.focus_fg_color[3] = 1.0f; - config.textdata.focus_bg_color[0] = 0x4e / 255.0f; - config.textdata.focus_bg_color[1] = 0x45 / 255.0f; - config.textdata.focus_bg_color[2] = 0x3c / 255.0f; - config.textdata.focus_bg_color[3] = 1.0f; - config.textdata.border_color[0] = 0x8b / 255.0f; - config.textdata.border_color[1] = 0xaa / 255.0f; - config.textdata.border_color[2] = 0x9b / 255.0f; - config.textdata.border_color[3] = 1.0f; - config.textdata.border_width = 4; - config.textdata.corner_radius = 5; - config.textdata.padding_x = 10; - config.textdata.padding_y = 10; + config.tabdata.fg_color[0] = 0xc4 / 255.0f; + config.tabdata.fg_color[1] = 0x93 / 255.0f; + config.tabdata.fg_color[2] = 0x9d / 255.0f; + config.tabdata.fg_color[3] = 1.0f; + config.tabdata.bg_color[0] = 0x32 / 255.0f; + config.tabdata.bg_color[1] = 0x32 / 255.0f; + config.tabdata.bg_color[2] = 0x32 / 255.0f; + config.tabdata.bg_color[3] = 1.0f; + config.tabdata.focus_fg_color[0] = 0xed / 255.0f; + config.tabdata.focus_fg_color[1] = 0xa6 / 255.0f; + config.tabdata.focus_fg_color[2] = 0xb4 / 255.0f; + config.tabdata.focus_fg_color[3] = 1.0f; + config.tabdata.focus_bg_color[0] = 0x4e / 255.0f; + config.tabdata.focus_bg_color[1] = 0x45 / 255.0f; + config.tabdata.focus_bg_color[2] = 0x3c / 255.0f; + config.tabdata.focus_bg_color[3] = 1.0f; + config.tabdata.border_color[0] = 0x8b / 255.0f; + config.tabdata.border_color[1] = 0xaa / 255.0f; + config.tabdata.border_color[2] = 0x9b / 255.0f; + config.tabdata.border_color[3] = 1.0f; + config.tabdata.border_width = 4; + config.tabdata.corner_radius = 5; + config.tabdata.padding_x = 0; + config.tabdata.padding_y = 0; + + config.jumplabeldata.fg_color[0] = 0xc4 / 255.0f; + config.jumplabeldata.fg_color[1] = 0x93 / 255.0f; + config.jumplabeldata.fg_color[2] = 0x9d / 255.0f; + config.jumplabeldata.fg_color[3] = 1.0f; + config.jumplabeldata.bg_color[0] = 0x32 / 255.0f; + config.jumplabeldata.bg_color[1] = 0x32 / 255.0f; + config.jumplabeldata.bg_color[2] = 0x32 / 255.0f; + config.jumplabeldata.bg_color[3] = 1.0f; + config.jumplabeldata.focus_fg_color[0] = 0xed / 255.0f; + config.jumplabeldata.focus_fg_color[1] = 0xa6 / 255.0f; + config.jumplabeldata.focus_fg_color[2] = 0xb4 / 255.0f; + config.jumplabeldata.focus_fg_color[3] = 1.0f; + config.jumplabeldata.focus_bg_color[0] = 0x4e / 255.0f; + config.jumplabeldata.focus_bg_color[1] = 0x45 / 255.0f; + config.jumplabeldata.focus_bg_color[2] = 0x3c / 255.0f; + config.jumplabeldata.focus_bg_color[3] = 1.0f; + config.jumplabeldata.border_color[0] = 0x8b / 255.0f; + config.jumplabeldata.border_color[1] = 0xaa / 255.0f; + config.jumplabeldata.border_color[2] = 0x9b / 255.0f; + config.jumplabeldata.border_color[3] = 1.0f; + config.jumplabeldata.border_width = 4; + config.jumplabeldata.corner_radius = 5; + config.jumplabeldata.padding_x = 10; + config.jumplabeldata.padding_y = 10; config.rootcolor[0] = 0x32 / 255.0f; config.rootcolor[1] = 0x32 / 255.0f; @@ -3705,7 +3813,8 @@ bool parse_config(void) { config.tag_rules = NULL; config.tag_rules_count = 0; config.cursor_theme = NULL; - config.textdata.font_desc = NULL; + config.jumplabeldata.font_desc = NULL; + config.tabdata.font_desc = NULL; config.tablet_map_to_mon = NULL; strcpy(config.keymode, "default"); @@ -3843,9 +3952,8 @@ void reapply_property(void) { c->bw = config.borderpx; } - mango_text_node_apply_config(c->text_node, &config.textdata); - mango_titlebar_node_apply_config(c->titlebar_node, - &config.textdata); + mango_jump_label_node_apply_config(c->jump_label_node, &config.jumplabeldata); + mango_tab_bar_node_apply_config(c->tab_bar_node, &config.tabdata); wlr_scene_rect_set_color(c->droparea, config.dropcolor); wlr_scene_rect_set_color(c->splitindicator[0], config.splitcolor); diff --git a/src/draw/text-node.c b/src/draw/text-node.c index 32164fbe..048b7d25 100644 --- a/src/draw/text-node.c +++ b/src/draw/text-node.c @@ -59,9 +59,9 @@ static const struct wlr_buffer_impl text_buffer_impl = { .end_data_ptr_access = text_buffer_end_data_ptr_access, }; -struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, - TextDrawData data) { - struct mango_text_node *node = calloc(1, sizeof(*node)); +struct mango_jump_label_node *mango_jump_label_node_create(struct wlr_scene_tree *parent, + DecorateDrawData data) { + struct mango_jump_label_node *node = calloc(1, sizeof(*node)); if (!node) return NULL; @@ -103,7 +103,7 @@ struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, return node; } -void mango_text_node_destroy(struct mango_text_node *node) { +void mango_jump_label_node_destroy(struct mango_jump_label_node *node) { if (!node) return; @@ -135,7 +135,7 @@ void mango_text_node_destroy(struct mango_text_node *node) { free(node); } -void mango_text_node_set_background(struct mango_text_node *node, float r, +void mango_jump_label_node_set_background(struct mango_jump_label_node *node, float r, float g, float b, float a) { if (!node) return; @@ -145,7 +145,7 @@ void mango_text_node_set_background(struct mango_text_node *node, float r, node->bg_color[3] = a; } -void mango_text_node_set_border(struct mango_text_node *node, float r, float g, +void mango_jump_label_node_set_border(struct mango_jump_label_node *node, float r, float g, float b, float a, int32_t width, int32_t radius) { if (!node) @@ -158,7 +158,7 @@ void mango_text_node_set_border(struct mango_text_node *node, float r, float g, node->corner_radius = radius; } -void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x, +void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, int32_t pad_x, int32_t pad_y) { if (!node) return; @@ -166,7 +166,7 @@ void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x, node->padding_y = pad_y >= 0 ? pad_y : 0; } -static void get_text_pixel_size(struct mango_text_node *node, const char *text, +static void get_text_pixel_size(struct mango_jump_label_node *node, const char *text, float scale, int32_t *out_w, int32_t *out_h) { if (node->measure_scale != scale) { pango_cairo_context_set_resolution(node->measure_context, 96.0 * scale); @@ -191,7 +191,7 @@ static void draw_rounded_rect(cairo_t *cr, double x, double y, double w, cairo_close_path(cr); } -void mango_text_node_update(struct mango_text_node *node, const char *text, +void mango_jump_label_node_update(struct mango_jump_label_node *node, const char *text, float scale) { if (!node || !text) return; @@ -405,20 +405,20 @@ void mango_text_node_update(struct mango_text_node *node, const char *text, node->logical_height); } -void mango_text_node_set_focus(struct mango_text_node *node, bool focused) { +void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, bool focused) { if (!node || node->focused == focused) return; node->focused = focused; // 使用缓存的文本和缩放触发重绘(如果无文本则不重绘) if (node->cached_text && node->cached_scale > 0.0f) { - mango_text_node_update(node, node->cached_text, node->cached_scale); + mango_jump_label_node_update(node, node->cached_text, node->cached_scale); } } -struct mango_titlebar_node * -mango_titlebar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, - TextDrawData data, int32_t width, int32_t height) { - struct mango_titlebar_node *node = calloc(1, sizeof(*node)); +struct mango_tab_bar_node * +mango_tab_bar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, + DecorateDrawData data, int32_t width, int32_t height) { + struct mango_tab_bar_node *node = calloc(1, sizeof(*node)); if (!node) return NULL; @@ -462,7 +462,7 @@ mango_titlebar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, return node; } -void mango_titlebar_node_destroy(struct mango_titlebar_node *node) { +void mango_tab_bar_node_destroy(struct mango_tab_bar_node *node) { if (!node) return; @@ -498,7 +498,7 @@ void mango_titlebar_node_destroy(struct mango_titlebar_node *node) { free(node); } -void mango_titlebar_node_set_size(struct mango_titlebar_node *node, +void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, int32_t width, int32_t height) { if (!node) return; @@ -517,10 +517,10 @@ void mango_titlebar_node_set_size(struct mango_titlebar_node *node, const char *redraw_text = node->last_text ? node->last_text : ""; float redraw_scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_titlebar_node_update(node, redraw_text, redraw_scale); + mango_tab_bar_node_update(node, redraw_text, redraw_scale); } -void mango_titlebar_node_update(struct mango_titlebar_node *node, +void mango_tab_bar_node_update(struct mango_tab_bar_node *node, const char *text, float scale) { if (!node || !text) return; @@ -760,18 +760,18 @@ void mango_titlebar_node_update(struct mango_titlebar_node *node, node->logical_height); } -void mango_titlebar_node_set_focus(struct mango_titlebar_node *node, +void mango_tab_bar_node_set_focus(struct mango_tab_bar_node *node, bool focused) { if (!node || node->focused == focused) return; node->focused = focused; if (node->last_text) { float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_titlebar_node_update(node, node->last_text, scale); + mango_tab_bar_node_update(node, node->last_text, scale); } } -void mango_titlebar_node_set_colors(struct mango_titlebar_node *node, +void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, const float fg[4], const float bg[4]) { if (!node) return; @@ -781,12 +781,12 @@ void mango_titlebar_node_set_colors(struct mango_titlebar_node *node, if (!node->focused && node->last_text) { float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_titlebar_node_update(node, node->last_text, scale); + mango_tab_bar_node_update(node, node->last_text, scale); } } -void mango_text_node_apply_config(struct mango_text_node *node, - const TextDrawData *data) { +void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, + const DecorateDrawData *data) { if (!node || !data) return; @@ -807,12 +807,12 @@ void mango_text_node_apply_config(struct mango_text_node *node, g_strdup(data->font_desc ? data->font_desc : "monospace Bold 16"); if (node->cached_text && node->cached_scale > 0.0f) { - mango_text_node_update(node, node->cached_text, node->cached_scale); + mango_jump_label_node_update(node, node->cached_text, node->cached_scale); } } -void mango_titlebar_node_apply_config(struct mango_titlebar_node *node, - const TextDrawData *data) { +void mango_tab_bar_node_apply_config(struct mango_tab_bar_node *node, + const DecorateDrawData *data) { if (!node || !data) return; @@ -834,6 +834,6 @@ void mango_titlebar_node_apply_config(struct mango_titlebar_node *node, if (node->last_text) { float scale = node->last_scale > 0.0f ? node->last_scale : 1.0f; - mango_titlebar_node_update(node, node->last_text, scale); + mango_tab_bar_node_update(node, node->last_text, scale); } } \ No newline at end of file diff --git a/src/draw/text-node.h b/src/draw/text-node.h index 32b937a1..ae9ac604 100644 --- a/src/draw/text-node.h +++ b/src/draw/text-node.h @@ -1,5 +1,5 @@ -#ifndef TEXT_NODE_H -#define TEXT_NODE_H +#ifndef jump_label_node_H +#define jump_label_node_H #include #include @@ -20,14 +20,14 @@ typedef struct { int32_t padding_x; int32_t padding_y; const char *font_desc; -} TextDrawData; +} DecorateDrawData; struct mango_text_buffer { struct wlr_buffer base; cairo_surface_t *surface; }; -struct mango_text_node { +struct mango_jump_label_node { struct wlr_scene_buffer *scene_buffer; struct mango_text_buffer *buffer; cairo_surface_t *surface; @@ -72,7 +72,7 @@ struct mango_text_node { int32_t logical_height; }; -struct mango_titlebar_node { +struct mango_tab_bar_node { struct wlr_scene_buffer *scene_buffer; struct mango_text_buffer *buffer; cairo_surface_t *surface; @@ -129,36 +129,36 @@ struct mango_titlebar_node { }; void mango_text_global_finish(void); -struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent, - TextDrawData data); -void mango_text_node_destroy(struct mango_text_node *node); -void mango_text_node_set_background(struct mango_text_node *node, float r, +struct mango_jump_label_node *mango_jump_label_node_create(struct wlr_scene_tree *parent, + DecorateDrawData data); +void mango_jump_label_node_destroy(struct mango_jump_label_node *node); +void mango_jump_label_node_set_background(struct mango_jump_label_node *node, float r, float g, float b, float a); -void mango_text_node_set_border(struct mango_text_node *node, float r, float g, +void mango_jump_label_node_set_border(struct mango_jump_label_node *node, float r, float g, float b, float a, int32_t width, int32_t radius); -void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x, +void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, int32_t pad_x, int32_t pad_y); -void mango_text_node_update(struct mango_text_node *node, const char *text, +void mango_jump_label_node_update(struct mango_jump_label_node *node, const char *text, float scale); -struct mango_titlebar_node * -mango_titlebar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, - TextDrawData data, int32_t width, int32_t height); -void mango_titlebar_node_destroy(struct mango_titlebar_node *node); -void mango_titlebar_node_set_size(struct mango_titlebar_node *node, +struct mango_tab_bar_node * +mango_tab_bar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, + DecorateDrawData data, int32_t width, int32_t height); +void mango_tab_bar_node_destroy(struct mango_tab_bar_node *node); +void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, int32_t width, int32_t height); -void mango_titlebar_node_update(struct mango_titlebar_node *node, +void mango_tab_bar_node_update(struct mango_tab_bar_node *node, const char *text, float scale); -void mango_text_node_set_focus(struct mango_text_node *node, bool focused); -void mango_titlebar_node_set_focus(struct mango_titlebar_node *node, +void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, bool focused); +void mango_tab_bar_node_set_focus(struct mango_tab_bar_node *node, bool focused); -void mango_titlebar_node_set_colors(struct mango_titlebar_node *node, +void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, const float fg[4], const float bg[4]); -void mango_text_node_apply_config(struct mango_text_node *node, - const TextDrawData *data); -void mango_titlebar_node_apply_config(struct mango_titlebar_node *node, - const TextDrawData *data); -#endif // TEXT_NODE_H \ No newline at end of file +void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, + const DecorateDrawData *data); +void mango_tab_bar_node_apply_config(struct mango_tab_bar_node *node, + const DecorateDrawData *data); +#endif // jump_label_node_H \ No newline at end of file diff --git a/src/layout/arrange.h b/src/layout/arrange.h index f1c4967a..4782b3ea 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -43,7 +43,7 @@ void monocle_set_focus(Client *c, bool focused) { return; c->is_monocle_hide = !focused; - mango_titlebar_node_set_focus(c->titlebar_node, focused); + mango_tab_bar_node_set_focus(c->tab_bar_node, focused); wlr_scene_node_set_enabled(&c->scene->node, focused); if (!focused) { @@ -1143,17 +1143,17 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, set_size_per(m, c); } - if (m->is_jump_mode && !c->text_node) { - client_add_text_node(c); + if (m->is_jump_mode && !c->jump_label_node) { + client_add_jump_label_node(c); } if (m->pertag->ltidxs[m->pertag->curtag]->id == MONOCLE && - !c->titlebar_node) { - client_add_titlebar_node(c); + !c->tab_bar_node) { + client_add_tab_bar_node(c); } - if (c->titlebar_node && c->mon == m) { - wlr_scene_node_set_enabled(&c->titlebar_node->scene_buffer->node, + if (c->tab_bar_node && c->mon == m) { + wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); } diff --git a/src/layout/overview.h b/src/layout/overview.h index 32a9a18d..b259fceb 100644 --- a/src/layout/overview.h +++ b/src/layout/overview.h @@ -368,13 +368,13 @@ void create_jump_hints(Monitor *m) { // 把字符变成字符串 char label_text[2] = {c_char, '\0'}; - mango_text_node_update(c->text_node, label_text, 1.0f); - wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, true); - wlr_scene_node_raise_to_top(&c->text_node->scene_buffer->node); + mango_jump_label_node_update(c->jump_label_node, label_text, 1.0f); + wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, true); + wlr_scene_node_raise_to_top(&c->jump_label_node->scene_buffer->node); wlr_scene_node_set_position( - &c->text_node->scene_buffer->node, - c->geom.width / 2 - c->text_node->logical_width / 2, - c->geom.height / 2 - c->text_node->logical_height / 2); + &c->jump_label_node->scene_buffer->node, + c->geom.width / 2 - c->jump_label_node->logical_width / 2, + c->geom.height / 2 - c->jump_label_node->logical_height / 2); label_idx++; } } @@ -391,9 +391,9 @@ void finish_jump_mode(Monitor *m) { wl_list_for_each(c, &clients, link) { if (VISIBLEON(c, m)) { - if (c->text_node->scene_buffer->node.enabled) { + if (c->jump_label_node->scene_buffer->node.enabled) { c->jump_char = '\0'; - wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, + wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, false); } } diff --git a/src/mango.c b/src/mango.c index d81eee58..498425cc 100644 --- a/src/mango.c +++ b/src/mango.c @@ -176,7 +176,7 @@ enum { NUM_LAYERS }; /* scene layers */ -enum mango_node_type { MANGO_TITLE_NODE, MANGO_TEXT_NODE }; +enum mango_node_type { MANGO_TITLE_NODE, MANGO_jump_label_node }; #ifdef XWAYLAND enum { @@ -339,8 +339,8 @@ struct Client { struct wlr_ext_image_capture_source_v1 *image_capture_source; struct wlr_scene_tree *overview_scene_surface; - struct mango_text_node *text_node; - struct mango_titlebar_node *titlebar_node; + struct mango_jump_label_node *jump_label_node; + struct mango_tab_bar_node *tab_bar_node; struct wl_list link; struct wl_list flink; struct wl_list fadeout_link; @@ -1295,8 +1295,8 @@ void swallow(Client *c, Client *w) { overview_backup_surface(c); } - if (w->titlebar_node) { - wlr_scene_node_set_enabled(&w->titlebar_node->scene_buffer->node, + if (w->tab_bar_node) { + wlr_scene_node_set_enabled(&w->tab_bar_node->scene_buffer->node, false); } @@ -6594,8 +6594,8 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->stack_proportion = 0.0f; - mango_text_node_destroy(c->text_node); - mango_titlebar_node_destroy(c->titlebar_node); + mango_jump_label_node_destroy(c->jump_label_node); + mango_tab_bar_node_destroy(c->tab_bar_node); wlr_scene_node_destroy(&c->scene->node); printstatus(IPC_WATCH_ARRANGGE); motionnotify(0, NULL, 0, 0, 0, 0); @@ -6743,7 +6743,7 @@ void updatetitle(struct wl_listener *listener, void *data) { const char *title; title = client_get_title(c); - mango_titlebar_node_update(c->titlebar_node, title, 1.0); + mango_tab_bar_node_update(c->tab_bar_node, title, 1.0); if (title && c->foreign_toplevel) wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title); if (title && c->ext_foreign_toplevel) { From 347f994ed0a1c7c358e81902588b2e382651235b Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 18 Jun 2026 11:52:16 +0800 Subject: [PATCH 09/13] opt: make sure the node init to null --- src/action/client.h | 3 ++- src/config/parse_config.h | 9 ++++--- src/draw/text-node.c | 54 ++++++++++++++++++++++----------------- src/draw/text-node.h | 42 +++++++++++++++--------------- src/layout/overview.h | 10 +++++--- src/mango.c | 16 +++++++++--- 6 files changed, 78 insertions(+), 56 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index 80327487..adddbbbc 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -98,7 +98,8 @@ void client_pending_force_kill(Client *c) { } void client_add_jump_label_node(Client *c) { - c->jump_label_node = mango_jump_label_node_create(c->scene, config.jumplabeldata); + c->jump_label_node = + mango_jump_label_node_create(c->scene, config.jumplabeldata); wlr_scene_node_lower_to_bottom(&c->jump_label_node->scene_buffer->node); wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, false); } diff --git a/src/config/parse_config.h b/src/config/parse_config.h index ef204b11..35c6c8ce 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -3497,8 +3497,10 @@ void override_config(void) { CLAMP_INT(config.jumplabeldata.border_width, 0, 100); config.jumplabeldata.corner_radius = CLAMP_INT(config.jumplabeldata.corner_radius, 0, 100); - config.jumplabeldata.padding_x = CLAMP_INT(config.jumplabeldata.padding_x, 0, 100); - config.jumplabeldata.padding_y = CLAMP_INT(config.jumplabeldata.padding_y, 0, 100); + config.jumplabeldata.padding_x = + CLAMP_INT(config.jumplabeldata.padding_x, 0, 100); + config.jumplabeldata.padding_y = + CLAMP_INT(config.jumplabeldata.padding_y, 0, 100); } void set_value_default() { @@ -3952,7 +3954,8 @@ void reapply_property(void) { c->bw = config.borderpx; } - mango_jump_label_node_apply_config(c->jump_label_node, &config.jumplabeldata); + mango_jump_label_node_apply_config(c->jump_label_node, + &config.jumplabeldata); mango_tab_bar_node_apply_config(c->tab_bar_node, &config.tabdata); wlr_scene_rect_set_color(c->droparea, config.dropcolor); diff --git a/src/draw/text-node.c b/src/draw/text-node.c index 048b7d25..c85b25dc 100644 --- a/src/draw/text-node.c +++ b/src/draw/text-node.c @@ -59,8 +59,9 @@ static const struct wlr_buffer_impl text_buffer_impl = { .end_data_ptr_access = text_buffer_end_data_ptr_access, }; -struct mango_jump_label_node *mango_jump_label_node_create(struct wlr_scene_tree *parent, - DecorateDrawData data) { +struct mango_jump_label_node * +mango_jump_label_node_create(struct wlr_scene_tree *parent, + DecorateDrawData data) { struct mango_jump_label_node *node = calloc(1, sizeof(*node)); if (!node) return NULL; @@ -135,8 +136,8 @@ void mango_jump_label_node_destroy(struct mango_jump_label_node *node) { free(node); } -void mango_jump_label_node_set_background(struct mango_jump_label_node *node, float r, - float g, float b, float a) { +void mango_jump_label_node_set_background(struct mango_jump_label_node *node, + float r, float g, float b, float a) { if (!node) return; node->bg_color[0] = r; @@ -145,9 +146,9 @@ void mango_jump_label_node_set_background(struct mango_jump_label_node *node, fl node->bg_color[3] = a; } -void mango_jump_label_node_set_border(struct mango_jump_label_node *node, float r, float g, - float b, float a, int32_t width, - int32_t radius) { +void mango_jump_label_node_set_border(struct mango_jump_label_node *node, + float r, float g, float b, float a, + int32_t width, int32_t radius) { if (!node) return; node->border_color[0] = r; @@ -158,16 +159,17 @@ void mango_jump_label_node_set_border(struct mango_jump_label_node *node, float node->corner_radius = radius; } -void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, int32_t pad_x, - int32_t pad_y) { +void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, + int32_t pad_x, int32_t pad_y) { if (!node) return; node->padding_x = pad_x >= 0 ? pad_x : 0; node->padding_y = pad_y >= 0 ? pad_y : 0; } -static void get_text_pixel_size(struct mango_jump_label_node *node, const char *text, - float scale, int32_t *out_w, int32_t *out_h) { +static void get_text_pixel_size(struct mango_jump_label_node *node, + const char *text, float scale, int32_t *out_w, + int32_t *out_h) { if (node->measure_scale != scale) { pango_cairo_context_set_resolution(node->measure_context, 96.0 * scale); node->measure_scale = scale; @@ -191,8 +193,8 @@ static void draw_rounded_rect(cairo_t *cr, double x, double y, double w, cairo_close_path(cr); } -void mango_jump_label_node_update(struct mango_jump_label_node *node, const char *text, - float scale) { +void mango_jump_label_node_update(struct mango_jump_label_node *node, + const char *text, float scale) { if (!node || !text) return; if (scale <= 0.0f) @@ -405,19 +407,22 @@ void mango_jump_label_node_update(struct mango_jump_label_node *node, const char node->logical_height); } -void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, bool focused) { +void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, + bool focused) { if (!node || node->focused == focused) return; node->focused = focused; // 使用缓存的文本和缩放触发重绘(如果无文本则不重绘) if (node->cached_text && node->cached_scale > 0.0f) { - mango_jump_label_node_update(node, node->cached_text, node->cached_scale); + mango_jump_label_node_update(node, node->cached_text, + node->cached_scale); } } struct mango_tab_bar_node * mango_tab_bar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, - DecorateDrawData data, int32_t width, int32_t height) { + DecorateDrawData data, int32_t width, + int32_t height) { struct mango_tab_bar_node *node = calloc(1, sizeof(*node)); if (!node) return NULL; @@ -498,8 +503,8 @@ void mango_tab_bar_node_destroy(struct mango_tab_bar_node *node) { free(node); } -void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, - int32_t width, int32_t height) { +void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, int32_t width, + int32_t height) { if (!node) return; @@ -521,7 +526,7 @@ void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, } void mango_tab_bar_node_update(struct mango_tab_bar_node *node, - const char *text, float scale) { + const char *text, float scale) { if (!node || !text) return; if (scale <= 0.0f) @@ -761,7 +766,7 @@ void mango_tab_bar_node_update(struct mango_tab_bar_node *node, } void mango_tab_bar_node_set_focus(struct mango_tab_bar_node *node, - bool focused) { + bool focused) { if (!node || node->focused == focused) return; node->focused = focused; @@ -772,7 +777,7 @@ void mango_tab_bar_node_set_focus(struct mango_tab_bar_node *node, } void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, - const float fg[4], const float bg[4]) { + const float fg[4], const float bg[4]) { if (!node) return; @@ -786,7 +791,7 @@ void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, } void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, - const DecorateDrawData *data) { + const DecorateDrawData *data) { if (!node || !data) return; @@ -807,12 +812,13 @@ void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, g_strdup(data->font_desc ? data->font_desc : "monospace Bold 16"); if (node->cached_text && node->cached_scale > 0.0f) { - mango_jump_label_node_update(node, node->cached_text, node->cached_scale); + mango_jump_label_node_update(node, node->cached_text, + node->cached_scale); } } void mango_tab_bar_node_apply_config(struct mango_tab_bar_node *node, - const DecorateDrawData *data) { + const DecorateDrawData *data) { if (!node || !data) return; diff --git a/src/draw/text-node.h b/src/draw/text-node.h index ae9ac604..079af908 100644 --- a/src/draw/text-node.h +++ b/src/draw/text-node.h @@ -129,36 +129,38 @@ struct mango_tab_bar_node { }; void mango_text_global_finish(void); -struct mango_jump_label_node *mango_jump_label_node_create(struct wlr_scene_tree *parent, - DecorateDrawData data); +struct mango_jump_label_node * +mango_jump_label_node_create(struct wlr_scene_tree *parent, + DecorateDrawData data); void mango_jump_label_node_destroy(struct mango_jump_label_node *node); -void mango_jump_label_node_set_background(struct mango_jump_label_node *node, float r, - float g, float b, float a); -void mango_jump_label_node_set_border(struct mango_jump_label_node *node, float r, float g, - float b, float a, int32_t width, - int32_t radius); -void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, int32_t pad_x, - int32_t pad_y); -void mango_jump_label_node_update(struct mango_jump_label_node *node, const char *text, - float scale); +void mango_jump_label_node_set_background(struct mango_jump_label_node *node, + float r, float g, float b, float a); +void mango_jump_label_node_set_border(struct mango_jump_label_node *node, + float r, float g, float b, float a, + int32_t width, int32_t radius); +void mango_jump_label_node_set_padding(struct mango_jump_label_node *node, + int32_t pad_x, int32_t pad_y); +void mango_jump_label_node_update(struct mango_jump_label_node *node, + const char *text, float scale); struct mango_tab_bar_node * mango_tab_bar_node_create(void *mango_node_data, struct wlr_scene_tree *parent, - DecorateDrawData data, int32_t width, int32_t height); + DecorateDrawData data, int32_t width, int32_t height); void mango_tab_bar_node_destroy(struct mango_tab_bar_node *node); -void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, - int32_t width, int32_t height); +void mango_tab_bar_node_set_size(struct mango_tab_bar_node *node, int32_t width, + int32_t height); void mango_tab_bar_node_update(struct mango_tab_bar_node *node, - const char *text, float scale); + const char *text, float scale); -void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, bool focused); +void mango_jump_label_node_set_focus(struct mango_jump_label_node *node, + bool focused); void mango_tab_bar_node_set_focus(struct mango_tab_bar_node *node, - bool focused); + bool focused); void mango_tab_bar_node_set_colors(struct mango_tab_bar_node *node, - const float fg[4], const float bg[4]); + const float fg[4], const float bg[4]); void mango_jump_label_node_apply_config(struct mango_jump_label_node *node, - const DecorateDrawData *data); + const DecorateDrawData *data); void mango_tab_bar_node_apply_config(struct mango_tab_bar_node *node, - const DecorateDrawData *data); + const DecorateDrawData *data); #endif // jump_label_node_H \ No newline at end of file diff --git a/src/layout/overview.h b/src/layout/overview.h index b259fceb..1ae9e7a0 100644 --- a/src/layout/overview.h +++ b/src/layout/overview.h @@ -369,8 +369,10 @@ void create_jump_hints(Monitor *m) { char label_text[2] = {c_char, '\0'}; mango_jump_label_node_update(c->jump_label_node, label_text, 1.0f); - wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, true); - wlr_scene_node_raise_to_top(&c->jump_label_node->scene_buffer->node); + wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, + true); + wlr_scene_node_raise_to_top( + &c->jump_label_node->scene_buffer->node); wlr_scene_node_set_position( &c->jump_label_node->scene_buffer->node, c->geom.width / 2 - c->jump_label_node->logical_width / 2, @@ -393,8 +395,8 @@ void finish_jump_mode(Monitor *m) { if (VISIBLEON(c, m)) { if (c->jump_label_node->scene_buffer->node.enabled) { c->jump_char = '\0'; - wlr_scene_node_set_enabled(&c->jump_label_node->scene_buffer->node, - false); + wlr_scene_node_set_enabled( + &c->jump_label_node->scene_buffer->node, false); } } } diff --git a/src/mango.c b/src/mango.c index 498425cc..89fac315 100644 --- a/src/mango.c +++ b/src/mango.c @@ -1296,8 +1296,7 @@ void swallow(Client *c, Client *w) { } if (w->tab_bar_node) { - wlr_scene_node_set_enabled(&w->tab_bar_node->scene_buffer->node, - false); + wlr_scene_node_set_enabled(&w->tab_bar_node->scene_buffer->node, false); } /* 全局链表替换 */ @@ -4348,6 +4347,8 @@ void init_client_properties(Client *c) { c->grid_col_per = 1.0f; c->grid_row_per = 1.0f; c->is_monocle_hide = false; + c->jump_label_node = NULL; + c->tab_bar_node = NULL; c->overview_scene_surface = NULL; c->drop_direction = UNDIR; c->enable_drop_area_draw = false; @@ -6594,8 +6595,15 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->stack_proportion = 0.0f; - mango_jump_label_node_destroy(c->jump_label_node); - mango_tab_bar_node_destroy(c->tab_bar_node); + if (c->jump_label_node) { + mango_jump_label_node_destroy(c->jump_label_node); + c->jump_label_node = NULL; + } + if (c->tab_bar_node) { + mango_tab_bar_node_destroy(c->tab_bar_node); + c->tab_bar_node = NULL; + } + wlr_scene_node_destroy(&c->scene->node); printstatus(IPC_WATCH_ARRANGGE); motionnotify(0, NULL, 0, 0, 0, 0); From 304e5a6a61ba6d7ac080b84c5331dd016b5a31b0 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 18 Jun 2026 13:58:07 +0800 Subject: [PATCH 10/13] opt: focus select exclude monocle hide client --- src/fetch/client.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fetch/client.h b/src/fetch/client.h index 49b45490..0195c05f 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -188,6 +188,8 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, continue; if (!findfloating && c->isfloating) continue; + if (c->is_monocle_hide) + continue; if (c->isunglobal) continue; if (c->is_monocle_hide) @@ -442,7 +444,7 @@ Client *get_focused_stack_client(Client *sc, Client *custom_focus_client) { return sc; wl_list_for_each(tc, &fstack, flink) { - if (tc->iskilling || tc->isunglobal) + if (tc->iskilling || tc->isunglobal || tc->is_monocle_hide) continue; if (!VISIBLEON(tc, sc->mon)) continue; From f4b2c7fd8e7b22ca50bd364eb8f8995045a853f9 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 18 Jun 2026 14:37:14 +0800 Subject: [PATCH 11/13] opt: Eliminate cumulative monocle layout deviations --- src/layout/horizontal.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 1634d849..49afc8ec 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -587,8 +587,13 @@ void monocle(Monitor *m) { m->w.height - 2 * cur_gappov - cur_gapiv - titlebar_height; int title_area_width = m->w.width - 2 * cur_gappoh; - int tw = (title_area_width - (n - 1) * cur_gapih) / n; + + int total_gaps = (n - 1) * cur_gapih; + int base_width = (title_area_width - total_gaps) / n; + int remainder = (title_area_width - total_gaps) % n; + int title_x = m->w.x + cur_gappoh; + int idx = 0; wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || !ISFAKETILED(c)) @@ -606,8 +611,11 @@ void monocle(Monitor *m) { geom.height = main_height; client_tile_resize(c, geom, 0); + int tw = base_width + (idx < remainder ? 1 : 0); global_draw_titlebar(c, title_x, title_y, tw, titlebar_height); + title_x += tw + cur_gapih; + idx++; } } From 3a6c1ea6bcedd00455eb4560d9816f9b1432577d Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 18 Jun 2026 14:54:50 +0800 Subject: [PATCH 12/13] opt: allow focustop get monocle hide client --- src/fetch/client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fetch/client.h b/src/fetch/client.h index 0195c05f..62bc14fe 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -314,7 +314,7 @@ Client *direction_select(const Arg *arg) { Client *focustop(Monitor *m) { Client *c = NULL; wl_list_for_each(c, &fstack, flink) { - if (c->iskilling || c->isunglobal || c->is_monocle_hide) + if (c->iskilling || c->isunglobal) continue; if (VISIBLEON(c, m)) return c; From f677c71ec6df02012c1f26c2f6fdd14531dbfebb Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 18 Jun 2026 23:35:25 +0800 Subject: [PATCH 13/13] opt: allow tab height to 0 --- src/action/client.h | 5 +++++ src/animation/client.h | 8 ++++++-- src/config/parse_config.h | 2 +- src/layout/horizontal.h | 29 +++++++++++++++++------------ 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/action/client.h b/src/action/client.h index adddbbbc..876284cd 100644 --- a/src/action/client.h +++ b/src/action/client.h @@ -105,6 +105,11 @@ void client_add_jump_label_node(Client *c) { } void client_add_tab_bar_node(Client *c) { + + if (config.tab_bar_height <= 0) { + return; + } + MangoNodeData *mangonodedata = ecalloc(1, sizeof(MangoNodeData)); mangonodedata->node_data = c; mangonodedata->type = MANGO_TITLE_NODE; diff --git a/src/animation/client.h b/src/animation/client.h index 0e21f236..832caa0b 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -289,11 +289,15 @@ void apply_shield(Client *c, struct wlr_box clip_box) { } } -void global_draw_titlebar(Client *c, int32_t x, int32_t y, int32_t width, - int32_t height) { +void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width, + int32_t height) { if (!c->tab_bar_node) return; + if (height <= 0) { + wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, false); + } + wlr_scene_node_set_position(&c->tab_bar_node->scene_buffer->node, x, y); wlr_scene_node_set_enabled(&c->tab_bar_node->scene_buffer->node, true); mango_tab_bar_node_set_size(c->tab_bar_node, width, height); diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 35c6c8ce..733990bd 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -3480,7 +3480,7 @@ void override_config(void) { config.scratchpad_height_ratio = CLAMP_FLOAT(config.scratchpad_height_ratio, 0.1f, 1.0f); config.borderpx = CLAMP_INT(config.borderpx, 0, 200); - config.tab_bar_height = CLAMP_INT(config.tab_bar_height, 5, 500); + config.tab_bar_height = CLAMP_INT(config.tab_bar_height, 0, 500); config.smartgaps = CLAMP_INT(config.smartgaps, 0, 1); config.focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f); config.unfocused_opacity = diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 49afc8ec..96c0ed54 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -546,7 +546,7 @@ void deck(Monitor *m) { } void monocle(Monitor *m) { - Client *c, *fc; + Client *c = NULL, *fc = NULL; struct wlr_box geom; int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; @@ -580,19 +580,24 @@ void monocle(Monitor *m) { return; } - int titlebar_height = config.tab_bar_height; - int title_y = m->w.y + cur_gappov; - int main_y = title_y + titlebar_height + cur_gapiv; - int main_height = - m->w.height - 2 * cur_gappov - cur_gapiv - titlebar_height; + int tab_bar_height = config.tab_bar_height; - int title_area_width = m->w.width - 2 * cur_gappoh; + int tab_bar_inner_gap_height = + config.tab_bar_height > 0 ? 2 * cur_gapiv : 0; + int tab_bar_y_offset = config.tab_bar_height > 0 ? cur_gapiv : 0; + + int tab_y = m->w.y + cur_gappov; + int main_y = tab_y + tab_bar_height + tab_bar_y_offset; + int main_height = m->w.height - 2 * cur_gappov - tab_bar_inner_gap_height - + tab_bar_height; + + int tab_area_width = m->w.width - 2 * cur_gappoh; int total_gaps = (n - 1) * cur_gapih; - int base_width = (title_area_width - total_gaps) / n; - int remainder = (title_area_width - total_gaps) % n; + int base_width = (tab_area_width - total_gaps) / n; + int remainder = (tab_area_width - total_gaps) % n; - int title_x = m->w.x + cur_gappoh; + int tab_x = m->w.x + cur_gappoh; int idx = 0; wl_list_for_each(c, &clients, link) { @@ -612,9 +617,9 @@ void monocle(Monitor *m) { client_tile_resize(c, geom, 0); int tw = base_width + (idx < remainder ? 1 : 0); - global_draw_titlebar(c, title_x, title_y, tw, titlebar_height); + global_draw_tab_bar(c, tab_x, tab_y, tw, tab_bar_height); - title_x += tw + cur_gapih; + tab_x += tw + cur_gapih; idx++; } }