feat: monocle layout support title tab

This commit is contained in:
DreamMaoMao 2026-06-17 10:22:37 +08:00
parent 99b696541a
commit 604372cabf
13 changed files with 868 additions and 156 deletions

View file

@ -14,6 +14,7 @@ Control the sizing of window borders and gaps.
| `gappiv` | `5` | Vertical inner gap. | | `gappiv` | `5` | Vertical inner gap. |
| `gappoh` | `10` | Horizontal outer gap (between windows and screen edges). | | `gappoh` | `10` | Horizontal outer gap (between windows and screen edges). |
| `gappov` | `10` | Vertical outer gap. | | `gappov` | `10` | Vertical outer gap. |
| `tab_bar_height` | `50` | Height of the tab bar for monocle layout. |
## Colors ## Colors
@ -55,14 +56,16 @@ You can also color-code windows based on their state:
### Overview Jump Mode ### Overview Jump Mode
| Setting | Default | Description | | Setting | Default | Description |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| `jump_hit_fg_color` | `0xc4939dff` | label text color. | | `text_decorate_fg_color` | `0xc4939dff` | label text color. |
| `jump_hit_bg_color` | `0x201b14ff` | label background color. | `text_decorate_bg_color` | `0x201b14ff` | label background color.|
| `jump_hit_border_color` | `0x8BAA9Bff` | label border color. | `text_decorate_focus_fg_color` | `0x201b14ff` | label text color for focus. |
| `jump_hit_border_width` | `4` | label border width. | `text_decorate_focus_bg_color` | `0xc4939dff` | label background color for focus.|
| `jump_hit_corner_radius` | `5` | label corner radius. | `text_decorate_border_color` | `0x8BAA9Bff` | label border color.|
| `jump_hit_padding_x` | `10` | label horizontal padding. | `text_decorate_border_width` | `4` | label border width.|
| `jump_hit_padding_y` | `10` | label vertical padding. | `text_decorate_corner_radius` | `5` | label corner radius.|
| `jump_hit_font_desc` | `monospace Bold 24` | label font set.| | `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 ## Cursor Theme

View file

@ -96,3 +96,21 @@ void client_pending_force_kill(Client *c) {
return; return;
kill(c->pid, SIGKILL); 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_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);
}

View file

@ -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) { void apply_split_border(Client *c, bool hit_no_border) {
if (c->iskilling || !c->mon || !client_surface(c)->mapped) 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)) { (ISSCROLLTILED(c) || c->animation.tagouting || c->animation.tagining)) {
c->is_clip_to_hide = true; c->is_clip_to_hide = true;
wlr_scene_node_set_enabled(&c->scene->node, false); 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_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->node, true);
} }

View file

@ -277,10 +277,10 @@ void layer_animation_next_tick(LayerSurface *l) {
double opacity_eased_progress = double opacity_eased_progress =
find_animation_curve_at(animation_passed, OPAFADEIN); find_animation_curve_at(animation_passed, OPAFADEIN);
double opacity = double opacity = MANGO_MIN(config.fadein_begin_opacity +
MANGO_MIN(config.fadein_begin_opacity + opacity_eased_progress *
opacity_eased_progress * (1.0 - config.fadein_begin_opacity), (1.0 - config.fadein_begin_opacity),
1.0f); 1.0f);
if (config.animation_fade_in) if (config.animation_fade_in)
wlr_scene_node_for_each_buffer(&l->scene->node, wlr_scene_node_for_each_buffer(&l->scene->node,

View file

@ -24,29 +24,31 @@ void set_tagin_animation(Monitor *m, Client *c) {
c->animainit_geom.x = config.tag_animation_direction == VERTICAL c->animainit_geom.x = config.tag_animation_direction == VERTICAL
? c->animation.current.x ? c->animation.current.x
: MANGO_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->geom.x + c->mon->m.width);
c->animainit_geom.y = config.tag_animation_direction == VERTICAL c->animainit_geom.y = config.tag_animation_direction == VERTICAL
? MANGO_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->geom.y + c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
} else { } else {
c->animainit_geom.x = c->animainit_geom.x = config.tag_animation_direction == VERTICAL
config.tag_animation_direction == VERTICAL ? c->animation.current.x
? c->animation.current.x : MANGO_MIN(m->m.x - c->geom.width,
: MANGO_MIN(m->m.x - c->geom.width, c->geom.x - c->mon->m.width); c->geom.x - c->mon->m.width);
c->animainit_geom.y = c->animainit_geom.y = config.tag_animation_direction == VERTICAL
config.tag_animation_direction == VERTICAL ? MANGO_MIN(m->m.y - c->geom.height,
? MANGO_MIN(m->m.y - c->geom.height, c->geom.y - c->mon->m.height) c->geom.y - c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
} }
} }
void set_arrange_visible(Monitor *m, Client *c, bool want_animation) { 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_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->node, true);
wlr_scene_node_set_enabled(&c->scene_surface->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; : m->pertag->curtag > m->pertag->prevtag;
if (going_forward) { if (going_forward) {
c->pending = c->geom; c->pending = c->geom;
c->pending.x = c->pending.x = config.tag_animation_direction == VERTICAL
config.tag_animation_direction == VERTICAL ? c->animation.current.x
? c->animation.current.x : MANGO_MIN(c->mon->m.x - c->geom.width,
: MANGO_MIN(c->mon->m.x - c->geom.width, c->geom.x - c->mon->m.width); c->geom.x - c->mon->m.width);
c->pending.y = config.tag_animation_direction == VERTICAL c->pending.y = config.tag_animation_direction == VERTICAL
? MANGO_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->geom.y - c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
resize(c, c->geom, 0); 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->pending.x = config.tag_animation_direction == VERTICAL
? c->animation.current.x ? c->animation.current.x
: MANGO_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->geom.x + c->mon->m.width);
c->pending.y = config.tag_animation_direction == VERTICAL c->pending.y = config.tag_animation_direction == VERTICAL
? MANGO_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->geom.y + c->mon->m.height)
: c->animation.current.y; : c->animation.current.y;
resize(c, c->geom, 0); resize(c, c->geom, 0);
} }

View file

@ -311,6 +311,7 @@ typedef struct {
uint32_t gappoh; uint32_t gappoh;
uint32_t gappov; uint32_t gappov;
uint32_t borderpx; uint32_t borderpx;
uint32_t tab_bar_height;
float scratchpad_width_ratio; float scratchpad_width_ratio;
float scratchpad_height_ratio; float scratchpad_height_ratio;
float rootcolor[4]; float rootcolor[4];
@ -387,7 +388,7 @@ typedef struct {
struct xkb_context *ctx; struct xkb_context *ctx;
struct xkb_keymap *keymap; struct xkb_keymap *keymap;
JumphitData jumhitdata; TextDrawData textdata;
} Config; } Config;
typedef int32_t (*FuncType)(const Arg *); typedef int32_t (*FuncType)(const Arg *);
@ -1706,50 +1707,76 @@ bool parse_option(Config *config, char *key, char *value) {
config->cursor_size = atoi(value); config->cursor_size = atoi(value);
} else if (strcmp(key, "cursor_theme") == 0) { } else if (strcmp(key, "cursor_theme") == 0) {
config->cursor_theme = strdup(value); 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); int64_t color = parse_color(value);
if (color == -1) { if (color == -1) {
fprintf(stderr, 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", "format: %s\n",
value); value);
return false; return false;
} else { } 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) { } else if (strcmp(key, "text_decorate_bg_color") == 0) {
config->jumhitdata.font_desc = strdup(value);
} else if (strcmp(key, "jump_hit_bg_color") == 0) {
int64_t color = parse_color(value); int64_t color = parse_color(value);
if (color == -1) { if (color == -1) {
fprintf(stderr, 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", "format: %s\n",
value); value);
return false; return false;
} else { } 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); int64_t color = parse_color(value);
if (color == -1) { if (color == -1) {
fprintf( fprintf(stderr,
stderr, "\033[1m\033[31m[ERROR]:\033[33m Invalid "
"\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_border_color " "text_decorate_focus_fg_color "
"format: %s\n", "format: %s\n",
value); value);
return false; return false;
} else { } 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) { } else if (strcmp(key, "text_decorate_focus_bg_color") == 0) {
config->jumhitdata.border_width = CLAMP_INT(atoi(value), 0, 100); int64_t color = parse_color(value);
} else if (strcmp(key, "jump_hit_corner_radius") == 0) { if (color == -1) {
config->jumhitdata.corner_radius = CLAMP_INT(atoi(value), 0, 100); fprintf(stderr,
} else if (strcmp(key, "jump_hit_padding_x") == 0) { "\033[1m\033[31m[ERROR]:\033[33m Invalid "
config->jumhitdata.padding_x = CLAMP_INT(atoi(value), 0, 100); "text_decorate_focus_bg_color "
} else if (strcmp(key, "jump_hit_padding_y") == 0) { "format: %s\n",
config->jumhitdata.padding_y = CLAMP_INT(atoi(value), 0, 100); 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) { } else if (strcmp(key, "disable_while_typing") == 0) {
config->disable_while_typing = atoi(value); config->disable_while_typing = atoi(value);
} else if (strcmp(key, "left_handed") == 0) { } 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); config->scratchpad_height_ratio = atof(value);
} else if (strcmp(key, "borderpx") == 0) { } else if (strcmp(key, "borderpx") == 0) {
config->borderpx = atoi(value); config->borderpx = atoi(value);
} else if (strcmp(key, "tab_bar_height") == 0) {
config->tab_bar_height = atoi(value);
} else if (strcmp(key, "rootcolor") == 0) { } else if (strcmp(key, "rootcolor") == 0) {
int64_t color = parse_color(value); int64_t color = parse_color(value);
if (color == -1) { if (color == -1) {
@ -3209,9 +3238,9 @@ void free_config(void) {
config.cursor_theme = NULL; config.cursor_theme = NULL;
} }
if (config.jumhitdata.font_desc) { if (config.textdata.font_desc) {
free((void *)config.jumhitdata.font_desc); free((void *)config.textdata.font_desc);
config.jumhitdata.font_desc = NULL; config.textdata.font_desc = NULL;
} }
if (config.tablet_map_to_mon) { if (config.tablet_map_to_mon) {
@ -3375,19 +3404,18 @@ void override_config(void) {
config.scratchpad_height_ratio = config.scratchpad_height_ratio =
CLAMP_FLOAT(config.scratchpad_height_ratio, 0.1f, 1.0f); CLAMP_FLOAT(config.scratchpad_height_ratio, 0.1f, 1.0f);
config.borderpx = CLAMP_INT(config.borderpx, 0, 200); 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.smartgaps = CLAMP_INT(config.smartgaps, 0, 1);
config.focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f); config.focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f);
config.unfocused_opacity = config.unfocused_opacity =
CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f); CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f);
config.jumhitdata.border_width = config.textdata.border_width =
CLAMP_INT(config.jumhitdata.border_width, 0, 100); CLAMP_INT(config.textdata.border_width, 0, 100);
config.jumhitdata.corner_radius = config.textdata.corner_radius =
CLAMP_INT(config.jumhitdata.corner_radius, 0, 100); CLAMP_INT(config.textdata.corner_radius, 0, 100);
config.jumhitdata.padding_x = config.textdata.padding_x = CLAMP_INT(config.textdata.padding_x, 0, 100);
CLAMP_INT(config.jumhitdata.padding_x, 0, 100); config.textdata.padding_y = CLAMP_INT(config.textdata.padding_y, 0, 100);
config.jumhitdata.padding_y =
CLAMP_INT(config.jumhitdata.padding_y, 0, 100);
} }
void set_value_default() { void set_value_default() {
@ -3475,6 +3503,7 @@ void set_value_default() {
config.idleinhibit_ignore_visible = 0; config.idleinhibit_ignore_visible = 0;
config.borderpx = 4; config.borderpx = 4;
config.tab_bar_height = 50;
config.overviewgappi = 5; config.overviewgappi = 5;
config.overviewgappo = 30; config.overviewgappo = 30;
config.cursor_hide_timeout = 0; config.cursor_hide_timeout = 0;
@ -3539,22 +3568,30 @@ void set_value_default() {
config.animation_curve_opafadeout[2] = 0.5; config.animation_curve_opafadeout[2] = 0.5;
config.animation_curve_opafadeout[3] = 0.5; config.animation_curve_opafadeout[3] = 0.5;
config.jumhitdata.fg_color[0] = 0xc4 / 255.0f; config.textdata.fg_color[0] = 0xc4 / 255.0f;
config.jumhitdata.fg_color[1] = 0x93 / 255.0f; config.textdata.fg_color[1] = 0x93 / 255.0f;
config.jumhitdata.fg_color[2] = 0x9d / 255.0f; config.textdata.fg_color[2] = 0x9d / 255.0f;
config.jumhitdata.fg_color[3] = 1.0f; config.textdata.fg_color[3] = 1.0f;
config.jumhitdata.bg_color[0] = 0x32 / 255.0f; config.textdata.bg_color[0] = 0x32 / 255.0f;
config.jumhitdata.bg_color[1] = 0x32 / 255.0f; config.textdata.bg_color[1] = 0x32 / 255.0f;
config.jumhitdata.bg_color[2] = 0x32 / 255.0f; config.textdata.bg_color[2] = 0x32 / 255.0f;
config.jumhitdata.bg_color[3] = 1.0f; config.textdata.bg_color[3] = 1.0f;
config.jumhitdata.border_color[0] = 0x8b / 255.0f; config.textdata.focus_fg_color[0] = 0xed / 255.0f;
config.jumhitdata.border_color[1] = 0xaa / 255.0f; config.textdata.focus_fg_color[1] = 0xa6 / 255.0f;
config.jumhitdata.border_color[2] = 0x9b / 255.0f; config.textdata.focus_fg_color[2] = 0xb4 / 255.0f;
config.jumhitdata.border_color[3] = 1.0f; config.textdata.focus_fg_color[3] = 1.0f;
config.jumhitdata.border_width = 4; config.textdata.focus_bg_color[0] = 0x4e / 255.0f;
config.jumhitdata.corner_radius = 5; config.textdata.focus_bg_color[1] = 0x45 / 255.0f;
config.jumhitdata.padding_x = 10; config.textdata.focus_bg_color[2] = 0x3c / 255.0f;
config.jumhitdata.padding_y = 10; 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[0] = 0x32 / 255.0f;
config.rootcolor[1] = 0x32 / 255.0f; config.rootcolor[1] = 0x32 / 255.0f;
@ -3668,7 +3705,7 @@ bool parse_config(void) {
config.tag_rules = NULL; config.tag_rules = NULL;
config.tag_rules_count = 0; config.tag_rules_count = 0;
config.cursor_theme = NULL; config.cursor_theme = NULL;
config.jumhitdata.font_desc = NULL; config.textdata.font_desc = NULL;
config.tablet_map_to_mon = NULL; config.tablet_map_to_mon = NULL;
strcpy(config.keymode, "default"); strcpy(config.keymode, "default");
@ -3806,6 +3843,10 @@ void reapply_property(void) {
c->bw = config.borderpx; 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->droparea, config.dropcolor);
wlr_scene_rect_set_color(c->splitindicator[0], config.splitcolor); wlr_scene_rect_set_color(c->splitindicator[0], config.splitcolor);
wlr_scene_rect_set_color(c->splitindicator[1], config.splitcolor); wlr_scene_rect_set_color(c->splitindicator[1], config.splitcolor);

View file

@ -137,6 +137,7 @@ int32_t focusdir(const Arg *arg) {
Client *c = NULL; Client *c = NULL;
c = direction_select(arg); c = direction_select(arg);
if (!selmon->isoverview) if (!selmon->isoverview)
c = get_focused_stack_client(c, arg->tc); c = get_focused_stack_client(c, arg->tc);
if (c) { if (c) {
@ -762,7 +763,7 @@ int32_t smartmovewin(const Arg *arg) {
} }
ny = tar == 99999 ? ny : tar; ny = tar == 99999 ? ny : tar;
ny = MANGO_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); c->mon->gappov);
break; break;
case LEFT: case LEFT:
tar = -99999; tar = -99999;
@ -801,7 +802,7 @@ int32_t smartmovewin(const Arg *arg) {
} }
nx = tar == 99999 ? nx : tar; nx = tar == 99999 ? nx : tar;
nx = MANGO_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); c->mon->gappoh);
break; break;
} }
@ -1083,7 +1084,8 @@ int32_t switch_layout(const Arg *arg) {
} }
for (ji = 0; ji < LENGTH(layouts); ji++) { 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) { if (strncmp(layouts[ji].name, target_layout_name, len) == 0) {
selmon->pertag->ltidxs[selmon->pertag->curtag] = &layouts[ji]; selmon->pertag->ltidxs[selmon->pertag->curtag] = &layouts[ji];

View file

@ -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, 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)); struct mango_text_node *node = calloc(1, sizeof(*node));
if (!node) if (!node)
return NULL; 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->fg_color, data.fg_color, sizeof(node->fg_color));
memcpy(node->bg_color, data.bg_color, sizeof(node->bg_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)); memcpy(node->border_color, data.border_color, sizeof(node->border_color));
node->border_width = data.border_width; node->border_width = data.border_width;
node->corner_radius = data.corner_radius; node->corner_radius = data.corner_radius;
node->padding_x = data.padding_x; node->padding_x = data.padding_x;
node->padding_y = data.padding_y; node->padding_y = data.padding_y;
node->font_desc = 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_text = NULL;
node->cached_scale = -1.0f; node->cached_scale = -1.0f;
node->cached_font_desc = NULL; node->cached_font_desc = NULL;
node->focused = false;
node->cached_focused = false;
node->measure_surface = node->measure_surface =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); 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_layout = pango_layout_new(node->measure_context);
node->measure_scale = 1.0f; node->measure_scale = 1.0f;
node->scene_buffer->node.data = NULL;
return node; return node;
} }
@ -190,7 +198,7 @@ void mango_text_node_update(struct mango_text_node *node, const char *text,
if (scale <= 0.0f) if (scale <= 0.0f)
scale = 1.0f; scale = 1.0f;
/* 脏检查 */ /* 脏检查,加入 focused 状态 */
if (node->cached_scale == scale && node->cached_font_desc && if (node->cached_scale == scale && node->cached_font_desc &&
strcmp(node->cached_font_desc, node->font_desc) == 0 && strcmp(node->cached_font_desc, node->font_desc) == 0 &&
node->cached_text && strcmp(node->cached_text, text) == 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 && 0 &&
memcmp(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)) == memcmp(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)) ==
0 && 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, memcmp(node->cached_border_color, node->border_color,
sizeof(node->border_color)) == 0 && sizeof(node->border_color)) == 0 &&
node->cached_border_width == node->border_width && node->cached_border_width == node->border_width &&
node->cached_corner_radius == node->corner_radius && node->cached_corner_radius == node->corner_radius &&
node->cached_padding_x == node->padding_x && 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; return;
} }
@ -215,12 +228,17 @@ void mango_text_node_update(struct mango_text_node *node, const char *text,
node->cached_scale = scale; node->cached_scale = scale;
memcpy(node->cached_fg_color, node->fg_color, sizeof(node->fg_color)); 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_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, memcpy(node->cached_border_color, node->border_color,
sizeof(node->border_color)); sizeof(node->border_color));
node->cached_border_width = node->border_width; node->cached_border_width = node->border_width;
node->cached_corner_radius = node->corner_radius; node->cached_corner_radius = node->corner_radius;
node->cached_padding_x = node->padding_x; 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;
int32_t text_pixel_w, text_pixel_h; int32_t text_pixel_w, text_pixel_h;
get_text_pixel_size(node, text, scale, &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; return;
} }
/* 逻辑尺寸:文本 + 内边距 + 边框(整数计算) */
int32_t logical_text_w = (int32_t)(text_pixel_w / 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 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_w = logical_text_w + 2 * node->padding_x;
int32_t box_logical_h = logical_text_h + 2 * node->padding_y; int32_t box_logical_h = logical_text_h + 2 * node->padding_y;
/* 加上边框后,乘以 scale 得到物理像素(表面尺寸),向上取整 */
int32_t required_pixel_w = int32_t required_pixel_w =
(int32_t)((box_logical_w + 2 * node->border_width) * scale + 0.5f); (int32_t)((box_logical_w + 2 * node->border_width) * scale + 0.5f);
int32_t required_pixel_h = 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_t *cr = cairo_create(node->surface);
/* 清空为透明 */
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr); cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
/* 计算背景矩形(物理像素) */
double border = node->border_width * scale; double border = node->border_width * scale;
double bg_x = border; double bg_x = border;
double bg_y = border; double bg_y = border;
double bg_w = box_logical_w * scale; double bg_w = box_logical_w * scale;
double bg_h = box_logical_h * scale; double bg_h = box_logical_h * scale;
/* 圆角半径(物理像素) */
double radius; double radius;
if (node->corner_radius < 0) { if (node->corner_radius < 0) {
/* 负数表示自动取半宽/半高作为圆角 */
radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0; radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0;
} else { } else {
radius = node->corner_radius * scale; radius = node->corner_radius * scale;
} }
/* 限制最大圆角 */
if (radius > bg_w / 2.0) if (radius > bg_w / 2.0)
radius = bg_w / 2.0; radius = bg_w / 2.0;
if (radius > bg_h / 2.0) if (radius > bg_h / 2.0)
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 = bool draw_border =
(node->border_width > 0) && (node->border_color[3] > 0.0f); (node->border_width > 0) && (node->border_color[3] > 0.0f);
/* 绘制背景 */
if (draw_bg) { if (draw_bg) {
cairo_set_source_rgba(cr, node->bg_color[0], node->bg_color[1], cairo_set_source_rgba(cr, active_bg[0], active_bg[1], active_bg[2],
node->bg_color[2], node->bg_color[3]); active_bg[3]);
if (radius > 0.0) { if (radius > 0.0) {
draw_rounded_rect(cr, bg_x, bg_y, bg_w, bg_h, radius); draw_rounded_rect(cr, bg_x, bg_y, bg_w, bg_h, radius);
cairo_fill(cr); cairo_fill(cr);
@ -323,7 +338,6 @@ void mango_text_node_update(struct mango_text_node *node, const char *text,
} }
} }
/* 绘制文本 */
cairo_save(cr); cairo_save(cr);
double text_x = (node->border_width + node->padding_x) * scale; double text_x = (node->border_width + node->padding_x) * scale;
double text_y = (node->border_width + node->padding_y) * 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_font_description(layout, desc);
pango_layout_set_text(layout, text, -1); pango_layout_set_text(layout, text, -1);
cairo_set_source_rgba(cr, node->fg_color[0], node->fg_color[1], cairo_set_source_rgba(cr, active_fg[0], active_fg[1], active_fg[2],
node->fg_color[2], node->fg_color[3]); active_fg[3]);
pango_cairo_show_layout(cr, layout); pango_cairo_show_layout(cr, layout);
g_object_unref(layout); g_object_unref(layout);
g_object_unref(ctx); g_object_unref(ctx);
cairo_restore(cr); cairo_restore(cr);
/* 绘制边框 */
if (draw_border) { if (draw_border) {
cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1], cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1],
node->border_color[2], node->border_color[3]); 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_surface_flush(node->surface);
cairo_destroy(cr); cairo_destroy(cr);
/* 更新 wlr_buffer */
if (node->buffer) { if (node->buffer) {
wlr_buffer_drop(&node->buffer->base); wlr_buffer_drop(&node->buffer->base);
node->buffer = NULL; node->buffer = NULL;
@ -392,3 +404,436 @@ void mango_text_node_update(struct mango_text_node *node, const char *text,
wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width, wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width,
node->logical_height); 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);
}
}

View file

@ -1,12 +1,27 @@
#ifndef MANGO_TEXT_NODE_H #ifndef TEXT_NODE_H
#define MANGO_TEXT_NODE_H #define TEXT_NODE_H
#include <cairo.h> #include <cairo.h>
#include <pango/pango.h>
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include <wayland-server-core.h> #include <stdbool.h>
#include <stdint.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
// 自定义 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 mango_text_buffer {
struct wlr_buffer base; struct wlr_buffer base;
cairo_surface_t *surface; cairo_surface_t *surface;
@ -14,29 +29,39 @@ struct mango_text_buffer {
struct mango_text_node { struct mango_text_node {
struct wlr_scene_buffer *scene_buffer; 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 fg_color[4];
float bg_color[4]; float bg_color[4];
float focus_fg_color[4];
float focus_bg_color[4];
float border_color[4]; float border_color[4];
int32_t border_width; int32_t border_width;
int32_t corner_radius; int32_t corner_radius;
int32_t padding_x, padding_y; int32_t padding_x;
int32_t padding_y;
char *font_desc; char *font_desc;
// 缓存
char *cached_text; char *cached_text;
char *cached_font_desc;
float cached_scale; float cached_scale;
float cached_fg_color[4]; float cached_fg_color[4];
float cached_bg_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_color[4];
float cached_border_width; int32_t cached_border_width;
float cached_corner_radius; int32_t cached_corner_radius;
float cached_padding_x, cached_padding_y; int32_t cached_padding_x;
char *cached_font_desc; int32_t cached_padding_y;
bool cached_focused;
cairo_surface_t *surface; bool focused;
struct mango_text_buffer *buffer;
int32_t surface_pixel_w, surface_pixel_h;
// 测量
cairo_surface_t *measure_surface; cairo_surface_t *measure_surface;
cairo_t *measure_cr; cairo_t *measure_cr;
PangoContext *measure_context; PangoContext *measure_context;
@ -47,32 +72,93 @@ struct mango_text_node {
int32_t logical_height; 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 fg_color[4];
float bg_color[4]; float bg_color[4];
float focus_fg_color[4];
float focus_bg_color[4];
float border_color[4]; float border_color[4];
int32_t border_width; int32_t border_width;
int32_t corner_radius; int32_t corner_radius;
int32_t padding_x; int32_t padding_x;
int32_t padding_y; int32_t padding_y;
const char *font_desc; char *font_desc;
} JumphitData;
// 尺寸
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, 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_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, void mango_text_node_set_background(struct mango_text_node *node, float r,
float g, float b, float a); float g, float b, float a);
void mango_text_node_set_border(struct mango_text_node *node, float r, float g, void mango_text_node_set_border(struct mango_text_node *node, float r, float g,
float b, float a, int32_t width, float b, float a, int32_t width,
int32_t radius); int32_t radius);
void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x, void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x,
int32_t pad_y); 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 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

View file

@ -26,6 +26,14 @@ bool is_scroller_layout(Monitor *m) {
return false; 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) { bool is_centertile_layout(Monitor *m) {
if (m->pertag->ltidxs[m->pertag->curtag]->id == CENTER_TILE) if (m->pertag->ltidxs[m->pertag->curtag]->id == CENTER_TILE)

View file

@ -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, void resize_tile_master_horizontal(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, int32_t offsety, uint32_t time,
int32_t type) { int32_t type) {
@ -1128,6 +1143,20 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation,
set_size_per(m, c); 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)) { if (c->mon == m && (c->isglobal || c->isunglobal)) {
c->tags = m->tagset[m->seltags]; c->tags = m->tagset[m->seltags];
} }

View file

@ -545,32 +545,70 @@ void deck(Monitor *m) {
} }
} }
void // 17 void monocle(Monitor *m) {
monocle(Monitor *m) { Client *c, *fc;
Client *c = NULL;
struct wlr_box geom; struct wlr_box geom;
int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 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 if (config.smartgaps && m->visible_fake_tiling_clients == 1) {
? 0 cur_gappov = cur_gappoh = cur_gapiv = cur_gapih = 0;
: cur_gappoh; }
cur_gappov = config.smartgaps && m->visible_fake_tiling_clients == 1
? 0
: cur_gappov;
wl_list_for_each(c, &clients, link) { int n = m->visible_fake_tiling_clients;
if (!VISIBLEON(c, m) || !ISFAKETILED(c)) if (n == 0)
return;
wl_list_for_each(c, &fstack, flink) {
if (c->iskilling || c->isunglobal || !ISFAKETILED(c))
continue; continue;
if (VISIBLEON(c, m)) {
fc = c;
break;
}
}
if (n == 1) {
geom.x = m->w.x + cur_gappoh; geom.x = m->w.x + cur_gappoh;
geom.y = m->w.y + cur_gappov; geom.y = m->w.y + cur_gappov;
geom.width = m->w.width - 2 * cur_gappoh; geom.width = m->w.width - 2 * cur_gappoh;
geom.height = m->w.height - 2 * cur_gappov; 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);
} }
// 网格布局窗口大小和位置计算 // 网格布局窗口大小和位置计算

View file

@ -166,6 +166,7 @@ enum { AxisUp, AxisDown, AxisLeft, AxisRight }; // 滚轮滚动的方向
enum { enum {
LyrBg, LyrBg,
LyrBottom, LyrBottom,
LyrDecorate,
LyrTile, LyrTile,
LyrTop, LyrTop,
LyrFadeOut, LyrFadeOut,
@ -174,6 +175,9 @@ enum {
LyrBlock, LyrBlock,
NUM_LAYERS NUM_LAYERS
}; /* scene layers */ }; /* scene layers */
enum mango_node_type { MANGO_TITLE_NODE, MANGO_TEXT_NODE };
#ifdef XWAYLAND #ifdef XWAYLAND
enum { enum {
NetWMWindowTypeDialog, NetWMWindowTypeDialog,
@ -240,6 +244,11 @@ typedef struct {
Client *tc; Client *tc;
} Arg; } Arg;
typedef struct {
enum mango_node_type type;
void *node_data;
} MangoNodeData;
typedef struct { typedef struct {
uint32_t mod; uint32_t mod;
uint32_t button; uint32_t button;
@ -263,8 +272,8 @@ typedef struct {
struct wl_list link; struct wl_list link;
struct wlr_input_device *wlr_device; struct wlr_input_device *wlr_device;
struct libinput_device *libinput_device; struct libinput_device *libinput_device;
struct wl_listener destroy_listener; // 用于监听设备销毁事件 struct wl_listener destroy_listener;
void *device_data; // 新增:指向设备特定数据(如 Switch void *device_data;
} InputDevice; } InputDevice;
typedef struct { typedef struct {
@ -331,6 +340,7 @@ struct Client {
struct wlr_scene_tree *overview_scene_surface; struct wlr_scene_tree *overview_scene_surface;
struct mango_text_node *text_node; struct mango_text_node *text_node;
struct mango_titlebar_node *titlebar_node;
struct wl_list link; struct wl_list link;
struct wl_list flink; struct wl_list flink;
struct wl_list fadeout_link; struct wl_list fadeout_link;
@ -397,6 +407,7 @@ struct Client {
int32_t istagswitching; int32_t istagswitching;
int32_t isnamedscratchpad; int32_t isnamedscratchpad;
int32_t shield_when_capture; int32_t shield_when_capture;
bool is_monocle_hide;
bool is_pending_open_animation; bool is_pending_open_animation;
bool is_restoring_from_ov; bool is_restoring_from_ov;
float scroller_proportion; float scroller_proportion;
@ -851,6 +862,7 @@ static struct wlr_scene_tree *
wlr_scene_tree_snapshot(struct wlr_scene_node *node, wlr_scene_tree_snapshot(struct wlr_scene_node *node,
struct wlr_scene_tree *parent); struct wlr_scene_tree *parent);
static bool is_scroller_layout(Monitor *m); static bool is_scroller_layout(Monitor *m);
static bool is_monocle_layout(Monitor *m);
static bool is_centertile_layout(Monitor *m); static bool is_centertile_layout(Monitor *m);
static void create_output(struct wlr_backend *backend, void *data); static void create_output(struct wlr_backend *backend, void *data);
static void get_layout_abbr(char *abbr, const char *full_name); 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); 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->link, &c->link);
wl_list_insert(&w->flink, &c->flink); wl_list_insert(&w->flink, &c->flink);
@ -2390,6 +2407,17 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) {
return true; 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状态
// 避免layer虚拟键盘锁死mod按键状态 // 避免layer虚拟键盘锁死mod按键状态
hard_keyboard = &kb_group->wlr_group->keyboard; hard_keyboard = &kb_group->wlr_group->keyboard;
@ -3810,17 +3838,17 @@ void focusclient(Client *c, int32_t lift) {
// decide whether need to re-arrange // 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 // change focus link position
wl_list_remove(&c->flink); wl_list_remove(&c->flink);
wl_list_insert(&fstack, &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 // change border color
c->isurgent = 0; c->isurgent = 0;
} }
@ -4319,6 +4347,7 @@ void locksession(struct wl_listener *listener, void *data) {
void init_client_properties(Client *c) { void init_client_properties(Client *c) {
c->grid_col_per = 1.0f; c->grid_col_per = 1.0f;
c->grid_row_per = 1.0f; c->grid_row_per = 1.0f;
c->is_monocle_hide = false;
c->overview_scene_surface = NULL; c->overview_scene_surface = NULL;
c->drop_direction = UNDIR; c->drop_direction = UNDIR;
c->enable_drop_area_draw = false; c->enable_drop_area_draw = false;
@ -4490,9 +4519,6 @@ mapnotify(struct wl_listener *listener, void *data) {
: config.bordercolor); : config.bordercolor);
c->border[i]->node.data = c; 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++) { for (i = 0; i < 2; i++) {
c->splitindicator[i] = wlr_scene_rect_create( c->splitindicator[i] = wlr_scene_rect_create(
@ -6569,6 +6595,7 @@ void unmapnotify(struct wl_listener *listener, void *data) {
c->stack_proportion = 0.0f; c->stack_proportion = 0.0f;
mango_text_node_destroy(c->text_node); mango_text_node_destroy(c->text_node);
mango_titlebar_node_destroy(c->titlebar_node);
wlr_scene_node_destroy(&c->scene->node); wlr_scene_node_destroy(&c->scene->node);
printstatus(IPC_WATCH_ARRANGGE); printstatus(IPC_WATCH_ARRANGGE);
motionnotify(0, NULL, 0, 0, 0, 0); motionnotify(0, NULL, 0, 0, 0, 0);
@ -6716,6 +6743,7 @@ void updatetitle(struct wl_listener *listener, void *data) {
const char *title; const char *title;
title = client_get_title(c); title = client_get_title(c);
mango_titlebar_node_update(c->titlebar_node, title, 1.0);
if (title && c->foreign_toplevel) if (title && c->foreign_toplevel)
wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title); wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title);
if (title && c->ext_foreign_toplevel) { if (title && c->ext_foreign_toplevel) {