feat: client 的任务栏标题点击行为根据 client 窗口的遮挡状态进行

- 当点击 client 在任务中的标题时,如果 client 被其他窗口遮挡,则将其显
示出来,否则切换其 minimize 属性
- 新增 rect 模块用于辅助判断 client 的遮挡状态,添加
rect_intersection/rect_subtract/rect_subtract_many 函数
- client 模块新增 client_get_obscured_state 计算 client 窗口被遮挡状态
- action 模块新增 change_client_state_dwim,实现上述的 client 状态切换
This commit is contained in:
2026-03-03 00:35:11 +08:00
parent 9b7c3a67ef
commit 41a2289c96
8 changed files with 280 additions and 1 deletions

View File

@@ -10,6 +10,7 @@
#include "atoms-extern.h"
#include "base.h"
#include "monitor.h"
#include "rect.h"
#include "types.h"
#include "utils.h"
#include "wm.h"
@@ -646,3 +647,50 @@ void client_stack_raise(client_t *client) {
bool client_is_visible(client_t *client) {
return client->tags & client->monitor->selected_tag->mask;
}
static inline rect_t client_to_rect(client_t *client) {
return (rect_t){
.x1 = client->geometry.x,
.y1 = client->geometry.y,
.x2 = client->geometry.x + (int32_t)client_width(client),
.y2 = client->geometry.y + (int32_t)client_height(client),
};
}
obscured_t client_get_obscured_state(client_t *client, client_t *client_stack) {
if (!client || !client_stack) return obscured_none;
rect_t source = client_to_rect(client);
size_t clip_count = 0;
size_t clip_capacity = 0;
rect_t *clips = nullptr;
bool has_overlap = false;
for (client_t *c = client_stack; c; c = c->stack_next) {
if (c == client) break;
if (c->minimize || !client_is_visible(c)) continue;
rect_t clip = client_to_rect(c);
if (!rect_intersection(source, clip, nullptr)) continue;
has_overlap = true;
if (clip_count == clip_capacity) {
clip_capacity = clip_capacity ? clip_capacity * 2 : 4;
p_realloc(&clips, clip_capacity);
}
clips[clip_count++] = clip;
}
if (!has_overlap) {
p_delete(&clips);
return obscured_none;
}
rect_t *remaining = nullptr;
size_t remaining_count =
rect_subtract_many(source, clips, clip_count, &remaining);
p_delete(&clips);
p_delete(&remaining);
return remaining_count == 0 ? obscured_fully : obscured_partially;
}