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. |
| `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

View file

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

View file

@ -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,

View file

@ -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);
}

View file

@ -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);

View file

@ -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];

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,
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;
@ -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,
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
#define MANGO_TEXT_NODE_H
#ifndef TEXT_NODE_H
#define TEXT_NODE_H
#include <cairo.h>
#include <pango/pango.h>
#include <pango/pangocairo.h>
#include <wayland-server-core.h>
#include <stdbool.h>
#include <stdint.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 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
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;
}
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)

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,
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];
}

View file

@ -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);
}
// 网格布局窗口大小和位置计算

View file

@ -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) {