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}/xres.c
|
||||||
${SOURCE_DIR}/status.c
|
${SOURCE_DIR}/status.c
|
||||||
${SOURCE_DIR}/audio.c
|
${SOURCE_DIR}/audio.c
|
||||||
|
${SOURCE_DIR}/rect.c
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
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);
|
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_maximize(const user_action_arg_t *arg);
|
||||||
void toggle_client_minimize(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 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 "atoms-extern.h"
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
|
#include "rect.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "wm.h"
|
#include "wm.h"
|
||||||
@@ -646,3 +647,50 @@ void client_stack_raise(client_t *client) {
|
|||||||
bool client_is_visible(client_t *client) {
|
bool client_is_visible(client_t *client) {
|
||||||
return client->tags & client->monitor->selected_tag->mask;
|
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);
|
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) {
|
static inline uint16_t client_width(client_t *c) {
|
||||||
return c->geometry.width + c->border_width * 2;
|
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[] = {
|
static const button_t button_list[] = {
|
||||||
{click_tag, modifier_none, button_left, select_tag_of_current_monitor, {0}},
|
{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) \
|
#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