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:
@@ -43,6 +43,7 @@ add_executable(${APP_NAME}
|
||||
${SOURCE_DIR}/xres.c
|
||||
${SOURCE_DIR}/status.c
|
||||
${SOURCE_DIR}/audio.c
|
||||
${SOURCE_DIR}/rect.c
|
||||
)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
20
src/action.c
20
src/action.c
@@ -118,3 +118,23 @@ void kill_client(const user_action_arg_t *arg) {
|
||||
client_kill(wm.client_focused);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据 client 的当前状态切换 client 下一状态
|
||||
* @description 如果当前 client 被遮挡了或者最小化了,则显示出来,否则最小化
|
||||
*/
|
||||
void change_client_state_dwim(const user_action_arg_t *arg) {
|
||||
client_t *client = (client_t *)arg->ptr;
|
||||
if (!client) return;
|
||||
|
||||
if (client != wm.client_stack_list &&
|
||||
client_get_obscured_state(client, wm.client_stack_list) !=
|
||||
obscured_none) {
|
||||
client_stack_raise(client);
|
||||
client_focus(client);
|
||||
return;
|
||||
}
|
||||
|
||||
client_set_minimize(client, !client->minimize);
|
||||
if (!client->minimize) client_stack_raise(client);
|
||||
}
|
||||
|
||||
@@ -63,3 +63,4 @@ void toggle_client_fullscreen(const user_action_arg_t *arg);
|
||||
void toggle_client_maximize(const user_action_arg_t *arg);
|
||||
void toggle_client_minimize(const user_action_arg_t *arg);
|
||||
void kill_client(const user_action_arg_t *arg);
|
||||
void change_client_state_dwim(const user_action_arg_t *arg);
|
||||
|
||||
48
src/client.c
48
src/client.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,13 @@ void client_stack_raise(client_t *client);
|
||||
|
||||
bool client_is_visible(client_t *client);
|
||||
|
||||
typedef enum obscured_t {
|
||||
obscured_none,
|
||||
obscured_partially,
|
||||
obscured_fully,
|
||||
} obscured_t;
|
||||
obscured_t client_get_obscured_state(client_t *client, client_t *client_stack);
|
||||
|
||||
static inline uint16_t client_width(client_t *c) {
|
||||
return c->geometry.width + c->border_width * 2;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,13 @@ static const char *const active_border_color = "#005577";
|
||||
|
||||
static const button_t button_list[] = {
|
||||
{click_tag, modifier_none, button_left, select_tag_of_current_monitor, {0}},
|
||||
{click_client_name, modifier_none, button_left, toggle_client_minimize, {0}},
|
||||
{
|
||||
click_client_name,
|
||||
modifier_none,
|
||||
button_left,
|
||||
change_client_state_dwim,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
#define TAGKEYS(KEY, TAG) \
|
||||
|
||||
155
src/rect.c
Normal file
155
src/rect.c
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "rect.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static inline bool rect_valid(rect_t rect) {
|
||||
return rect.x1 < rect.x2 && rect.y1 < rect.y2;
|
||||
}
|
||||
|
||||
/* 将一个合法矩形追加到动态矩形数组中。 */
|
||||
static inline void rect_list_append(rect_t **list, size_t *length,
|
||||
size_t *capacity, rect_t rect) {
|
||||
if (!rect_valid(rect)) return;
|
||||
|
||||
if (*length == *capacity) {
|
||||
*capacity = *capacity ? *capacity * 2 : 4;
|
||||
p_realloc(list, *capacity);
|
||||
}
|
||||
(*list)[(*length)++] = rect;
|
||||
}
|
||||
|
||||
bool rect_intersection(rect_t a, rect_t b, rect_t *intersection) {
|
||||
if (!rect_valid(a) || !rect_valid(b)) return false;
|
||||
|
||||
rect_t overlap = {
|
||||
.x1 = MAX(a.x1, b.x1),
|
||||
.y1 = MAX(a.y1, b.y1),
|
||||
.x2 = MIN(a.x2, b.x2),
|
||||
.y2 = MIN(a.y2, b.y2),
|
||||
};
|
||||
if (!rect_valid(overlap)) return false;
|
||||
|
||||
if (intersection) *intersection = overlap;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t rect_subtract(rect_t source, rect_t clip, rect_t remaining[4]) {
|
||||
if (!rect_valid(source)) return 0;
|
||||
|
||||
rect_t overlap;
|
||||
if (!rect_intersection(source, clip, &overlap)) {
|
||||
if (remaining) remaining[0] = source;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
/*
|
||||
* source: [sx1,sx2) x [sy1,sy2)
|
||||
* overlap: [ox1,ox2) x [oy1,oy2) = source 与 clip 的交集
|
||||
*
|
||||
* y=sy1 ┌───────────────────────────────────────────┐
|
||||
* │ TOP │
|
||||
* │ UL UR │
|
||||
* y=oy1 ├───────────────┬───────────────┬───────────┤
|
||||
* │ LEFT │ OVERLAP │ RIGHT │
|
||||
* y=oy2 ├───────────────┴───────────────┴───────────┤
|
||||
* │ LL LR │
|
||||
* │ BOTTOM │
|
||||
* y=sy2 └───────────────────────────────────────────┘
|
||||
* x=sx1 x=ox1 x=ox2 x=sx2
|
||||
*
|
||||
* 角块归属:
|
||||
* UL、UR 属于 TOP
|
||||
* LL、LR 属于 BOTTOM
|
||||
* LEFT/RIGHT 只覆盖 y ∈ [oy1, oy2),因此不会包含四个角块。
|
||||
*/
|
||||
rect_t parts[] = {
|
||||
/* top */
|
||||
{
|
||||
.x1 = source.x1,
|
||||
.y1 = source.y1,
|
||||
.x2 = source.x2,
|
||||
.y2 = overlap.y1,
|
||||
},
|
||||
/* bottom */
|
||||
{
|
||||
.x1 = source.x1,
|
||||
.y1 = overlap.y2,
|
||||
.x2 = source.x2,
|
||||
.y2 = source.y2,
|
||||
},
|
||||
/* left */
|
||||
{
|
||||
.x1 = source.x1,
|
||||
.y1 = overlap.y1,
|
||||
.x2 = overlap.x1,
|
||||
.y2 = overlap.y2,
|
||||
},
|
||||
/* right */
|
||||
{
|
||||
.x1 = overlap.x2,
|
||||
.y1 = overlap.y1,
|
||||
.x2 = source.x2,
|
||||
.y2 = overlap.y2,
|
||||
},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < countof(parts); i++) {
|
||||
if (!rect_valid(parts[i])) continue;
|
||||
if (remaining) remaining[count] = parts[i];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t rect_subtract_many(rect_t source, const rect_t clips[],
|
||||
size_t clip_count, rect_t **remaining) {
|
||||
/*
|
||||
* 实现说明:
|
||||
* 初始剩余集合为 {source},然后依次处理每个 clip:
|
||||
* 1) 用 clip 去减当前集合中的每个碎片
|
||||
* 2) 将产生的新碎片合并到 next 集合
|
||||
* 3) 用 next 替换 current,进入下一轮
|
||||
* 最终 current 即 source - clips[0] - clips[1] - ... 的结果。
|
||||
*/
|
||||
if (remaining) *remaining = nullptr;
|
||||
if (!rect_valid(source)) return 0;
|
||||
|
||||
size_t current_length = 1;
|
||||
size_t current_capacity = 1;
|
||||
rect_t *current = p_new(rect_t, current_capacity);
|
||||
current[0] = source;
|
||||
|
||||
if (clips) {
|
||||
for (size_t i = 0; i < clip_count && current_length > 0; i++) {
|
||||
rect_t clip = clips[i];
|
||||
if (!rect_valid(clip)) continue;
|
||||
|
||||
size_t next_length = 0;
|
||||
size_t next_capacity = current_length ? current_length : 1;
|
||||
rect_t *next = p_new(rect_t, next_capacity);
|
||||
|
||||
for (size_t j = 0; j < current_length; j++) {
|
||||
rect_t fragments[4];
|
||||
size_t fragment_count = rect_subtract(current[j], clip, fragments);
|
||||
for (size_t k = 0; k < fragment_count; k++) {
|
||||
rect_list_append(&next, &next_length, &next_capacity, fragments[k]);
|
||||
}
|
||||
}
|
||||
|
||||
p_delete(¤t);
|
||||
current = next;
|
||||
current_length = next_length;
|
||||
current_capacity = next_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining) {
|
||||
*remaining = current;
|
||||
} else {
|
||||
p_delete(¤t);
|
||||
}
|
||||
|
||||
return current_length;
|
||||
}
|
||||
41
src/rect.h
Normal file
41
src/rect.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct rect_t {
|
||||
int32_t x1;
|
||||
int32_t y1;
|
||||
int32_t x2;
|
||||
int32_t y2;
|
||||
} rect_t;
|
||||
|
||||
bool rect_intersection(rect_t a, rect_t b, rect_t *intersection);
|
||||
size_t rect_subtract(rect_t source, rect_t clip, rect_t remaining[4]);
|
||||
/**
|
||||
* @brief 从 source 中按顺序减去 clips 中的多个矩形
|
||||
* @param source 源矩形
|
||||
* @param clips 要减去的矩形数组,可为 nullptr
|
||||
* @param clip_count clips 的长度
|
||||
* @param remaining 输出剩余矩形数组;若不为 nullptr,则由函数分配内存并由调用方释放
|
||||
* @return 剩余矩形数量
|
||||
*
|
||||
* @code{.c}
|
||||
* rect_t source = {.x1 = 0, .y1 = 0, .x2 = 100, .y2 = 80};
|
||||
* rect_t clips[] = {
|
||||
* {.x1 = 10, .y1 = 10, .x2 = 40, .y2 = 40},
|
||||
* {.x1 = 30, .y1 = 20, .x2 = 70, .y2 = 60},
|
||||
* };
|
||||
*
|
||||
* rect_t *remaining = nullptr;
|
||||
* size_t count = rect_subtract_many(source, clips, 2, &remaining);
|
||||
*
|
||||
* for (size_t i = 0; i < count; i++) {
|
||||
* // use remaining[i]
|
||||
* }
|
||||
*
|
||||
* free(remaining);
|
||||
* @endcode
|
||||
*/
|
||||
size_t rect_subtract_many(rect_t source, const rect_t clips[],
|
||||
size_t clip_count, rect_t **remaining);
|
||||
Reference in New Issue
Block a user