mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-05-23 21:37:53 -04:00
opt: repl scroller layout
This commit is contained in:
parent
cabafb2393
commit
90a5d12ed4
8 changed files with 1427 additions and 925 deletions
|
|
@ -693,7 +693,6 @@ uint32_t parse_mod(const char *mod_str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 完整的 modifier 检查(保留原始所有检查项)
|
|
||||||
if (!strcmp(token, "super") || !strcmp(token, "super_l") ||
|
if (!strcmp(token, "super") || !strcmp(token, "super_l") ||
|
||||||
!strcmp(token, "super_r")) {
|
!strcmp(token, "super_r")) {
|
||||||
mod |= WLR_MODIFIER_LOGO;
|
mod |= WLR_MODIFIER_LOGO;
|
||||||
|
|
|
||||||
|
|
@ -629,15 +629,106 @@ int32_t set_proportion(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *tc = selmon->sel;
|
Client *tc = selmon->sel;
|
||||||
|
if (!tc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (tc) {
|
/* 获取堆叠头部客户端 */
|
||||||
tc = get_scroll_stack_head(tc);
|
tc = get_scroll_stack_head(tc);
|
||||||
uint32_t max_client_width =
|
if (!tc)
|
||||||
selmon->w.width - 2 * config.scroller_structs - config.gappih;
|
return 0;
|
||||||
tc->scroller_proportion = arg->f;
|
|
||||||
tc->geom.width = max_client_width * arg->f;
|
Monitor *m = tc->mon;
|
||||||
arrange(selmon, false, false);
|
uint32_t tag = m->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = m->pertag->scroller_state[tag];
|
||||||
|
struct ScrollerStackNode *node = NULL;
|
||||||
|
|
||||||
|
if (st)
|
||||||
|
node = find_scroller_node(st, tc);
|
||||||
|
|
||||||
|
/* 同时更新节点和客户端字段 */
|
||||||
|
if (node)
|
||||||
|
node->scroller_proportion = arg->f;
|
||||||
|
tc->scroller_proportion = arg->f;
|
||||||
|
|
||||||
|
/* 可选的即时几何更新,arrange 时会重新计算 */
|
||||||
|
uint32_t max_client_width =
|
||||||
|
m->w.width - 2 * config.scroller_structs - config.gappih;
|
||||||
|
tc->geom.width = max_client_width * arg->f;
|
||||||
|
|
||||||
|
arrange(m, false, false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t switch_proportion_preset(const Arg *arg) {
|
||||||
|
float target_proportion = 0;
|
||||||
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (config.scroller_proportion_preset_count == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (selmon->isoverview || !is_scroller_layout(selmon))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (selmon->visible_tiling_clients == 1 &&
|
||||||
|
!config.scroller_ignore_proportion_single)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Client *tc = selmon->sel;
|
||||||
|
if (!tc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tc = get_scroll_stack_head(tc);
|
||||||
|
if (!tc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Monitor *m = tc->mon;
|
||||||
|
uint32_t tag = m->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = m->pertag->scroller_state[tag];
|
||||||
|
struct ScrollerStackNode *node = NULL;
|
||||||
|
|
||||||
|
if (st)
|
||||||
|
node = find_scroller_node(st, tc);
|
||||||
|
|
||||||
|
/* 优先从节点读取当前比例,以确保切换基于正确的值 */
|
||||||
|
float current_proportion =
|
||||||
|
node ? node->scroller_proportion : tc->scroller_proportion;
|
||||||
|
|
||||||
|
/* 查找预设目标 */
|
||||||
|
for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) {
|
||||||
|
if (config.scroller_proportion_preset[i] == current_proportion) {
|
||||||
|
if (arg->i == NEXT) {
|
||||||
|
if (i == config.scroller_proportion_preset_count - 1)
|
||||||
|
target_proportion = config.scroller_proportion_preset[0];
|
||||||
|
else
|
||||||
|
target_proportion =
|
||||||
|
config.scroller_proportion_preset[i + 1];
|
||||||
|
} else {
|
||||||
|
if (i == 0)
|
||||||
|
target_proportion =
|
||||||
|
config.scroller_proportion_preset
|
||||||
|
[config.scroller_proportion_preset_count - 1];
|
||||||
|
else
|
||||||
|
target_proportion =
|
||||||
|
config.scroller_proportion_preset[i - 1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target_proportion == 0.0f)
|
||||||
|
target_proportion = config.scroller_proportion_preset[0];
|
||||||
|
|
||||||
|
/* 更新节点和客户端 */
|
||||||
|
if (node)
|
||||||
|
node->scroller_proportion = target_proportion;
|
||||||
|
tc->scroller_proportion = target_proportion;
|
||||||
|
|
||||||
|
uint32_t max_client_width =
|
||||||
|
m->w.width - 2 * config.scroller_structs - config.gappih;
|
||||||
|
tc->geom.width = max_client_width * target_proportion;
|
||||||
|
|
||||||
|
arrange(m, false, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1042,68 +1133,6 @@ int32_t switch_layout(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t switch_proportion_preset(const Arg *arg) {
|
|
||||||
float target_proportion = 0;
|
|
||||||
if (!selmon)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (config.scroller_proportion_preset_count == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selmon->isoverview || !is_scroller_layout(selmon))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (selmon->visible_tiling_clients == 1 &&
|
|
||||||
!config.scroller_ignore_proportion_single)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
Client *tc = selmon->sel;
|
|
||||||
|
|
||||||
if (tc) {
|
|
||||||
tc = get_scroll_stack_head(tc);
|
|
||||||
for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) {
|
|
||||||
if (config.scroller_proportion_preset[i] ==
|
|
||||||
tc->scroller_proportion) {
|
|
||||||
|
|
||||||
if (arg->i == NEXT) {
|
|
||||||
if (i == config.scroller_proportion_preset_count - 1) {
|
|
||||||
target_proportion =
|
|
||||||
config.scroller_proportion_preset[0];
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
target_proportion =
|
|
||||||
config.scroller_proportion_preset[i + 1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (i == 0) {
|
|
||||||
target_proportion =
|
|
||||||
config.scroller_proportion_preset
|
|
||||||
[config.scroller_proportion_preset_count - 1];
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
target_proportion =
|
|
||||||
config.scroller_proportion_preset[i - 1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target_proportion == 0) {
|
|
||||||
target_proportion = config.scroller_proportion_preset[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t max_client_width =
|
|
||||||
selmon->w.width - 2 * config.scroller_structs - config.gappih;
|
|
||||||
tc->scroller_proportion = target_proportion;
|
|
||||||
tc->geom.width = max_client_width * target_proportion;
|
|
||||||
arrange(selmon, false, false);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t tag(const Arg *arg) {
|
int32_t tag(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1202,7 +1231,6 @@ int32_t tagsilent(const Arg *arg) {
|
||||||
clear_fullscreen_flag(fc);
|
clear_fullscreen_flag(fc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exit_scroller_stack(target_client);
|
|
||||||
focusclient(focustop(selmon), 1);
|
focusclient(focustop(selmon), 1);
|
||||||
arrange(target_client->mon, false, false);
|
arrange(target_client->mon, false, false);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1355,7 +1383,6 @@ int32_t toggleglobal(const Arg *arg) {
|
||||||
selmon->sel->isglobal ^= 1;
|
selmon->sel->isglobal ^= 1;
|
||||||
if (selmon->sel->isglobal &&
|
if (selmon->sel->isglobal &&
|
||||||
(selmon->sel->prev_in_stack || selmon->sel->next_in_stack)) {
|
(selmon->sel->prev_in_stack || selmon->sel->next_in_stack)) {
|
||||||
exit_scroller_stack(selmon->sel);
|
|
||||||
arrange(selmon, false, false);
|
arrange(selmon, false, false);
|
||||||
}
|
}
|
||||||
setborder_color(selmon->sel);
|
setborder_color(selmon->sel);
|
||||||
|
|
@ -1780,76 +1807,69 @@ int32_t toggle_monitor(const Arg *arg) {
|
||||||
|
|
||||||
int32_t scroller_apply_stack(Client *c, Client *target_client,
|
int32_t scroller_apply_stack(Client *c, Client *target_client,
|
||||||
int32_t direction) {
|
int32_t direction) {
|
||||||
|
if (!c || !c->mon || c->isfloating || !is_scroller_layout(c->mon))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Client *source_stack_head = NULL;
|
Monitor *m = c->mon;
|
||||||
Client *stack_head = NULL;
|
uint32_t tag = m->pertag->curtag;
|
||||||
bool is_horizontal_layout =
|
struct TagScrollerState *st = ensure_scroller_state(m, tag);
|
||||||
c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true
|
|
||||||
: false;
|
|
||||||
|
|
||||||
if (target_client) {
|
/* 获取当前节点 */
|
||||||
stack_head = get_scroll_stack_head(target_client);
|
struct ScrollerStackNode *cnode = find_scroller_node(st, c);
|
||||||
}
|
struct ScrollerStackNode *tnode =
|
||||||
|
target_client ? find_scroller_node(st, target_client) : NULL;
|
||||||
|
|
||||||
source_stack_head = get_scroll_stack_head(c);
|
bool is_horizontal = (m->pertag->ltidxs[tag]->id == SCROLLER);
|
||||||
|
|
||||||
if (source_stack_head == stack_head) {
|
/* 若方向为 UNDIR 且有目标,直接插入到目标尾部 */
|
||||||
|
if (direction == UNDIR && target_client && target_client->mon == c->mon) {
|
||||||
|
scroller_insert_stack(c, target_client, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->isfullscreen) {
|
/* 处理从堆叠中移出的情况(方向 LEFT/UP 或 RIGHT/DOWN) */
|
||||||
setfullscreen(c, 0);
|
if (cnode && (cnode->prev_in_stack || cnode->next_in_stack)) {
|
||||||
}
|
bool to_left_or_up = (is_horizontal && direction == LEFT) ||
|
||||||
|
(!is_horizontal && direction == UP);
|
||||||
|
bool to_right_or_down = (is_horizontal && direction == RIGHT) ||
|
||||||
|
(!is_horizontal && direction == DOWN);
|
||||||
|
|
||||||
if (c->ismaximizescreen) {
|
if (to_left_or_up || to_right_or_down) {
|
||||||
setmaximizescreen(c, 0);
|
/* 找到当前堆叠的头节点,以便移动全局链表位置 */
|
||||||
}
|
struct ScrollerStackNode *head = cnode;
|
||||||
|
while (head->prev_in_stack)
|
||||||
|
head = head->prev_in_stack;
|
||||||
|
Client *source_stack_head = head->client;
|
||||||
|
|
||||||
if (c->prev_in_stack && direction != UNDIR) {
|
/* 从 tag 状态中移除该客户端对应的节点 */
|
||||||
if ((is_horizontal_layout && direction == LEFT) ||
|
scroller_node_remove(st, cnode);
|
||||||
(!is_horizontal_layout && direction == UP)) {
|
/* 重新创建一个独立的节点(无堆叠关系) */
|
||||||
exit_scroller_stack(c);
|
scroller_node_create(st, c);
|
||||||
|
|
||||||
|
/* 调整全局客户端链表顺序:移到源堆叠头的前面或后面 */
|
||||||
wl_list_remove(&c->link);
|
wl_list_remove(&c->link);
|
||||||
wl_list_insert(source_stack_head->link.prev, &c->link);
|
if (to_left_or_up)
|
||||||
arrange(selmon, false, false);
|
wl_list_insert(source_stack_head->link.prev, &c->link);
|
||||||
|
else
|
||||||
|
wl_list_insert(&source_stack_head->link, &c->link);
|
||||||
|
|
||||||
} else if ((is_horizontal_layout && direction == RIGHT) ||
|
/* 同步到客户端字段并重排 */
|
||||||
(!is_horizontal_layout && direction == DOWN)) {
|
sync_scroller_state_to_clients(m, tag);
|
||||||
exit_scroller_stack(c);
|
arrange(m, false, false);
|
||||||
wl_list_remove(&c->link);
|
return 0;
|
||||||
wl_list_insert(&source_stack_head->link, &c->link);
|
|
||||||
arrange(selmon, false, false);
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
} else if (c->next_in_stack && direction != UNDIR) {
|
|
||||||
Client *next_in_stack = c->next_in_stack;
|
|
||||||
if ((is_horizontal_layout && direction == LEFT) ||
|
|
||||||
(!is_horizontal_layout && direction == UP)) {
|
|
||||||
exit_scroller_stack(c);
|
|
||||||
wl_list_remove(&c->link);
|
|
||||||
wl_list_insert(next_in_stack->link.prev, &c->link);
|
|
||||||
arrange(selmon, false, false);
|
|
||||||
} else if ((is_horizontal_layout && direction == RIGHT) ||
|
|
||||||
(!is_horizontal_layout && direction == DOWN)) {
|
|
||||||
exit_scroller_stack(c);
|
|
||||||
wl_list_remove(&c->link);
|
|
||||||
wl_list_insert(&next_in_stack->link, &c->link);
|
|
||||||
arrange(selmon, false, false);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target_client || target_client->mon != c->mon) {
|
if (!target_client || target_client->mon != c->mon)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
// Find the tail of target_client's stack
|
/* 找到目标堆叠的尾部节点 */
|
||||||
Client *stack_tail = target_client;
|
struct ScrollerStackNode *tail = tnode;
|
||||||
while (stack_tail->next_in_stack) {
|
while (tail->next_in_stack)
|
||||||
stack_tail = stack_tail->next_in_stack;
|
tail = tail->next_in_stack;
|
||||||
}
|
|
||||||
|
|
||||||
scroller_insert_stack(c, stack_tail, false);
|
/* 通过封装好的插入函数实现(尾部插入) */
|
||||||
|
scroller_insert_stack(c, tail->client, false);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -539,15 +539,22 @@ bool client_only_in_one_tag(Client *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Client *get_scroll_stack_head(Client *c) {
|
Client *get_scroll_stack_head(Client *c) {
|
||||||
Client *scroller_stack_head = c;
|
if (!c || !c->mon)
|
||||||
|
return c;
|
||||||
if (!scroller_stack_head)
|
uint32_t tag = c->mon->pertag->curtag;
|
||||||
return NULL;
|
struct TagScrollerState *st = c->mon->pertag->scroller_state[tag];
|
||||||
|
if (st) {
|
||||||
while (scroller_stack_head->prev_in_stack) {
|
struct ScrollerStackNode *n = find_scroller_node(st, c);
|
||||||
scroller_stack_head = scroller_stack_head->prev_in_stack;
|
if (n) {
|
||||||
|
while (n->prev_in_stack)
|
||||||
|
n = n->prev_in_stack;
|
||||||
|
return n->client;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return scroller_stack_head;
|
/* 如果 tag 状态未初始化或节点丢失,使用 client 字段 */
|
||||||
|
while (c->prev_in_stack)
|
||||||
|
c = c->prev_in_stack;
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
|
bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
|
||||||
|
|
|
||||||
|
|
@ -505,36 +505,51 @@ void resize_tile_dwindle(Client *grabc, bool isdrag, int32_t offsetx,
|
||||||
|
|
||||||
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
|
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
|
||||||
int32_t offsety, uint32_t time, bool isvertical) {
|
int32_t offsety, uint32_t time, bool isvertical) {
|
||||||
Client *tc = NULL;
|
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
|
||||||
|
return;
|
||||||
|
if (grabc->mon->isoverview)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Monitor *m = grabc->mon;
|
||||||
|
uint32_t tag = m->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = m->pertag->scroller_state[tag];
|
||||||
|
if (!st)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct ScrollerStackNode *curnode = find_scroller_node(st, grabc);
|
||||||
|
if (!curnode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct ScrollerStackNode *headnode = curnode;
|
||||||
|
while (headnode->prev_in_stack)
|
||||||
|
headnode = headnode->prev_in_stack;
|
||||||
|
|
||||||
|
Client *stack_head_client = headnode->client;
|
||||||
|
|
||||||
|
if (m->visible_tiling_clients == 1 &&
|
||||||
|
!config.scroller_ignore_proportion_single)
|
||||||
|
return;
|
||||||
|
|
||||||
float delta_x, delta_y;
|
float delta_x, delta_y;
|
||||||
float new_scroller_proportion;
|
float new_scroller_proportion;
|
||||||
float new_stack_proportion;
|
float new_stack_proportion;
|
||||||
Client *stack_head = get_scroll_stack_head(grabc);
|
|
||||||
|
|
||||||
if (grabc && grabc->mon->visible_tiling_clients == 1 &&
|
|
||||||
!config.scroller_ignore_proportion_single)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!start_drag_window && isdrag) {
|
if (!start_drag_window && isdrag) {
|
||||||
drag_begin_cursorx = cursor->x;
|
drag_begin_cursorx = cursor->x;
|
||||||
drag_begin_cursory = cursor->y;
|
drag_begin_cursory = cursor->y;
|
||||||
start_drag_window = true;
|
start_drag_window = true;
|
||||||
|
|
||||||
// 记录初始状态
|
headnode->client->old_scroller_pproportion =
|
||||||
stack_head->old_scroller_pproportion = stack_head->scroller_proportion;
|
headnode->scroller_proportion;
|
||||||
grabc->old_stack_proportion = grabc->stack_proportion;
|
grabc->old_stack_proportion = curnode->stack_proportion;
|
||||||
|
|
||||||
grabc->cursor_in_left_half =
|
grabc->cursor_in_left_half =
|
||||||
cursor->x < grabc->geom.x + grabc->geom.width / 2;
|
cursor->x < grabc->geom.x + grabc->geom.width / 2;
|
||||||
grabc->cursor_in_upper_half =
|
grabc->cursor_in_upper_half =
|
||||||
cursor->y < grabc->geom.y + grabc->geom.height / 2;
|
cursor->y < grabc->geom.y + grabc->geom.height / 2;
|
||||||
// 记录初始几何信息
|
|
||||||
grabc->drag_begin_geom = grabc->geom;
|
grabc->drag_begin_geom = grabc->geom;
|
||||||
} else {
|
} else {
|
||||||
// 计算相对于屏幕尺寸的比例变化
|
|
||||||
// 计算相对于屏幕尺寸的比例变化
|
|
||||||
if (isdrag) {
|
if (isdrag) {
|
||||||
|
|
||||||
offsetx = cursor->x - drag_begin_cursorx;
|
offsetx = cursor->x - drag_begin_cursorx;
|
||||||
offsety = cursor->y - drag_begin_cursory;
|
offsety = cursor->y - drag_begin_cursory;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -542,37 +557,33 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
|
||||||
grabc->old_master_inner_per = grabc->master_inner_per;
|
grabc->old_master_inner_per = grabc->master_inner_per;
|
||||||
grabc->old_stack_inner_per = grabc->stack_inner_per;
|
grabc->old_stack_inner_per = grabc->stack_inner_per;
|
||||||
grabc->drag_begin_geom = grabc->geom;
|
grabc->drag_begin_geom = grabc->geom;
|
||||||
stack_head->old_scroller_pproportion =
|
stack_head_client->old_scroller_pproportion =
|
||||||
stack_head->scroller_proportion;
|
headnode->scroller_proportion;
|
||||||
grabc->old_stack_proportion = grabc->stack_proportion;
|
grabc->old_stack_proportion = curnode->stack_proportion;
|
||||||
grabc->cursor_in_upper_half = false;
|
grabc->cursor_in_upper_half = false;
|
||||||
grabc->cursor_in_left_half = false;
|
grabc->cursor_in_left_half = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isvertical) {
|
if (isvertical) {
|
||||||
delta_y = (float)(offsety) *
|
delta_y = (float)(offsety) *
|
||||||
(stack_head->old_scroller_pproportion) /
|
(headnode->client->old_scroller_pproportion) /
|
||||||
grabc->drag_begin_geom.height;
|
grabc->drag_begin_geom.height;
|
||||||
delta_x = (float)(offsetx) * (grabc->old_stack_proportion) /
|
delta_x = (float)(offsetx) * (grabc->old_stack_proportion) /
|
||||||
grabc->drag_begin_geom.width;
|
grabc->drag_begin_geom.width;
|
||||||
} else {
|
} else {
|
||||||
delta_x = (float)(offsetx) *
|
delta_x = (float)(offsetx) *
|
||||||
(stack_head->old_scroller_pproportion) /
|
(headnode->client->old_scroller_pproportion) /
|
||||||
grabc->drag_begin_geom.width;
|
grabc->drag_begin_geom.width;
|
||||||
delta_y = (float)(offsety) * (grabc->old_stack_proportion) /
|
delta_y = (float)(offsety) * (grabc->old_stack_proportion) /
|
||||||
grabc->drag_begin_geom.height;
|
grabc->drag_begin_geom.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool moving_up;
|
bool moving_up, moving_down, moving_left, moving_right;
|
||||||
bool moving_down;
|
|
||||||
bool moving_left;
|
|
||||||
bool moving_right;
|
|
||||||
|
|
||||||
if (!isdrag) {
|
if (!isdrag) {
|
||||||
moving_up = offsety < 0 ? true : false;
|
moving_up = offsety < 0;
|
||||||
moving_down = offsety > 0 ? true : false;
|
moving_down = offsety > 0;
|
||||||
moving_left = offsetx < 0 ? true : false;
|
moving_left = offsetx < 0;
|
||||||
moving_right = offsetx > 0 ? true : false;
|
moving_right = offsetx > 0;
|
||||||
} else {
|
} else {
|
||||||
moving_up = cursor->y < drag_begin_cursory;
|
moving_up = cursor->y < drag_begin_cursory;
|
||||||
moving_down = cursor->y > drag_begin_cursory;
|
moving_down = cursor->y > drag_begin_cursory;
|
||||||
|
|
@ -582,10 +593,8 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
|
||||||
|
|
||||||
if ((grabc->cursor_in_upper_half && moving_up) ||
|
if ((grabc->cursor_in_upper_half && moving_up) ||
|
||||||
(!grabc->cursor_in_upper_half && moving_down)) {
|
(!grabc->cursor_in_upper_half && moving_down)) {
|
||||||
// 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度
|
|
||||||
delta_y = fabsf(delta_y);
|
delta_y = fabsf(delta_y);
|
||||||
} else {
|
} else {
|
||||||
// 其他情况 → 减小高度
|
|
||||||
delta_y = -fabsf(delta_y);
|
delta_y = -fabsf(delta_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -597,99 +606,93 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isvertical) {
|
if (isvertical) {
|
||||||
if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) {
|
if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) {
|
||||||
delta_x = delta_x * -1.0f;
|
delta_x = delta_x * -1.0f;
|
||||||
}
|
}
|
||||||
if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) {
|
if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) {
|
||||||
if (moving_right) {
|
if (moving_right)
|
||||||
delta_x = -fabsf(delta_x);
|
delta_x = -fabsf(delta_x);
|
||||||
} else {
|
else
|
||||||
delta_x = fabsf(delta_x);
|
delta_x = fabsf(delta_x);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) {
|
if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) {
|
||||||
if (moving_left) {
|
if (moving_left)
|
||||||
delta_x = -fabsf(delta_x);
|
delta_x = -fabsf(delta_x);
|
||||||
} else {
|
else
|
||||||
delta_x = fabsf(delta_x);
|
delta_x = fabsf(delta_x);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isdrag) {
|
if (isdrag) {
|
||||||
if (moving_up) {
|
if (moving_up)
|
||||||
delta_y = -fabsf(delta_y);
|
delta_y = -fabsf(delta_y);
|
||||||
} else {
|
else
|
||||||
delta_y = fabsf(delta_y);
|
delta_y = fabsf(delta_y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) {
|
if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) {
|
||||||
delta_y = delta_y * -1.0f;
|
delta_y = delta_y * -1.0f;
|
||||||
}
|
}
|
||||||
if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) {
|
if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) {
|
||||||
if (moving_down) {
|
if (moving_down)
|
||||||
delta_y = -fabsf(delta_y);
|
delta_y = -fabsf(delta_y);
|
||||||
} else {
|
else
|
||||||
delta_y = fabsf(delta_y);
|
delta_y = fabsf(delta_y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) {
|
if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) {
|
||||||
if (moving_up) {
|
if (moving_up)
|
||||||
delta_y = -fabsf(delta_y);
|
delta_y = -fabsf(delta_y);
|
||||||
} else {
|
else
|
||||||
delta_y = fabsf(delta_y);
|
delta_y = fabsf(delta_y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isdrag) {
|
if (isdrag) {
|
||||||
if (moving_left) {
|
if (moving_left)
|
||||||
delta_x = -fabsf(delta_x);
|
delta_x = -fabsf(delta_x);
|
||||||
} else {
|
else
|
||||||
delta_x = fabsf(delta_x);
|
delta_x = fabsf(delta_x);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接设置新的比例,基于初始值 + 变化量
|
|
||||||
if (isvertical) {
|
if (isvertical) {
|
||||||
new_scroller_proportion =
|
new_scroller_proportion =
|
||||||
stack_head->old_scroller_pproportion + delta_y;
|
headnode->client->old_scroller_pproportion + delta_y;
|
||||||
new_stack_proportion = grabc->old_stack_proportion + delta_x;
|
new_stack_proportion = grabc->old_stack_proportion + delta_x;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
new_scroller_proportion =
|
new_scroller_proportion =
|
||||||
stack_head->old_scroller_pproportion + delta_x;
|
headnode->client->old_scroller_pproportion + delta_x;
|
||||||
new_stack_proportion = grabc->old_stack_proportion + delta_y;
|
new_stack_proportion = grabc->old_stack_proportion + delta_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用限制,确保比例在合理范围内
|
|
||||||
new_scroller_proportion =
|
new_scroller_proportion =
|
||||||
fmaxf(0.1f, fminf(1.0f, new_scroller_proportion));
|
fmaxf(0.1f, fminf(1.0f, new_scroller_proportion));
|
||||||
new_stack_proportion = fmaxf(0.1f, fminf(0.9f, new_stack_proportion));
|
new_stack_proportion = fmaxf(0.1f, fminf(0.9f, new_stack_proportion));
|
||||||
|
|
||||||
grabc->stack_proportion = new_stack_proportion;
|
curnode->stack_proportion = new_stack_proportion;
|
||||||
|
headnode->scroller_proportion = new_scroller_proportion;
|
||||||
|
|
||||||
stack_head->scroller_proportion = new_scroller_proportion;
|
/* 调整同一堆叠内其他节点的 stack_proportion */
|
||||||
|
/* 调整整个堆叠内除当前窗口外的所有节点 */
|
||||||
wl_list_for_each(tc, &clients, link) {
|
if (!isdrag) {
|
||||||
if (!isdrag && new_stack_proportion != 1.0f &&
|
for (struct ScrollerStackNode *tc = headnode; tc;
|
||||||
grabc->old_stack_proportion != 1.0f && tc != grabc &&
|
tc = tc->next_in_stack) {
|
||||||
ISTILED(tc) && get_scroll_stack_head(tc) == stack_head) {
|
if (tc != curnode) {
|
||||||
tc->stack_proportion = (1.0f - new_stack_proportion) /
|
tc->stack_proportion =
|
||||||
(1.0f - grabc->old_stack_proportion) *
|
(1.0f - new_stack_proportion) /
|
||||||
tc->stack_proportion;
|
(1.0f - grabc->old_stack_proportion) *
|
||||||
|
tc->stack_proportion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 同步回全局字段*/
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
|
||||||
if (!isdrag) {
|
if (!isdrag) {
|
||||||
arrange(grabc->mon, false, false);
|
arrange(m, false, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_apply_drap_time == 0 ||
|
if (last_apply_drap_time == 0 ||
|
||||||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
|
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
|
||||||
arrange(grabc->mon, false, false);
|
arrange(m, false, false);
|
||||||
last_apply_drap_time = time;
|
last_apply_drap_time = time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -834,11 +837,10 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation,
|
||||||
m->visible_tiling_clients = 0;
|
m->visible_tiling_clients = 0;
|
||||||
m->visible_scroll_tiling_clients = 0;
|
m->visible_scroll_tiling_clients = 0;
|
||||||
|
|
||||||
wl_list_for_each(c, &clients, link) {
|
uint32_t tag = m->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = m->pertag->scroller_state[tag];
|
||||||
|
|
||||||
if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) {
|
wl_list_for_each(c, &clients, link) {
|
||||||
exit_scroller_stack(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from_view && (c->isglobal || c->isunglobal)) {
|
if (from_view && (c->isglobal || c->isunglobal)) {
|
||||||
set_size_per(m, c);
|
set_size_per(m, c);
|
||||||
|
|
@ -862,10 +864,16 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation,
|
||||||
|
|
||||||
if (ISTILED(c)) {
|
if (ISTILED(c)) {
|
||||||
m->visible_tiling_clients++;
|
m->visible_tiling_clients++;
|
||||||
}
|
|
||||||
|
|
||||||
if (ISSCROLLTILED(c) && !c->prev_in_stack) {
|
/* 更新可见滚动客户端计数 */
|
||||||
m->visible_scroll_tiling_clients++;
|
if (st) {
|
||||||
|
struct ScrollerStackNode *n = find_scroller_node(st, c);
|
||||||
|
if (n && !n->prev_in_stack) /* 是堆叠头部 */
|
||||||
|
m->visible_scroll_tiling_clients++;
|
||||||
|
} else {
|
||||||
|
if (ISSCROLLTILED(c) && !c->prev_in_stack)
|
||||||
|
m->visible_scroll_tiling_clients++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -879,7 +887,6 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation,
|
||||||
if (c->mon == m) {
|
if (c->mon == m) {
|
||||||
if (VISIBLEON(c, m)) {
|
if (VISIBLEON(c, m)) {
|
||||||
if (ISTILED(c)) {
|
if (ISTILED(c)) {
|
||||||
|
|
||||||
if (i < nmasters) {
|
if (i < nmasters) {
|
||||||
master_num++;
|
master_num++;
|
||||||
total_master_inner_percent += c->master_inner_per;
|
total_master_inner_percent += c->master_inner_per;
|
||||||
|
|
@ -898,7 +905,6 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation,
|
||||||
c->stack_inner_per;
|
c->stack_inner_per;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,325 +184,6 @@ void deck(Monitor *m) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void horizontal_scroll_adjust_fullandmax(Client *c,
|
|
||||||
struct wlr_box *target_geom) {
|
|
||||||
Monitor *m = c->mon;
|
|
||||||
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
|
||||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
|
||||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
|
||||||
|
|
||||||
cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappih;
|
|
||||||
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappoh;
|
|
||||||
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappov;
|
|
||||||
|
|
||||||
if (c->isfullscreen) {
|
|
||||||
target_geom->height = m->m.height;
|
|
||||||
target_geom->width = m->m.width;
|
|
||||||
target_geom->y = m->m.y;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->ismaximizescreen) {
|
|
||||||
target_geom->height = m->w.height - 2 * cur_gappov;
|
|
||||||
target_geom->width = m->w.width - 2 * cur_gappoh;
|
|
||||||
target_geom->y = m->w.y + cur_gappov;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
target_geom->height = m->w.height - 2 * cur_gappov;
|
|
||||||
target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arrange_stack(Client *scroller_stack_head, struct wlr_box geometry,
|
|
||||||
int32_t gappiv) {
|
|
||||||
int32_t stack_size = 0;
|
|
||||||
Client *iter = scroller_stack_head;
|
|
||||||
|
|
||||||
while (iter) {
|
|
||||||
stack_size++;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stack_size == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
float total_proportion = 0.0f;
|
|
||||||
iter = scroller_stack_head;
|
|
||||||
while (iter) {
|
|
||||||
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) {
|
|
||||||
iter->stack_proportion =
|
|
||||||
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
|
|
||||||
}
|
|
||||||
total_proportion += iter->stack_proportion;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
iter = scroller_stack_head;
|
|
||||||
while (iter) {
|
|
||||||
iter->stack_proportion = iter->stack_proportion / total_proportion;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t client_height;
|
|
||||||
int32_t current_y = geometry.y;
|
|
||||||
int32_t remain_client_height = geometry.height - (stack_size - 1) * gappiv;
|
|
||||||
float remain_proportion = 1.0f;
|
|
||||||
|
|
||||||
iter = scroller_stack_head;
|
|
||||||
while (iter) {
|
|
||||||
|
|
||||||
client_height =
|
|
||||||
remain_client_height * (iter->stack_proportion / remain_proportion);
|
|
||||||
|
|
||||||
struct wlr_box client_geom = {.x = geometry.x,
|
|
||||||
.y = current_y,
|
|
||||||
.width = geometry.width,
|
|
||||||
.height = client_height};
|
|
||||||
resize(iter, client_geom, 0);
|
|
||||||
remain_proportion -= iter->stack_proportion;
|
|
||||||
remain_client_height -= client_height;
|
|
||||||
current_y += client_height + gappiv;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void horizontal_check_scroller_root_inside_mon(Client *c,
|
|
||||||
struct wlr_box *geometry) {
|
|
||||||
if (!GEOMINSIDEMON(geometry, c->mon)) {
|
|
||||||
geometry->x = c->mon->w.x + (c->mon->w.width - geometry->width) / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 滚动布局
|
|
||||||
void scroller(Monitor *m) {
|
|
||||||
int32_t i, n, j;
|
|
||||||
float single_proportion = 1.0;
|
|
||||||
|
|
||||||
Client *c = NULL, *root_client = NULL;
|
|
||||||
Client **tempClients = NULL; // 初始化为 NULL
|
|
||||||
struct wlr_box target_geom;
|
|
||||||
int32_t focus_client_index = 0;
|
|
||||||
bool need_scroller = false;
|
|
||||||
bool over_overspread_to_left = false;
|
|
||||||
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
|
||||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
|
||||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
|
||||||
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
|
||||||
|
|
||||||
cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappih;
|
|
||||||
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappoh;
|
|
||||||
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappov;
|
|
||||||
|
|
||||||
int32_t max_client_width =
|
|
||||||
m->w.width - 2 * config.scroller_structs - cur_gappih;
|
|
||||||
|
|
||||||
n = m->visible_scroll_tiling_clients;
|
|
||||||
|
|
||||||
if (n == 0) {
|
|
||||||
return; // 没有需要处理的客户端,直接返回
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动态分配内存
|
|
||||||
tempClients = malloc(n * sizeof(Client *));
|
|
||||||
if (!tempClients) {
|
|
||||||
// 处理内存分配失败的情况
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第二次遍历,填充 tempClients
|
|
||||||
j = 0;
|
|
||||||
wl_list_for_each(c, &clients, link) {
|
|
||||||
if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
|
|
||||||
tempClients[j] = c;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == 1 && !config.scroller_ignore_proportion_single &&
|
|
||||||
!tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) {
|
|
||||||
c = tempClients[0];
|
|
||||||
|
|
||||||
single_proportion = c->scroller_proportion_single > 0.0f
|
|
||||||
? c->scroller_proportion_single
|
|
||||||
: config.scroller_default_proportion_single;
|
|
||||||
|
|
||||||
target_geom.height = m->w.height - 2 * cur_gappov;
|
|
||||||
target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion;
|
|
||||||
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
|
||||||
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
|
||||||
horizontal_check_scroller_root_inside_mon(c, &target_geom);
|
|
||||||
arrange_stack(c, target_geom, cur_gappiv);
|
|
||||||
free(tempClients); // 释放内存
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) {
|
|
||||||
root_client = m->sel;
|
|
||||||
} else if (m->prevsel && ISSCROLLTILED(m->prevsel) &&
|
|
||||||
VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) {
|
|
||||||
root_client = m->prevsel;
|
|
||||||
} else {
|
|
||||||
root_client = center_tiled_select(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
// root_client might be in a stack, find the stack head
|
|
||||||
if (root_client) {
|
|
||||||
root_client = get_scroll_stack_head(root_client);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root_client) {
|
|
||||||
free(tempClients); // 释放内存
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
c = tempClients[i];
|
|
||||||
if (root_client == c) {
|
|
||||||
if (c->geom.x >= m->w.x + config.scroller_structs &&
|
|
||||||
c->geom.x + c->geom.width <=
|
|
||||||
m->w.x + m->w.width - config.scroller_structs) {
|
|
||||||
need_scroller = false;
|
|
||||||
} else {
|
|
||||||
need_scroller = true;
|
|
||||||
}
|
|
||||||
focus_client_index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool need_apply_overspread =
|
|
||||||
config.scroller_prefer_overspread &&
|
|
||||||
m->visible_scroll_tiling_clients > 1 &&
|
|
||||||
(focus_client_index == 0 || focus_client_index == n - 1) &&
|
|
||||||
tempClients[focus_client_index]->scroller_proportion < 1.0f;
|
|
||||||
|
|
||||||
if (need_apply_overspread) {
|
|
||||||
|
|
||||||
if (focus_client_index == 0) {
|
|
||||||
over_overspread_to_left = true;
|
|
||||||
} else {
|
|
||||||
over_overspread_to_left = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (over_overspread_to_left &&
|
|
||||||
(!INSIDEMON(tempClients[1]) ||
|
|
||||||
(tempClients[1]->scroller_proportion +
|
|
||||||
tempClients[focus_client_index]->scroller_proportion >=
|
|
||||||
1.0f))) {
|
|
||||||
need_scroller = true;
|
|
||||||
} else if (!over_overspread_to_left &&
|
|
||||||
(!INSIDEMON(tempClients[focus_client_index - 1]) ||
|
|
||||||
(tempClients[focus_client_index - 1]->scroller_proportion +
|
|
||||||
tempClients[focus_client_index]->scroller_proportion >=
|
|
||||||
1.0f))) {
|
|
||||||
need_scroller = true;
|
|
||||||
} else {
|
|
||||||
need_apply_overspread = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool need_apply_center =
|
|
||||||
config.scroller_focus_center || m->visible_scroll_tiling_clients == 1 ||
|
|
||||||
(config.scroller_prefer_center && !need_apply_overspread &&
|
|
||||||
(!m->prevsel ||
|
|
||||||
(ISSCROLLTILED(m->prevsel) &&
|
|
||||||
(m->prevsel->scroller_proportion * max_client_width) +
|
|
||||||
(tempClients[focus_client_index]->scroller_proportion *
|
|
||||||
max_client_width) >
|
|
||||||
m->w.width - 2 * config.scroller_structs - cur_gappih)));
|
|
||||||
|
|
||||||
if (n == 1 && config.scroller_ignore_proportion_single) {
|
|
||||||
need_scroller = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start_drag_window)
|
|
||||||
need_scroller = false;
|
|
||||||
|
|
||||||
target_geom.height = m->w.height - 2 * cur_gappov;
|
|
||||||
target_geom.width = max_client_width * c->scroller_proportion;
|
|
||||||
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
|
||||||
horizontal_scroll_adjust_fullandmax(tempClients[focus_client_index],
|
|
||||||
&target_geom);
|
|
||||||
if (tempClients[focus_client_index]->isfullscreen) {
|
|
||||||
target_geom.x = m->m.x;
|
|
||||||
horizontal_check_scroller_root_inside_mon(
|
|
||||||
tempClients[focus_client_index], &target_geom);
|
|
||||||
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
|
|
||||||
} else if (tempClients[focus_client_index]->ismaximizescreen) {
|
|
||||||
target_geom.x = m->w.x + cur_gappoh;
|
|
||||||
horizontal_check_scroller_root_inside_mon(
|
|
||||||
tempClients[focus_client_index], &target_geom);
|
|
||||||
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
|
|
||||||
} else if (need_scroller) {
|
|
||||||
if (need_apply_center) {
|
|
||||||
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
|
||||||
} else if (need_apply_overspread) {
|
|
||||||
if (over_overspread_to_left) {
|
|
||||||
target_geom.x = m->w.x + config.scroller_structs;
|
|
||||||
} else {
|
|
||||||
target_geom.x =
|
|
||||||
m->w.x +
|
|
||||||
(m->w.width -
|
|
||||||
tempClients[focus_client_index]->scroller_proportion *
|
|
||||||
max_client_width -
|
|
||||||
config.scroller_structs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
target_geom.x = tempClients[focus_client_index]->geom.x >
|
|
||||||
m->w.x + (m->w.width) / 2
|
|
||||||
? m->w.x + (m->w.width -
|
|
||||||
tempClients[focus_client_index]
|
|
||||||
->scroller_proportion *
|
|
||||||
max_client_width -
|
|
||||||
config.scroller_structs)
|
|
||||||
: m->w.x + config.scroller_structs;
|
|
||||||
}
|
|
||||||
horizontal_check_scroller_root_inside_mon(
|
|
||||||
tempClients[focus_client_index], &target_geom);
|
|
||||||
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
|
|
||||||
} else {
|
|
||||||
target_geom.x = c->geom.x;
|
|
||||||
horizontal_check_scroller_root_inside_mon(
|
|
||||||
tempClients[focus_client_index], &target_geom);
|
|
||||||
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i <= focus_client_index; i++) {
|
|
||||||
c = tempClients[focus_client_index - i];
|
|
||||||
target_geom.width = max_client_width * c->scroller_proportion;
|
|
||||||
horizontal_scroll_adjust_fullandmax(c, &target_geom);
|
|
||||||
target_geom.x = tempClients[focus_client_index - i + 1]->geom.x -
|
|
||||||
cur_gappih - target_geom.width;
|
|
||||||
|
|
||||||
arrange_stack(c, target_geom, cur_gappiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i < n - focus_client_index; i++) {
|
|
||||||
c = tempClients[focus_client_index + i];
|
|
||||||
target_geom.width = max_client_width * c->scroller_proportion;
|
|
||||||
horizontal_scroll_adjust_fullandmax(c, &target_geom);
|
|
||||||
target_geom.x = tempClients[focus_client_index + i - 1]->geom.x +
|
|
||||||
cur_gappih +
|
|
||||||
tempClients[focus_client_index + i - 1]->geom.width;
|
|
||||||
arrange_stack(c, target_geom, cur_gappiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(tempClients); // 最后释放内存
|
|
||||||
}
|
|
||||||
|
|
||||||
void center_tile(Monitor *m) {
|
void center_tile(Monitor *m) {
|
||||||
int32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw;
|
int32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw;
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
|
|
|
||||||
829
src/layout/scroll.h
Normal file
829
src/layout/scroll.h
Normal file
|
|
@ -0,0 +1,829 @@
|
||||||
|
/* 获取或创建指定 monitor 某个 tag 的 scroller 状态 */
|
||||||
|
static struct TagScrollerState *ensure_scroller_state(Monitor *m,
|
||||||
|
uint32_t tag) {
|
||||||
|
if (!m->pertag->scroller_state[tag]) {
|
||||||
|
struct TagScrollerState *st =
|
||||||
|
calloc(1, sizeof(struct TagScrollerState));
|
||||||
|
m->pertag->scroller_state[tag] = st;
|
||||||
|
}
|
||||||
|
return m->pertag->scroller_state[tag];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在 tag 状态中查找客户端对应的节点(无则返回 NULL) */
|
||||||
|
static struct ScrollerStackNode *find_scroller_node(struct TagScrollerState *st,
|
||||||
|
Client *c) {
|
||||||
|
if (!st)
|
||||||
|
return NULL;
|
||||||
|
for (struct ScrollerStackNode *n = st->all_first; n; n = n->all_next)
|
||||||
|
if (n->client == c)
|
||||||
|
return n;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 创建一个新节点并插入到 tag 状态的 all 链表中 */
|
||||||
|
static struct ScrollerStackNode *
|
||||||
|
scroller_node_create(struct TagScrollerState *st, Client *c) {
|
||||||
|
struct ScrollerStackNode *n = calloc(1, sizeof(*n));
|
||||||
|
n->client = c;
|
||||||
|
n->scroller_proportion = c->scroller_proportion;
|
||||||
|
n->stack_proportion = c->stack_proportion;
|
||||||
|
n->scroller_proportion_single = c->scroller_proportion_single;
|
||||||
|
n->next_in_stack = NULL;
|
||||||
|
n->prev_in_stack = NULL;
|
||||||
|
n->all_next = st->all_first;
|
||||||
|
st->all_first = n;
|
||||||
|
st->count++;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 从 tag 状态中移除一个节点并释放 */
|
||||||
|
static void scroller_node_remove(struct TagScrollerState *st,
|
||||||
|
struct ScrollerStackNode *target) {
|
||||||
|
if (!st || !target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* 保存邻居 */
|
||||||
|
struct ScrollerStackNode *prev = target->prev_in_stack;
|
||||||
|
struct ScrollerStackNode *next = target->next_in_stack;
|
||||||
|
|
||||||
|
/* 从堆叠链表中摘除 */
|
||||||
|
if (prev)
|
||||||
|
prev->next_in_stack = next;
|
||||||
|
if (next)
|
||||||
|
next->prev_in_stack = prev;
|
||||||
|
|
||||||
|
/* 清空目标客户端的堆叠指针,使其彻底脱离 */
|
||||||
|
if (target->client) {
|
||||||
|
target->client->prev_in_stack = NULL;
|
||||||
|
target->client->next_in_stack = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 从 all 链表摘除 */
|
||||||
|
struct ScrollerStackNode **indirect = &st->all_first;
|
||||||
|
while (*indirect && *indirect != target)
|
||||||
|
indirect = &(*indirect)->all_next;
|
||||||
|
if (*indirect == target) {
|
||||||
|
*indirect = target->all_next;
|
||||||
|
st->count--;
|
||||||
|
}
|
||||||
|
free(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 清空一个 tag 的全部 scroller 状态 */
|
||||||
|
static void clear_scroller_state(struct TagScrollerState *st) {
|
||||||
|
if (!st)
|
||||||
|
return;
|
||||||
|
struct ScrollerStackNode *n = st->all_first;
|
||||||
|
while (n) {
|
||||||
|
struct ScrollerStackNode *next = n->all_next;
|
||||||
|
free(n);
|
||||||
|
n = next;
|
||||||
|
}
|
||||||
|
free(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在 Monitor 销毁时清理所有 tag 的 scroller 状态 */
|
||||||
|
static void cleanup_monitor_scroller(Monitor *m) {
|
||||||
|
for (int t = 0; t < LENGTH(tags) + 1; t++) {
|
||||||
|
if (m->pertag->scroller_state[t]) {
|
||||||
|
clear_scroller_state(m->pertag->scroller_state[t]);
|
||||||
|
m->pertag->scroller_state[t] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 将某个 tag 的状态同步回所有客户端的全局字段 */
|
||||||
|
static void sync_scroller_state_to_clients(Monitor *m, uint32_t tag) {
|
||||||
|
struct TagScrollerState *st = m->pertag->scroller_state[tag];
|
||||||
|
if (!st)
|
||||||
|
return;
|
||||||
|
for (struct ScrollerStackNode *n = st->all_first; n; n = n->all_next) {
|
||||||
|
Client *c = n->client;
|
||||||
|
c->scroller_proportion = n->scroller_proportion;
|
||||||
|
c->stack_proportion = n->stack_proportion;
|
||||||
|
c->scroller_proportion_single = n->scroller_proportion_single;
|
||||||
|
c->prev_in_stack = n->prev_in_stack ? n->prev_in_stack->client : NULL;
|
||||||
|
c->next_in_stack = n->next_in_stack ? n->next_in_stack->client : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) {
|
||||||
|
Monitor *m = c->mon;
|
||||||
|
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||||
|
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||||
|
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||||
|
|
||||||
|
cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
||||||
|
? 0
|
||||||
|
: cur_gappiv;
|
||||||
|
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
||||||
|
? 0
|
||||||
|
: cur_gappov;
|
||||||
|
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
||||||
|
? 0
|
||||||
|
: cur_gappoh;
|
||||||
|
|
||||||
|
if (c->isfullscreen) {
|
||||||
|
target_geom->width = m->m.width;
|
||||||
|
target_geom->height = m->m.height;
|
||||||
|
target_geom->x = m->m.x;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->ismaximizescreen) {
|
||||||
|
target_geom->width = m->w.width - 2 * cur_gappoh;
|
||||||
|
target_geom->height = m->w.height - 2 * cur_gappov;
|
||||||
|
target_geom->x = m->w.x + cur_gappoh;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_geom->width = m->w.width - 2 * cur_gappoh;
|
||||||
|
target_geom->x = m->w.x + (m->w.width - target_geom->width) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertical_check_scroller_root_inside_mon(Client *c,
|
||||||
|
struct wlr_box *geometry) {
|
||||||
|
if (!GEOMINSIDEMON(geometry, c->mon)) {
|
||||||
|
geometry->y = c->mon->w.y + (c->mon->w.height - geometry->height) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void horizontal_scroll_adjust_fullandmax(Client *c,
|
||||||
|
struct wlr_box *target_geom) {
|
||||||
|
Monitor *m = c->mon;
|
||||||
|
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
||||||
|
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||||
|
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||||
|
|
||||||
|
cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
||||||
|
? 0
|
||||||
|
: cur_gappih;
|
||||||
|
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
||||||
|
? 0
|
||||||
|
: cur_gappoh;
|
||||||
|
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
||||||
|
? 0
|
||||||
|
: cur_gappov;
|
||||||
|
|
||||||
|
if (c->isfullscreen) {
|
||||||
|
target_geom->height = m->m.height;
|
||||||
|
target_geom->width = m->m.width;
|
||||||
|
target_geom->y = m->m.y;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->ismaximizescreen) {
|
||||||
|
target_geom->height = m->w.height - 2 * cur_gappov;
|
||||||
|
target_geom->width = m->w.width - 2 * cur_gappoh;
|
||||||
|
target_geom->y = m->w.y + cur_gappov;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_geom->height = m->w.height - 2 * cur_gappov;
|
||||||
|
target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void horizontal_check_scroller_root_inside_mon(Client *c,
|
||||||
|
struct wlr_box *geometry) {
|
||||||
|
if (!GEOMINSIDEMON(geometry, c->mon)) {
|
||||||
|
geometry->x = c->mon->w.x + (c->mon->w.width - geometry->width) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrange_stack_node(struct ScrollerStackNode *head, struct wlr_box geometry,
|
||||||
|
int32_t gappiv) {
|
||||||
|
int32_t stack_size = 0;
|
||||||
|
struct ScrollerStackNode *iter = head;
|
||||||
|
while (iter) {
|
||||||
|
stack_size++;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
if (stack_size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* 归一化比例 */
|
||||||
|
float total_proportion = 0.0f;
|
||||||
|
iter = head;
|
||||||
|
while (iter) {
|
||||||
|
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f)
|
||||||
|
iter->stack_proportion =
|
||||||
|
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
|
||||||
|
total_proportion += iter->stack_proportion;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
iter = head;
|
||||||
|
while (iter) {
|
||||||
|
iter->stack_proportion /= total_proportion;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 竖向排列(水平堆叠) */
|
||||||
|
int32_t client_height;
|
||||||
|
int32_t current_y = geometry.y;
|
||||||
|
int32_t remain_client_height = geometry.height - (stack_size - 1) * gappiv;
|
||||||
|
float remain_proportion = 1.0f;
|
||||||
|
|
||||||
|
iter = head;
|
||||||
|
while (iter) {
|
||||||
|
client_height =
|
||||||
|
remain_client_height * (iter->stack_proportion / remain_proportion);
|
||||||
|
struct wlr_box client_geom = {.x = geometry.x,
|
||||||
|
.y = current_y,
|
||||||
|
.width = geometry.width,
|
||||||
|
.height = client_height};
|
||||||
|
resize(iter->client, client_geom, 0);
|
||||||
|
remain_proportion -= iter->stack_proportion;
|
||||||
|
remain_client_height -= client_height;
|
||||||
|
current_y += client_height + gappiv;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrange_stack_vertical_node(struct ScrollerStackNode *head,
|
||||||
|
struct wlr_box geometry, int32_t gappih) {
|
||||||
|
int32_t stack_size = 0;
|
||||||
|
struct ScrollerStackNode *iter = head;
|
||||||
|
while (iter) {
|
||||||
|
stack_size++;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
if (stack_size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* 归一化比例 */
|
||||||
|
float total_proportion = 0.0f;
|
||||||
|
iter = head;
|
||||||
|
while (iter) {
|
||||||
|
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f)
|
||||||
|
iter->stack_proportion =
|
||||||
|
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
|
||||||
|
total_proportion += iter->stack_proportion;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
iter = head;
|
||||||
|
while (iter) {
|
||||||
|
iter->stack_proportion /= total_proportion;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 横向排列(垂直堆叠) */
|
||||||
|
int32_t client_width;
|
||||||
|
int32_t current_x = geometry.x;
|
||||||
|
int32_t remain_client_width = geometry.width - (stack_size - 1) * gappih;
|
||||||
|
float remain_proportion = 1.0f;
|
||||||
|
|
||||||
|
iter = head;
|
||||||
|
while (iter) {
|
||||||
|
client_width =
|
||||||
|
remain_client_width * (iter->stack_proportion / remain_proportion);
|
||||||
|
struct wlr_box client_geom = {.y = geometry.y,
|
||||||
|
.x = current_x,
|
||||||
|
.height = geometry.height,
|
||||||
|
.width = client_width};
|
||||||
|
resize(iter->client, client_geom, 0);
|
||||||
|
remain_proportion -= iter->stack_proportion;
|
||||||
|
remain_client_width -= client_width;
|
||||||
|
current_x += client_width + gappih;
|
||||||
|
iter = iter->next_in_stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroller(Monitor *m) {
|
||||||
|
uint32_t tag = m->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = ensure_scroller_state(m, tag);
|
||||||
|
|
||||||
|
/* 收集当前可见的所有 scroller 平铺窗口 */
|
||||||
|
Client *vis[512];
|
||||||
|
int32_t count = 0;
|
||||||
|
Client *c;
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (VISIBLEON(c, m) && ISSCROLLTILED(c))
|
||||||
|
vis[count++] = c;
|
||||||
|
if (count == 512)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除不再可见的节点 */
|
||||||
|
struct ScrollerStackNode *n = st->all_first;
|
||||||
|
while (n) {
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (vis[i] == n->client) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct ScrollerStackNode *next = n->all_next;
|
||||||
|
if (!found)
|
||||||
|
scroller_node_remove(st, n);
|
||||||
|
n = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为新的可见窗口创建节点,并尝试恢复堆叠关系 */
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (!find_scroller_node(st, vis[i])) {
|
||||||
|
struct ScrollerStackNode *new_node =
|
||||||
|
scroller_node_create(st, vis[i]);
|
||||||
|
Client *prev = vis[i]->prev_in_stack;
|
||||||
|
if (prev) {
|
||||||
|
struct ScrollerStackNode *prev_node =
|
||||||
|
find_scroller_node(st, prev);
|
||||||
|
if (prev_node) {
|
||||||
|
new_node->prev_in_stack = prev_node;
|
||||||
|
prev_node->next_in_stack = new_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按全局客户端链表顺序收集所有堆叠头,确保视觉顺序正确 */
|
||||||
|
struct ScrollerStackNode *heads[64];
|
||||||
|
int32_t n_heads = 0;
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (VISIBLEON(c, m) && ISSCROLLTILED(c)) {
|
||||||
|
struct ScrollerStackNode *node = find_scroller_node(st, c);
|
||||||
|
if (node && !node->prev_in_stack) {
|
||||||
|
bool already = false;
|
||||||
|
for (int k = 0; k < n_heads; k++) {
|
||||||
|
if (heads[k] == node) {
|
||||||
|
already = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!already)
|
||||||
|
heads[n_heads++] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_heads == 0) {
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m->visible_scroll_tiling_clients = n_heads;
|
||||||
|
|
||||||
|
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
||||||
|
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||||
|
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||||
|
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||||
|
if (config.smartgaps && n_heads == 1) {
|
||||||
|
cur_gappih = cur_gappoh = cur_gappov = 0;
|
||||||
|
}
|
||||||
|
int32_t max_client_width =
|
||||||
|
m->w.width - 2 * config.scroller_structs - cur_gappih;
|
||||||
|
|
||||||
|
/* 单客户端特例 */
|
||||||
|
if (n_heads == 1 && !config.scroller_ignore_proportion_single &&
|
||||||
|
!heads[0]->client->isfullscreen &&
|
||||||
|
!heads[0]->client->ismaximizescreen) {
|
||||||
|
struct ScrollerStackNode *head = heads[0];
|
||||||
|
float single_proportion =
|
||||||
|
head->scroller_proportion_single > 0.0f
|
||||||
|
? head->scroller_proportion_single
|
||||||
|
: config.scroller_default_proportion_single;
|
||||||
|
struct wlr_box target_geom;
|
||||||
|
target_geom.height = m->w.height - 2 * cur_gappov;
|
||||||
|
target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion;
|
||||||
|
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
||||||
|
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
||||||
|
horizontal_check_scroller_root_inside_mon(head->client, &target_geom);
|
||||||
|
arrange_stack_node(head, target_geom, cur_gappiv);
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScrollerStackNode *root_node = NULL;
|
||||||
|
if (m->sel && ISSCROLLTILED(m->sel)) {
|
||||||
|
root_node = find_scroller_node(st, m->sel);
|
||||||
|
if (root_node) {
|
||||||
|
while (root_node->prev_in_stack)
|
||||||
|
root_node = root_node->prev_in_stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!root_node && m->prevsel && ISSCROLLTILED(m->prevsel)) {
|
||||||
|
root_node = find_scroller_node(st, m->prevsel);
|
||||||
|
if (root_node) {
|
||||||
|
while (root_node->prev_in_stack)
|
||||||
|
root_node = root_node->prev_in_stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!root_node)
|
||||||
|
root_node = heads[n_heads / 2]; /* 简单回退 */
|
||||||
|
|
||||||
|
int32_t focus_index = -1;
|
||||||
|
for (int i = 0; i < n_heads; i++) {
|
||||||
|
if (heads[i] == root_node) {
|
||||||
|
focus_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (focus_index < 0)
|
||||||
|
focus_index = n_heads / 2;
|
||||||
|
|
||||||
|
/* 判断是否需要滚动、overspread、center */
|
||||||
|
bool need_scroller = false;
|
||||||
|
bool over_overspread_to_left = false;
|
||||||
|
Client *root_client = root_node->client;
|
||||||
|
|
||||||
|
if (root_client->geom.x >= m->w.x + config.scroller_structs &&
|
||||||
|
root_client->geom.x + root_client->geom.width <=
|
||||||
|
m->w.x + m->w.width - config.scroller_structs) {
|
||||||
|
need_scroller = false;
|
||||||
|
} else {
|
||||||
|
need_scroller = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool need_apply_overspread =
|
||||||
|
config.scroller_prefer_overspread && n_heads > 1 &&
|
||||||
|
(focus_index == 0 || focus_index == n_heads - 1) &&
|
||||||
|
heads[focus_index]->scroller_proportion < 1.0f;
|
||||||
|
|
||||||
|
if (need_apply_overspread) {
|
||||||
|
if (focus_index == 0) {
|
||||||
|
over_overspread_to_left = true;
|
||||||
|
} else {
|
||||||
|
over_overspread_to_left = false;
|
||||||
|
}
|
||||||
|
if (over_overspread_to_left &&
|
||||||
|
(!INSIDEMON(heads[1]->client) ||
|
||||||
|
(heads[1]->scroller_proportion + heads[0]->scroller_proportion >=
|
||||||
|
1.0f))) {
|
||||||
|
need_scroller = true;
|
||||||
|
} else if (!over_overspread_to_left &&
|
||||||
|
(!INSIDEMON(heads[focus_index - 1]->client) ||
|
||||||
|
(heads[focus_index - 1]->scroller_proportion +
|
||||||
|
heads[focus_index]->scroller_proportion >=
|
||||||
|
1.0f))) {
|
||||||
|
need_scroller = true;
|
||||||
|
} else {
|
||||||
|
need_apply_overspread = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool need_apply_center =
|
||||||
|
config.scroller_focus_center || n_heads == 1 ||
|
||||||
|
(config.scroller_prefer_center && !need_apply_overspread &&
|
||||||
|
(!m->prevsel ||
|
||||||
|
(ISSCROLLTILED(m->prevsel) &&
|
||||||
|
(m->prevsel->scroller_proportion * max_client_width) +
|
||||||
|
(heads[focus_index]->scroller_proportion *
|
||||||
|
max_client_width) >
|
||||||
|
m->w.width - 2 * config.scroller_structs - cur_gappih)));
|
||||||
|
|
||||||
|
if (n_heads == 1 && config.scroller_ignore_proportion_single) {
|
||||||
|
need_scroller = true;
|
||||||
|
}
|
||||||
|
if (start_drag_window)
|
||||||
|
need_scroller = false;
|
||||||
|
|
||||||
|
struct wlr_box target_geom;
|
||||||
|
target_geom.height = m->w.height - 2 * cur_gappov;
|
||||||
|
target_geom.width =
|
||||||
|
max_client_width * heads[focus_index]->scroller_proportion;
|
||||||
|
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
||||||
|
horizontal_scroll_adjust_fullandmax(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
|
||||||
|
if (heads[focus_index]->client->isfullscreen) {
|
||||||
|
target_geom.x = m->m.x;
|
||||||
|
horizontal_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_node(heads[focus_index], target_geom, cur_gappiv);
|
||||||
|
} else if (heads[focus_index]->client->ismaximizescreen) {
|
||||||
|
target_geom.x = m->w.x + cur_gappoh;
|
||||||
|
horizontal_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_node(heads[focus_index], target_geom, cur_gappiv);
|
||||||
|
} else if (need_scroller) {
|
||||||
|
if (need_apply_center) {
|
||||||
|
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
||||||
|
} else if (need_apply_overspread) {
|
||||||
|
if (over_overspread_to_left) {
|
||||||
|
target_geom.x = m->w.x + config.scroller_structs;
|
||||||
|
} else {
|
||||||
|
target_geom.x =
|
||||||
|
m->w.x + (m->w.width -
|
||||||
|
heads[focus_index]->scroller_proportion *
|
||||||
|
max_client_width -
|
||||||
|
config.scroller_structs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target_geom.x =
|
||||||
|
root_client->geom.x > m->w.x + (m->w.width) / 2
|
||||||
|
? m->w.x + (m->w.width -
|
||||||
|
heads[focus_index]->scroller_proportion *
|
||||||
|
max_client_width -
|
||||||
|
config.scroller_structs)
|
||||||
|
: m->w.x + config.scroller_structs;
|
||||||
|
}
|
||||||
|
horizontal_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_node(heads[focus_index], target_geom, cur_gappiv);
|
||||||
|
} else {
|
||||||
|
target_geom.x = root_client->geom.x;
|
||||||
|
horizontal_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_node(heads[focus_index], target_geom, cur_gappiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 排列左侧的堆叠 */
|
||||||
|
for (int i = 1; i <= focus_index; i++) {
|
||||||
|
struct ScrollerStackNode *cur = heads[focus_index - i];
|
||||||
|
struct wlr_box left_geom;
|
||||||
|
left_geom.height = m->w.height - 2 * cur_gappov;
|
||||||
|
left_geom.width = max_client_width * cur->scroller_proportion;
|
||||||
|
horizontal_scroll_adjust_fullandmax(cur->client, &left_geom);
|
||||||
|
left_geom.x = heads[focus_index - i + 1]->client->geom.x - cur_gappih -
|
||||||
|
left_geom.width;
|
||||||
|
arrange_stack_node(cur, left_geom, cur_gappiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 排列右侧的堆叠 */
|
||||||
|
for (int i = 1; i < n_heads - focus_index; i++) {
|
||||||
|
struct ScrollerStackNode *cur = heads[focus_index + i];
|
||||||
|
struct wlr_box right_geom;
|
||||||
|
right_geom.height = m->w.height - 2 * cur_gappov;
|
||||||
|
right_geom.width = max_client_width * cur->scroller_proportion;
|
||||||
|
horizontal_scroll_adjust_fullandmax(cur->client, &right_geom);
|
||||||
|
right_geom.x = heads[focus_index + i - 1]->client->geom.x + cur_gappih +
|
||||||
|
heads[focus_index + i - 1]->client->geom.width;
|
||||||
|
arrange_stack_node(cur, right_geom, cur_gappiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertical_scroller(Monitor *m) {
|
||||||
|
uint32_t tag = m->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = ensure_scroller_state(m, tag);
|
||||||
|
|
||||||
|
Client *vis[512];
|
||||||
|
int32_t count = 0;
|
||||||
|
Client *c;
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (VISIBLEON(c, m) && ISSCROLLTILED(c))
|
||||||
|
vis[count++] = c;
|
||||||
|
if (count == 512)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScrollerStackNode *n = st->all_first;
|
||||||
|
while (n) {
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (vis[i] == n->client) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct ScrollerStackNode *next = n->all_next;
|
||||||
|
if (!found)
|
||||||
|
scroller_node_remove(st, n);
|
||||||
|
n = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (!find_scroller_node(st, vis[i])) {
|
||||||
|
struct ScrollerStackNode *new_node =
|
||||||
|
scroller_node_create(st, vis[i]);
|
||||||
|
Client *prev = vis[i]->prev_in_stack;
|
||||||
|
if (prev) {
|
||||||
|
struct ScrollerStackNode *prev_node =
|
||||||
|
find_scroller_node(st, prev);
|
||||||
|
if (prev_node) {
|
||||||
|
new_node->prev_in_stack = prev_node;
|
||||||
|
prev_node->next_in_stack = new_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按全局顺序收集堆叠头 */
|
||||||
|
struct ScrollerStackNode *heads[64];
|
||||||
|
int32_t n_heads = 0;
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (VISIBLEON(c, m) && ISSCROLLTILED(c)) {
|
||||||
|
struct ScrollerStackNode *node = find_scroller_node(st, c);
|
||||||
|
if (node && !node->prev_in_stack) {
|
||||||
|
bool already = false;
|
||||||
|
for (int k = 0; k < n_heads; k++)
|
||||||
|
if (heads[k] == node)
|
||||||
|
already = true;
|
||||||
|
if (!already)
|
||||||
|
heads[n_heads++] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_heads == 0) {
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m->visible_scroll_tiling_clients = n_heads;
|
||||||
|
|
||||||
|
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||||
|
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||||
|
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||||
|
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
||||||
|
if (config.smartgaps && n_heads == 1) {
|
||||||
|
cur_gappiv = cur_gappov = cur_gappoh = 0;
|
||||||
|
}
|
||||||
|
int32_t max_client_height =
|
||||||
|
m->w.height - 2 * config.scroller_structs - cur_gappiv;
|
||||||
|
|
||||||
|
if (n_heads == 1 && !config.scroller_ignore_proportion_single &&
|
||||||
|
!heads[0]->client->isfullscreen &&
|
||||||
|
!heads[0]->client->ismaximizescreen) {
|
||||||
|
struct ScrollerStackNode *head = heads[0];
|
||||||
|
float single_proportion =
|
||||||
|
head->scroller_proportion_single > 0.0f
|
||||||
|
? head->scroller_proportion_single
|
||||||
|
: config.scroller_default_proportion_single;
|
||||||
|
struct wlr_box target_geom;
|
||||||
|
target_geom.width = m->w.width - 2 * cur_gappoh;
|
||||||
|
target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion;
|
||||||
|
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
||||||
|
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
||||||
|
vertical_check_scroller_root_inside_mon(head->client, &target_geom);
|
||||||
|
arrange_stack_vertical_node(head, target_geom, cur_gappih);
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScrollerStackNode *root_node = NULL;
|
||||||
|
if (m->sel && ISSCROLLTILED(m->sel)) {
|
||||||
|
root_node = find_scroller_node(st, m->sel);
|
||||||
|
if (root_node) {
|
||||||
|
while (root_node->prev_in_stack)
|
||||||
|
root_node = root_node->prev_in_stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!root_node && m->prevsel && ISSCROLLTILED(m->prevsel)) {
|
||||||
|
root_node = find_scroller_node(st, m->prevsel);
|
||||||
|
if (root_node) {
|
||||||
|
while (root_node->prev_in_stack)
|
||||||
|
root_node = root_node->prev_in_stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!root_node)
|
||||||
|
root_node = heads[n_heads / 2];
|
||||||
|
|
||||||
|
int32_t focus_index = -1;
|
||||||
|
for (int i = 0; i < n_heads; i++) {
|
||||||
|
if (heads[i] == root_node) {
|
||||||
|
focus_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (focus_index < 0)
|
||||||
|
focus_index = n_heads / 2;
|
||||||
|
|
||||||
|
bool need_scroller = false;
|
||||||
|
bool over_overspread_to_up = false;
|
||||||
|
Client *root_client = root_node->client;
|
||||||
|
|
||||||
|
if (root_client->geom.y >= m->w.y + config.scroller_structs &&
|
||||||
|
root_client->geom.y + root_client->geom.height <=
|
||||||
|
m->w.y + m->w.height - config.scroller_structs) {
|
||||||
|
need_scroller = false;
|
||||||
|
} else {
|
||||||
|
need_scroller = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool need_apply_overspread =
|
||||||
|
config.scroller_prefer_overspread && n_heads > 1 &&
|
||||||
|
(focus_index == 0 || focus_index == n_heads - 1) &&
|
||||||
|
heads[focus_index]->scroller_proportion < 1.0f;
|
||||||
|
|
||||||
|
if (need_apply_overspread) {
|
||||||
|
if (focus_index == 0) {
|
||||||
|
over_overspread_to_up = true;
|
||||||
|
} else {
|
||||||
|
over_overspread_to_up = false;
|
||||||
|
}
|
||||||
|
if (over_overspread_to_up &&
|
||||||
|
(!INSIDEMON(heads[1]->client) ||
|
||||||
|
(heads[1]->scroller_proportion + heads[0]->scroller_proportion >=
|
||||||
|
1.0f))) {
|
||||||
|
need_scroller = true;
|
||||||
|
} else if (!over_overspread_to_up &&
|
||||||
|
(!INSIDEMON(heads[focus_index - 1]->client) ||
|
||||||
|
(heads[focus_index - 1]->scroller_proportion +
|
||||||
|
heads[focus_index]->scroller_proportion >=
|
||||||
|
1.0f))) {
|
||||||
|
need_scroller = true;
|
||||||
|
} else {
|
||||||
|
need_apply_overspread = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool need_apply_center =
|
||||||
|
config.scroller_focus_center || n_heads == 1 ||
|
||||||
|
(config.scroller_prefer_center && !need_apply_overspread &&
|
||||||
|
(!m->prevsel ||
|
||||||
|
(ISSCROLLTILED(m->prevsel) &&
|
||||||
|
(m->prevsel->scroller_proportion * max_client_height) +
|
||||||
|
(heads[focus_index]->scroller_proportion *
|
||||||
|
max_client_height) >
|
||||||
|
m->w.height - 2 * config.scroller_structs - cur_gappiv)));
|
||||||
|
|
||||||
|
if (n_heads == 1 && config.scroller_ignore_proportion_single) {
|
||||||
|
need_scroller = true;
|
||||||
|
}
|
||||||
|
if (start_drag_window)
|
||||||
|
need_scroller = false;
|
||||||
|
|
||||||
|
struct wlr_box target_geom;
|
||||||
|
target_geom.width = m->w.width - 2 * cur_gappoh;
|
||||||
|
target_geom.height =
|
||||||
|
max_client_height * heads[focus_index]->scroller_proportion;
|
||||||
|
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
||||||
|
vertical_scroll_adjust_fullandmax(heads[focus_index]->client, &target_geom);
|
||||||
|
|
||||||
|
if (heads[focus_index]->client->isfullscreen) {
|
||||||
|
target_geom.y = m->m.y;
|
||||||
|
vertical_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_vertical_node(heads[focus_index], target_geom,
|
||||||
|
cur_gappih);
|
||||||
|
} else if (heads[focus_index]->client->ismaximizescreen) {
|
||||||
|
target_geom.y = m->w.y + cur_gappov;
|
||||||
|
vertical_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_vertical_node(heads[focus_index], target_geom,
|
||||||
|
cur_gappih);
|
||||||
|
} else if (need_scroller) {
|
||||||
|
if (need_apply_center) {
|
||||||
|
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
||||||
|
} else if (need_apply_overspread) {
|
||||||
|
if (over_overspread_to_up) {
|
||||||
|
target_geom.y = m->w.y + config.scroller_structs;
|
||||||
|
} else {
|
||||||
|
target_geom.y =
|
||||||
|
m->w.y + (m->w.height -
|
||||||
|
heads[focus_index]->scroller_proportion *
|
||||||
|
max_client_height -
|
||||||
|
config.scroller_structs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target_geom.y =
|
||||||
|
root_client->geom.y > m->w.y + (m->w.height) / 2
|
||||||
|
? m->w.y + (m->w.height -
|
||||||
|
heads[focus_index]->scroller_proportion *
|
||||||
|
max_client_height -
|
||||||
|
config.scroller_structs)
|
||||||
|
: m->w.y + config.scroller_structs;
|
||||||
|
}
|
||||||
|
vertical_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_vertical_node(heads[focus_index], target_geom,
|
||||||
|
cur_gappih);
|
||||||
|
} else {
|
||||||
|
target_geom.y = root_client->geom.y;
|
||||||
|
vertical_check_scroller_root_inside_mon(heads[focus_index]->client,
|
||||||
|
&target_geom);
|
||||||
|
arrange_stack_vertical_node(heads[focus_index], target_geom,
|
||||||
|
cur_gappih);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i <= focus_index; i++) {
|
||||||
|
struct ScrollerStackNode *cur = heads[focus_index - i];
|
||||||
|
struct wlr_box up_geom;
|
||||||
|
up_geom.width = m->w.width - 2 * cur_gappoh;
|
||||||
|
up_geom.height = max_client_height * cur->scroller_proportion;
|
||||||
|
vertical_scroll_adjust_fullandmax(cur->client, &up_geom);
|
||||||
|
up_geom.y = heads[focus_index - i + 1]->client->geom.y - cur_gappiv -
|
||||||
|
up_geom.height;
|
||||||
|
arrange_stack_vertical_node(cur, up_geom, cur_gappih);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < n_heads - focus_index; i++) {
|
||||||
|
struct ScrollerStackNode *cur = heads[focus_index + i];
|
||||||
|
struct wlr_box down_geom;
|
||||||
|
down_geom.width = m->w.width - 2 * cur_gappoh;
|
||||||
|
down_geom.height = max_client_height * cur->scroller_proportion;
|
||||||
|
vertical_scroll_adjust_fullandmax(cur->client, &down_geom);
|
||||||
|
down_geom.y = heads[focus_index + i - 1]->client->geom.y + cur_gappiv +
|
||||||
|
heads[focus_index + i - 1]->client->geom.height;
|
||||||
|
arrange_stack_vertical_node(cur, down_geom, cur_gappih);
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroller_remove_client(Client *c) {
|
||||||
|
Monitor *m;
|
||||||
|
wl_list_for_each(m, &mons, link) {
|
||||||
|
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) {
|
||||||
|
struct TagScrollerState *st = m->pertag->scroller_state[t];
|
||||||
|
if (!st)
|
||||||
|
continue;
|
||||||
|
struct ScrollerStackNode *node = find_scroller_node(st, c);
|
||||||
|
if (node) {
|
||||||
|
scroller_node_remove(st, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -176,324 +176,6 @@ void vertical_deck(Monitor *m) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) {
|
|
||||||
Monitor *m = c->mon;
|
|
||||||
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
|
||||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
|
||||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
|
||||||
|
|
||||||
cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappiv;
|
|
||||||
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappov;
|
|
||||||
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappoh;
|
|
||||||
|
|
||||||
if (c->isfullscreen) {
|
|
||||||
target_geom->width = m->m.width;
|
|
||||||
target_geom->height = m->m.height;
|
|
||||||
target_geom->x = m->m.x;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->ismaximizescreen) {
|
|
||||||
target_geom->width = m->w.width - 2 * cur_gappoh;
|
|
||||||
target_geom->height = m->w.height - 2 * cur_gappov;
|
|
||||||
target_geom->x = m->w.x + cur_gappoh;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
target_geom->width = m->w.width - 2 * cur_gappoh;
|
|
||||||
target_geom->x = m->w.x + (m->w.width - target_geom->width) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arrange_stack_vertical(Client *scroller_stack_head,
|
|
||||||
struct wlr_box geometry, int32_t gappih) {
|
|
||||||
int32_t stack_size = 0;
|
|
||||||
Client *iter = scroller_stack_head;
|
|
||||||
|
|
||||||
while (iter) {
|
|
||||||
stack_size++;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stack_size == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
float total_proportion = 0.0f;
|
|
||||||
iter = scroller_stack_head;
|
|
||||||
while (iter) {
|
|
||||||
if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) {
|
|
||||||
iter->stack_proportion =
|
|
||||||
stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1);
|
|
||||||
}
|
|
||||||
total_proportion += iter->stack_proportion;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
iter = scroller_stack_head;
|
|
||||||
while (iter) {
|
|
||||||
iter->stack_proportion = iter->stack_proportion / total_proportion;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t client_width;
|
|
||||||
int32_t current_x = geometry.x;
|
|
||||||
int32_t remain_client_width = geometry.width - (stack_size - 1) * gappih;
|
|
||||||
float remain_proportion = 1.0f;
|
|
||||||
|
|
||||||
iter = scroller_stack_head;
|
|
||||||
while (iter) {
|
|
||||||
|
|
||||||
client_width =
|
|
||||||
remain_client_width * (iter->stack_proportion / remain_proportion);
|
|
||||||
|
|
||||||
struct wlr_box client_geom = {.y = geometry.y,
|
|
||||||
.x = current_x,
|
|
||||||
.height = geometry.height,
|
|
||||||
.width = client_width};
|
|
||||||
resize(iter, client_geom, 0);
|
|
||||||
remain_proportion -= iter->stack_proportion;
|
|
||||||
remain_client_width -= client_width;
|
|
||||||
current_x += client_width + gappih;
|
|
||||||
iter = iter->next_in_stack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void vertical_check_scroller_root_inside_mon(Client *c,
|
|
||||||
struct wlr_box *geometry) {
|
|
||||||
if (!GEOMINSIDEMON(geometry, c->mon)) {
|
|
||||||
geometry->y = c->mon->w.y + (c->mon->w.height - geometry->height) / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 竖屏滚动布局
|
|
||||||
void vertical_scroller(Monitor *m) {
|
|
||||||
int32_t i, n, j;
|
|
||||||
float single_proportion = 1.0;
|
|
||||||
|
|
||||||
Client *c = NULL, *root_client = NULL;
|
|
||||||
Client **tempClients = NULL;
|
|
||||||
struct wlr_box target_geom;
|
|
||||||
int32_t focus_client_index = 0;
|
|
||||||
bool need_scroller = false;
|
|
||||||
bool over_overspread_to_up = false;
|
|
||||||
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
|
||||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
|
||||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
|
||||||
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
|
||||||
|
|
||||||
cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappiv;
|
|
||||||
cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappov;
|
|
||||||
cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1
|
|
||||||
? 0
|
|
||||||
: cur_gappoh;
|
|
||||||
|
|
||||||
int32_t max_client_height =
|
|
||||||
m->w.height - 2 * config.scroller_structs - cur_gappiv;
|
|
||||||
|
|
||||||
n = m->visible_scroll_tiling_clients;
|
|
||||||
|
|
||||||
if (n == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tempClients = malloc(n * sizeof(Client *));
|
|
||||||
if (!tempClients) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
wl_list_for_each(c, &clients, link) {
|
|
||||||
if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
|
|
||||||
tempClients[j] = c;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == 1 && !config.scroller_ignore_proportion_single &&
|
|
||||||
!tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) {
|
|
||||||
c = tempClients[0];
|
|
||||||
|
|
||||||
single_proportion = c->scroller_proportion_single > 0.0f
|
|
||||||
? c->scroller_proportion_single
|
|
||||||
: config.scroller_default_proportion_single;
|
|
||||||
|
|
||||||
target_geom.width = m->w.width - 2 * cur_gappoh;
|
|
||||||
target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion;
|
|
||||||
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
|
||||||
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
|
||||||
vertical_check_scroller_root_inside_mon(c, &target_geom);
|
|
||||||
arrange_stack_vertical(c, target_geom, cur_gappih);
|
|
||||||
free(tempClients);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) {
|
|
||||||
root_client = m->sel;
|
|
||||||
} else if (m->prevsel && ISSCROLLTILED(m->prevsel) &&
|
|
||||||
VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) {
|
|
||||||
root_client = m->prevsel;
|
|
||||||
} else {
|
|
||||||
root_client = center_tiled_select(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
// root_client might be in a stack, find the stack head
|
|
||||||
if (root_client) {
|
|
||||||
root_client = get_scroll_stack_head(root_client);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root_client) {
|
|
||||||
free(tempClients);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
c = tempClients[i];
|
|
||||||
if (root_client == c) {
|
|
||||||
if (c->geom.y >= m->w.y + config.scroller_structs &&
|
|
||||||
c->geom.y + c->geom.height <=
|
|
||||||
m->w.y + m->w.height - config.scroller_structs) {
|
|
||||||
need_scroller = false;
|
|
||||||
} else {
|
|
||||||
need_scroller = true;
|
|
||||||
}
|
|
||||||
focus_client_index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool need_apply_overspread =
|
|
||||||
config.scroller_prefer_overspread &&
|
|
||||||
m->visible_scroll_tiling_clients > 1 &&
|
|
||||||
(focus_client_index == 0 || focus_client_index == n - 1) &&
|
|
||||||
tempClients[focus_client_index]->scroller_proportion < 1.0f;
|
|
||||||
|
|
||||||
if (need_apply_overspread) {
|
|
||||||
|
|
||||||
if (focus_client_index == 0) {
|
|
||||||
over_overspread_to_up = true;
|
|
||||||
} else {
|
|
||||||
over_overspread_to_up = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (over_overspread_to_up &&
|
|
||||||
(!INSIDEMON(tempClients[1]) ||
|
|
||||||
(tempClients[1]->scroller_proportion +
|
|
||||||
tempClients[focus_client_index]->scroller_proportion >=
|
|
||||||
1.0f))) {
|
|
||||||
need_scroller = true;
|
|
||||||
} else if (!over_overspread_to_up &&
|
|
||||||
(!INSIDEMON(tempClients[focus_client_index - 1]) ||
|
|
||||||
(tempClients[focus_client_index - 1]->scroller_proportion +
|
|
||||||
tempClients[focus_client_index]->scroller_proportion >=
|
|
||||||
1.0f))) {
|
|
||||||
need_scroller = true;
|
|
||||||
} else {
|
|
||||||
need_apply_overspread = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool need_apply_center =
|
|
||||||
config.scroller_focus_center || m->visible_scroll_tiling_clients == 1 ||
|
|
||||||
(config.scroller_prefer_center && !need_apply_overspread &&
|
|
||||||
(!m->prevsel ||
|
|
||||||
(ISSCROLLTILED(m->prevsel) &&
|
|
||||||
(m->prevsel->scroller_proportion * max_client_height) +
|
|
||||||
(tempClients[focus_client_index]->scroller_proportion *
|
|
||||||
max_client_height) >
|
|
||||||
m->w.height - 2 * config.scroller_structs - cur_gappiv)));
|
|
||||||
|
|
||||||
if (n == 1 && config.scroller_ignore_proportion_single) {
|
|
||||||
need_scroller = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start_drag_window)
|
|
||||||
need_scroller = false;
|
|
||||||
|
|
||||||
target_geom.width = m->w.width - 2 * cur_gappoh;
|
|
||||||
target_geom.height = max_client_height * c->scroller_proportion;
|
|
||||||
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
|
|
||||||
vertical_scroll_adjust_fullandmax(tempClients[focus_client_index],
|
|
||||||
&target_geom);
|
|
||||||
|
|
||||||
if (tempClients[focus_client_index]->isfullscreen) {
|
|
||||||
target_geom.y = m->m.y;
|
|
||||||
vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
|
|
||||||
&target_geom);
|
|
||||||
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
|
|
||||||
cur_gappih);
|
|
||||||
} else if (tempClients[focus_client_index]->ismaximizescreen) {
|
|
||||||
target_geom.y = m->w.y + cur_gappov;
|
|
||||||
vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
|
|
||||||
&target_geom);
|
|
||||||
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
|
|
||||||
cur_gappih);
|
|
||||||
} else if (need_scroller) {
|
|
||||||
if (need_apply_center) {
|
|
||||||
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
|
|
||||||
} else if (need_apply_overspread) {
|
|
||||||
if (over_overspread_to_up) {
|
|
||||||
target_geom.y = m->w.y + config.scroller_structs;
|
|
||||||
} else {
|
|
||||||
target_geom.y =
|
|
||||||
m->w.y +
|
|
||||||
(m->w.height -
|
|
||||||
tempClients[focus_client_index]->scroller_proportion *
|
|
||||||
max_client_height -
|
|
||||||
config.scroller_structs);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2
|
|
||||||
? m->w.y + (m->w.height -
|
|
||||||
tempClients[focus_client_index]
|
|
||||||
->scroller_proportion *
|
|
||||||
max_client_height -
|
|
||||||
config.scroller_structs)
|
|
||||||
: m->w.y + config.scroller_structs;
|
|
||||||
}
|
|
||||||
vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
|
|
||||||
&target_geom);
|
|
||||||
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
|
|
||||||
cur_gappih);
|
|
||||||
} else {
|
|
||||||
target_geom.y = c->geom.y;
|
|
||||||
vertical_check_scroller_root_inside_mon(tempClients[focus_client_index],
|
|
||||||
&target_geom);
|
|
||||||
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
|
|
||||||
cur_gappih);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i <= focus_client_index; i++) {
|
|
||||||
c = tempClients[focus_client_index - i];
|
|
||||||
target_geom.height = max_client_height * c->scroller_proportion;
|
|
||||||
vertical_scroll_adjust_fullandmax(c, &target_geom);
|
|
||||||
target_geom.y = tempClients[focus_client_index - i + 1]->geom.y -
|
|
||||||
cur_gappiv - target_geom.height;
|
|
||||||
|
|
||||||
arrange_stack_vertical(c, target_geom, cur_gappih);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i < n - focus_client_index; i++) {
|
|
||||||
c = tempClients[focus_client_index + i];
|
|
||||||
target_geom.height = max_client_height * c->scroller_proportion;
|
|
||||||
vertical_scroll_adjust_fullandmax(c, &target_geom);
|
|
||||||
target_geom.y = tempClients[focus_client_index + i - 1]->geom.y +
|
|
||||||
cur_gappiv +
|
|
||||||
tempClients[focus_client_index + i - 1]->geom.height;
|
|
||||||
arrange_stack_vertical(c, target_geom, cur_gappih);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(tempClients);
|
|
||||||
}
|
|
||||||
|
|
||||||
void vertical_grid(Monitor *m) {
|
void vertical_grid(Monitor *m) {
|
||||||
int32_t i, n;
|
int32_t i, n;
|
||||||
int32_t cx, cy, cw, ch;
|
int32_t cx, cy, cw, ch;
|
||||||
|
|
|
||||||
424
src/mango.c
424
src/mango.c
|
|
@ -561,6 +561,21 @@ typedef struct {
|
||||||
} SessionLock;
|
} SessionLock;
|
||||||
|
|
||||||
typedef struct DwindleNode DwindleNode;
|
typedef struct DwindleNode DwindleNode;
|
||||||
|
struct ScrollerStackNode {
|
||||||
|
Client *client;
|
||||||
|
float scroller_proportion;
|
||||||
|
float stack_proportion;
|
||||||
|
float scroller_proportion_single;
|
||||||
|
|
||||||
|
struct ScrollerStackNode *next_in_stack;
|
||||||
|
struct ScrollerStackNode *prev_in_stack;
|
||||||
|
struct ScrollerStackNode *all_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TagScrollerState {
|
||||||
|
struct ScrollerStackNode *all_first; /* 所有节点的单链表头 */
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
/* function declarations */
|
/* function declarations */
|
||||||
static void applybounds(
|
static void applybounds(
|
||||||
|
|
@ -823,6 +838,15 @@ static void dwindle_resize_client_step(Monitor *m, Client *c, int32_t dx,
|
||||||
int32_t dy);
|
int32_t dy);
|
||||||
static void dwindle_resize_client(Monitor *m, Client *c);
|
static void dwindle_resize_client(Monitor *m, Client *c);
|
||||||
|
|
||||||
|
static struct TagScrollerState *ensure_scroller_state(Monitor *m, uint32_t tag);
|
||||||
|
static struct ScrollerStackNode *find_scroller_node(struct TagScrollerState *st,
|
||||||
|
Client *c);
|
||||||
|
static void sync_scroller_state_to_clients(Monitor *m, uint32_t tag);
|
||||||
|
static void scroller_node_remove(struct TagScrollerState *st,
|
||||||
|
struct ScrollerStackNode *target);
|
||||||
|
static struct ScrollerStackNode *
|
||||||
|
scroller_node_create(struct TagScrollerState *st, Client *c);
|
||||||
|
|
||||||
#include "data/static_keymap.h"
|
#include "data/static_keymap.h"
|
||||||
#include "dispatch/bind_declare.h"
|
#include "dispatch/bind_declare.h"
|
||||||
#include "layout/layout.h"
|
#include "layout/layout.h"
|
||||||
|
|
@ -945,19 +969,17 @@ static struct {
|
||||||
|
|
||||||
#include "client/client.h"
|
#include "client/client.h"
|
||||||
#include "config/preset.h"
|
#include "config/preset.h"
|
||||||
|
|
||||||
struct Pertag {
|
struct Pertag {
|
||||||
uint32_t curtag, prevtag; /* current and previous tag */
|
uint32_t curtag, prevtag;
|
||||||
int32_t nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
|
int32_t nmasters[LENGTH(tags) + 1];
|
||||||
float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
|
float mfacts[LENGTH(tags) + 1];
|
||||||
int32_t no_hide[LENGTH(tags) + 1]; /* no_hide per tag */
|
int32_t no_hide[LENGTH(tags) + 1];
|
||||||
int32_t no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */
|
int32_t no_render_border[LENGTH(tags) + 1];
|
||||||
int32_t open_as_floating[LENGTH(tags) + 1]; /* open_as_floating per tag */
|
int32_t open_as_floating[LENGTH(tags) + 1];
|
||||||
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
|
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
|
||||||
const Layout
|
const Layout *ltidxs[LENGTH(tags) + 1];
|
||||||
*ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */
|
struct TagScrollerState *scroller_state[LENGTH(tags) + 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "config/parse_config.h"
|
#include "config/parse_config.h"
|
||||||
|
|
||||||
static struct wl_signal mango_print_status;
|
static struct wl_signal mango_print_status;
|
||||||
|
|
@ -1028,6 +1050,7 @@ static struct wl_event_source *sync_keymap;
|
||||||
#include "layout/arrange.h"
|
#include "layout/arrange.h"
|
||||||
#include "layout/dwindle.h"
|
#include "layout/dwindle.h"
|
||||||
#include "layout/horizontal.h"
|
#include "layout/horizontal.h"
|
||||||
|
#include "layout/scroll.h"
|
||||||
#include "layout/vertical.h"
|
#include "layout/vertical.h"
|
||||||
|
|
||||||
void client_change_mon(Client *c, Monitor *m) {
|
void client_change_mon(Client *c, Monitor *m) {
|
||||||
|
|
@ -1160,11 +1183,14 @@ void swallow(Client *c, Client *w) {
|
||||||
c->prev_in_stack = w->prev_in_stack;
|
c->prev_in_stack = w->prev_in_stack;
|
||||||
c->isglobal = w->isglobal;
|
c->isglobal = w->isglobal;
|
||||||
|
|
||||||
|
/* 调整 w 的邻居指针,让它们指向 c */
|
||||||
if (w->next_in_stack)
|
if (w->next_in_stack)
|
||||||
w->next_in_stack->prev_in_stack = c;
|
w->next_in_stack->prev_in_stack = c;
|
||||||
if (w->prev_in_stack)
|
if (w->prev_in_stack)
|
||||||
w->prev_in_stack->next_in_stack = c;
|
w->prev_in_stack->next_in_stack = c;
|
||||||
c->stack_proportion = w->stack_proportion;
|
c->stack_proportion = w->stack_proportion;
|
||||||
|
|
||||||
|
/* 全局链表替换 */
|
||||||
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);
|
||||||
|
|
||||||
|
|
@ -1190,16 +1216,39 @@ void swallow(Client *c, Client *w) {
|
||||||
client_pending_maximized_state(c, w->ismaximizescreen);
|
client_pending_maximized_state(c, w->ismaximizescreen);
|
||||||
client_pending_minimized_state(c, w->isminimized);
|
client_pending_minimized_state(c, w->isminimized);
|
||||||
|
|
||||||
|
/* ---------- 跨 tag 同步:dwindle 与 scroller ---------- */
|
||||||
Monitor *m;
|
Monitor *m;
|
||||||
wl_list_for_each(m, &mons, link) {
|
wl_list_for_each(m, &mons, link) {
|
||||||
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) {
|
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) {
|
||||||
|
/* dwindle */
|
||||||
DwindleNode **root = &m->pertag->dwindle_root[t];
|
DwindleNode **root = &m->pertag->dwindle_root[t];
|
||||||
dwindle_remove(root, c);
|
dwindle_remove(root, c);
|
||||||
DwindleNode *wn = dwindle_find_leaf(*root, w);
|
DwindleNode *dnode = dwindle_find_leaf(*root, w);
|
||||||
|
if (dnode)
|
||||||
|
dnode->client = c;
|
||||||
|
|
||||||
|
/* scroller */
|
||||||
|
struct TagScrollerState *st = m->pertag->scroller_state[t];
|
||||||
|
if (!st)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* 先移除 c 在任意 tag 中的旧节点 */
|
||||||
|
struct ScrollerStackNode *cn = find_scroller_node(st, c);
|
||||||
|
if (cn)
|
||||||
|
scroller_node_remove(st, cn);
|
||||||
|
|
||||||
|
/* 将 w 的节点(如果存在)转给 c */
|
||||||
|
struct ScrollerStackNode *wn = find_scroller_node(st, w);
|
||||||
if (wn)
|
if (wn)
|
||||||
wn->client = c;
|
wn->client = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 同步当前活动 tag 的全局客户端字段 */
|
||||||
|
if (c->mon) {
|
||||||
|
uint32_t curtag = c->mon->pertag->curtag;
|
||||||
|
sync_scroller_state_to_clients(c->mon, curtag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool switch_scratchpad_client_state(Client *c) {
|
bool switch_scratchpad_client_state(Client *c) {
|
||||||
|
|
@ -2109,55 +2158,56 @@ Client *find_closest_tiled_client(Client *c) {
|
||||||
|
|
||||||
void scroller_insert_stack(Client *c, Client *target_client,
|
void scroller_insert_stack(Client *c, Client *target_client,
|
||||||
bool insert_before) {
|
bool insert_before) {
|
||||||
Client *stack_head = NULL;
|
if (!target_client || target_client->mon != c->mon)
|
||||||
|
|
||||||
if (!target_client || target_client->mon != c->mon) {
|
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
c->isglobal = target_client->isglobal = 0;
|
|
||||||
c->isunglobal = target_client->isunglobal = 0;
|
|
||||||
c->tags = target_client->tags = get_tags_first_tag(target_client->tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->isfullscreen) {
|
if (c->isfullscreen)
|
||||||
setfullscreen(c, 0);
|
setfullscreen(c, 0);
|
||||||
}
|
if (c->ismaximizescreen)
|
||||||
|
|
||||||
if (c->ismaximizescreen) {
|
|
||||||
setmaximizescreen(c, 0);
|
setmaximizescreen(c, 0);
|
||||||
}
|
|
||||||
|
|
||||||
exit_scroller_stack(c);
|
Monitor *m = c->mon;
|
||||||
stack_head = get_scroll_stack_head(target_client);
|
uint32_t tag = m->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = ensure_scroller_state(m, tag);
|
||||||
|
|
||||||
|
struct ScrollerStackNode *cnode = find_scroller_node(st, c);
|
||||||
|
if (cnode)
|
||||||
|
scroller_node_remove(st, cnode);
|
||||||
|
|
||||||
|
struct ScrollerStackNode *tnode = find_scroller_node(st, target_client);
|
||||||
|
if (!tnode)
|
||||||
|
tnode = scroller_node_create(st, target_client);
|
||||||
|
|
||||||
|
struct ScrollerStackNode *newnode = scroller_node_create(st, c);
|
||||||
|
/* 将新节点插入到 tnode 的前面或后面 */
|
||||||
if (insert_before) {
|
if (insert_before) {
|
||||||
if (target_client->prev_in_stack) {
|
newnode->next_in_stack = tnode;
|
||||||
target_client->prev_in_stack->next_in_stack = c;
|
newnode->prev_in_stack = tnode->prev_in_stack;
|
||||||
}
|
if (tnode->prev_in_stack)
|
||||||
c->prev_in_stack = target_client->prev_in_stack;
|
tnode->prev_in_stack->next_in_stack = newnode;
|
||||||
c->next_in_stack = target_client;
|
tnode->prev_in_stack = newnode;
|
||||||
target_client->prev_in_stack = c;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (target_client->next_in_stack) {
|
newnode->prev_in_stack = tnode;
|
||||||
target_client->next_in_stack->prev_in_stack = c;
|
newnode->next_in_stack = tnode->next_in_stack;
|
||||||
}
|
if (tnode->next_in_stack)
|
||||||
c->next_in_stack = target_client->next_in_stack;
|
tnode->next_in_stack->prev_in_stack = newnode;
|
||||||
c->prev_in_stack = target_client;
|
tnode->next_in_stack = newnode;
|
||||||
target_client->next_in_stack = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stack_head->ismaximizescreen) {
|
/* 处理堆叠头部的全屏/最大化状态*/
|
||||||
|
struct ScrollerStackNode *head = tnode;
|
||||||
|
while (head->prev_in_stack)
|
||||||
|
head = head->prev_in_stack;
|
||||||
|
Client *stack_head = head->client;
|
||||||
|
if (stack_head->ismaximizescreen)
|
||||||
setmaximizescreen(stack_head, 0);
|
setmaximizescreen(stack_head, 0);
|
||||||
}
|
if (stack_head->isfullscreen)
|
||||||
|
|
||||||
if (stack_head->isfullscreen) {
|
|
||||||
setfullscreen(stack_head, 0);
|
setfullscreen(stack_head, 0);
|
||||||
}
|
|
||||||
|
|
||||||
arrange(c->mon, false, false);
|
/* 同步到 Client 字段 */
|
||||||
|
sync_scroller_state_to_clients(m, tag);
|
||||||
|
|
||||||
return;
|
arrange(m, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void try_scroller_drop(Client *c, Client *closest, int vertical) {
|
void try_scroller_drop(Client *c, Client *closest, int vertical) {
|
||||||
|
|
@ -2550,6 +2600,9 @@ void cleanupmon(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++)
|
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++)
|
||||||
dwindle_free_tree(m->pertag->dwindle_root[t]);
|
dwindle_free_tree(m->pertag->dwindle_root[t]);
|
||||||
|
|
||||||
|
cleanup_monitor_scroller(m);
|
||||||
|
|
||||||
free(m->pertag);
|
free(m->pertag);
|
||||||
free(m);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
@ -3292,7 +3345,11 @@ void createmon(struct wl_listener *listener, void *data) {
|
||||||
wlr_output_state_finish(&state);
|
wlr_output_state_finish(&state);
|
||||||
|
|
||||||
wl_list_insert(&mons, &m->link);
|
wl_list_insert(&mons, &m->link);
|
||||||
|
|
||||||
m->pertag = calloc(1, sizeof(Pertag));
|
m->pertag = calloc(1, sizeof(Pertag));
|
||||||
|
for (int i = 0; i < LENGTH(tags) + 1; i++)
|
||||||
|
m->pertag->scroller_state[i] = NULL;
|
||||||
|
|
||||||
if (chvt_backup_tag &&
|
if (chvt_backup_tag &&
|
||||||
regex_match(chvt_backup_selmon, m->wlr_output->name)) {
|
regex_match(chvt_backup_selmon, m->wlr_output->name)) {
|
||||||
m->tagset[0] = m->tagset[1] = (1 << (chvt_backup_tag - 1)) & TAGMASK;
|
m->tagset[0] = m->tagset[1] = (1 << (chvt_backup_tag - 1)) & TAGMASK;
|
||||||
|
|
@ -5027,7 +5084,6 @@ void setborder_color(Client *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void exchange_two_client(Client *c1, Client *c2) {
|
void exchange_two_client(Client *c1, Client *c2) {
|
||||||
|
|
||||||
Monitor *tmp_mon = NULL;
|
Monitor *tmp_mon = NULL;
|
||||||
uint32_t tmp_tags;
|
uint32_t tmp_tags;
|
||||||
double master_inner_per = 0.0f;
|
double master_inner_per = 0.0f;
|
||||||
|
|
@ -5041,6 +5097,237 @@ void exchange_two_client(Client *c1, Client *c2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool c1_scroller = c1->mon && is_scroller_layout(c1->mon);
|
||||||
|
bool c2_scroller = c2->mon && is_scroller_layout(c2->mon);
|
||||||
|
|
||||||
|
if (c1_scroller && c2_scroller) {
|
||||||
|
Monitor *m1 = c1->mon;
|
||||||
|
Monitor *m2 = c2->mon;
|
||||||
|
uint32_t tag1 = m1->pertag->curtag;
|
||||||
|
uint32_t tag2 = m2->pertag->curtag;
|
||||||
|
|
||||||
|
/* 跨显示器且任一方有堆叠关系时不允许交换 */
|
||||||
|
if (m1 != m2 && (c1->prev_in_stack || c2->prev_in_stack ||
|
||||||
|
c1->next_in_stack || c2->next_in_stack))
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct TagScrollerState *st1 = ensure_scroller_state(m1, tag1);
|
||||||
|
struct TagScrollerState *st2 =
|
||||||
|
(m1 == m2) ? st1 : ensure_scroller_state(m2, tag2);
|
||||||
|
|
||||||
|
struct ScrollerStackNode *n1 = find_scroller_node(st1, c1);
|
||||||
|
struct ScrollerStackNode *n2 = find_scroller_node(st2, c2);
|
||||||
|
|
||||||
|
if (!n1 || !n2)
|
||||||
|
goto non_scroller_fallback;
|
||||||
|
|
||||||
|
/* 获取各自的堆叠头节点 */
|
||||||
|
struct ScrollerStackNode *head1 = n1;
|
||||||
|
while (head1->prev_in_stack)
|
||||||
|
head1 = head1->prev_in_stack;
|
||||||
|
struct ScrollerStackNode *head2 = n2;
|
||||||
|
while (head2->prev_in_stack)
|
||||||
|
head2 = head2->prev_in_stack;
|
||||||
|
|
||||||
|
/* 同一堆叠内交换 */
|
||||||
|
if (head1 == head2) {
|
||||||
|
float tmp_scroller = n1->scroller_proportion;
|
||||||
|
float tmp_stack = n1->stack_proportion;
|
||||||
|
n1->scroller_proportion = n2->scroller_proportion;
|
||||||
|
n1->stack_proportion = n2->stack_proportion;
|
||||||
|
n2->scroller_proportion = tmp_scroller;
|
||||||
|
n2->stack_proportion = tmp_stack;
|
||||||
|
|
||||||
|
/* 交换堆叠链表指针 */
|
||||||
|
struct ScrollerStackNode *p1 = n1->prev_in_stack;
|
||||||
|
struct ScrollerStackNode *next1 = n1->next_in_stack;
|
||||||
|
struct ScrollerStackNode *p2 = n2->prev_in_stack;
|
||||||
|
struct ScrollerStackNode *next2 = n2->next_in_stack;
|
||||||
|
|
||||||
|
if (n1->next_in_stack == n2) {
|
||||||
|
n1->next_in_stack = next2;
|
||||||
|
n2->prev_in_stack = p1;
|
||||||
|
n1->prev_in_stack = n2;
|
||||||
|
n2->next_in_stack = n1;
|
||||||
|
if (p1)
|
||||||
|
p1->next_in_stack = n2;
|
||||||
|
if (next2)
|
||||||
|
next2->prev_in_stack = n1;
|
||||||
|
} else if (n2->next_in_stack == n1) {
|
||||||
|
n2->next_in_stack = next1;
|
||||||
|
n1->prev_in_stack = p2;
|
||||||
|
n2->prev_in_stack = n1;
|
||||||
|
n1->next_in_stack = n2;
|
||||||
|
if (p2)
|
||||||
|
p2->next_in_stack = n1;
|
||||||
|
if (next1)
|
||||||
|
next1->prev_in_stack = n2;
|
||||||
|
} else {
|
||||||
|
if (p1)
|
||||||
|
p1->next_in_stack = n2;
|
||||||
|
if (next1)
|
||||||
|
next1->prev_in_stack = n2;
|
||||||
|
if (p2)
|
||||||
|
p2->next_in_stack = n1;
|
||||||
|
if (next2)
|
||||||
|
next2->prev_in_stack = n1;
|
||||||
|
n1->prev_in_stack = p2;
|
||||||
|
n1->next_in_stack = next2;
|
||||||
|
n2->prev_in_stack = p1;
|
||||||
|
n2->next_in_stack = next1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_scroller_state_to_clients(m1, tag1);
|
||||||
|
} else {
|
||||||
|
/* 不同堆叠:交换两个堆叠整体位置 */
|
||||||
|
if (n1 != head1 || n2 != head2) {
|
||||||
|
/* 当前不是头部,递归交换头部 */
|
||||||
|
exchange_two_client(head1->client, head2->client);
|
||||||
|
focusclient(c1, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 两者都是头部,直接交换两个堆叠的头部客户端 */
|
||||||
|
float tmp_scroller = head1->scroller_proportion;
|
||||||
|
head1->scroller_proportion = head2->scroller_proportion;
|
||||||
|
head2->scroller_proportion = tmp_scroller;
|
||||||
|
|
||||||
|
/* 交换布局通用参数 */
|
||||||
|
master_inner_per = c1->master_inner_per;
|
||||||
|
master_mfact_per = c1->master_mfact_per;
|
||||||
|
stack_inner_per = c1->stack_inner_per;
|
||||||
|
c1->master_inner_per = c2->master_inner_per;
|
||||||
|
c1->master_mfact_per = c2->master_mfact_per;
|
||||||
|
c1->stack_inner_per = c2->stack_inner_per;
|
||||||
|
c2->master_inner_per = master_inner_per;
|
||||||
|
c2->master_mfact_per = master_mfact_per;
|
||||||
|
c2->stack_inner_per = stack_inner_per;
|
||||||
|
|
||||||
|
/* 交换全局链表位置 */
|
||||||
|
struct wl_list *tmp1_prev = c1->link.prev;
|
||||||
|
struct wl_list *tmp2_prev = c2->link.prev;
|
||||||
|
struct wl_list *tmp1_next = c1->link.next;
|
||||||
|
struct wl_list *tmp2_next = c2->link.next;
|
||||||
|
|
||||||
|
if (c1->link.next == &c2->link) {
|
||||||
|
c1->link.next = c2->link.next;
|
||||||
|
c1->link.prev = &c2->link;
|
||||||
|
c2->link.next = &c1->link;
|
||||||
|
c2->link.prev = tmp1_prev;
|
||||||
|
tmp1_prev->next = &c2->link;
|
||||||
|
tmp2_next->prev = &c1->link;
|
||||||
|
} else if (c2->link.next == &c1->link) {
|
||||||
|
c2->link.next = c1->link.next;
|
||||||
|
c2->link.prev = &c1->link;
|
||||||
|
c1->link.next = &c2->link;
|
||||||
|
c1->link.prev = tmp2_prev;
|
||||||
|
tmp2_prev->next = &c1->link;
|
||||||
|
tmp1_next->prev = &c2->link;
|
||||||
|
} else {
|
||||||
|
c2->link.next = tmp1_next;
|
||||||
|
c2->link.prev = tmp1_prev;
|
||||||
|
c1->link.next = tmp2_next;
|
||||||
|
c1->link.prev = tmp2_prev;
|
||||||
|
tmp1_prev->next = &c2->link;
|
||||||
|
tmp1_next->prev = &c2->link;
|
||||||
|
tmp2_prev->next = &c1->link;
|
||||||
|
tmp2_next->prev = &c1->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 跨显示器交换处理 */
|
||||||
|
if (config.exchange_cross_monitor && m1 != m2) {
|
||||||
|
tmp_mon = m2;
|
||||||
|
tmp_tags = c2->tags;
|
||||||
|
setmon(c2, m1, c1->tags, false);
|
||||||
|
setmon(c1, tmp_mon, tmp_tags, false);
|
||||||
|
|
||||||
|
if (m1->pertag->ltidxs[tag1]->id == DWINDLE)
|
||||||
|
dwindle_swap_clients(&m1->pertag->dwindle_root[tag1], c1,
|
||||||
|
c2);
|
||||||
|
if (m2->pertag->ltidxs[tag2]->id == DWINDLE)
|
||||||
|
dwindle_swap_clients(&m2->pertag->dwindle_root[tag2], c2,
|
||||||
|
c1);
|
||||||
|
|
||||||
|
sync_scroller_state_to_clients(m1, tag1);
|
||||||
|
sync_scroller_state_to_clients(m2, tag2);
|
||||||
|
arrange(m1, false, false);
|
||||||
|
arrange(m2, false, false);
|
||||||
|
} else {
|
||||||
|
if (m1->pertag->ltidxs[tag1]->id == DWINDLE)
|
||||||
|
dwindle_swap_clients(&m1->pertag->dwindle_root[tag1], c1,
|
||||||
|
c2);
|
||||||
|
sync_scroller_state_to_clients(m1, tag1);
|
||||||
|
arrange(m1, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_remove(&c2->flink);
|
||||||
|
wl_list_insert(&c1->flink, &c2->flink);
|
||||||
|
focusclient(c1, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 同一堆叠交换完成后的公共处理(全局链表、跨显示器等) */
|
||||||
|
struct wl_list *tmp1_prev = c1->link.prev;
|
||||||
|
struct wl_list *tmp2_prev = c2->link.prev;
|
||||||
|
struct wl_list *tmp1_next = c1->link.next;
|
||||||
|
struct wl_list *tmp2_next = c2->link.next;
|
||||||
|
|
||||||
|
if (c1->link.next == &c2->link) {
|
||||||
|
c1->link.next = c2->link.next;
|
||||||
|
c1->link.prev = &c2->link;
|
||||||
|
c2->link.next = &c1->link;
|
||||||
|
c2->link.prev = tmp1_prev;
|
||||||
|
tmp1_prev->next = &c2->link;
|
||||||
|
tmp2_next->prev = &c1->link;
|
||||||
|
} else if (c2->link.next == &c1->link) {
|
||||||
|
c2->link.next = c1->link.next;
|
||||||
|
c2->link.prev = &c1->link;
|
||||||
|
c1->link.next = &c2->link;
|
||||||
|
c1->link.prev = tmp2_prev;
|
||||||
|
tmp2_prev->next = &c1->link;
|
||||||
|
tmp1_next->prev = &c2->link;
|
||||||
|
} else {
|
||||||
|
c2->link.next = tmp1_next;
|
||||||
|
c2->link.prev = tmp1_prev;
|
||||||
|
c1->link.next = tmp2_next;
|
||||||
|
c1->link.prev = tmp2_prev;
|
||||||
|
tmp1_prev->next = &c2->link;
|
||||||
|
tmp1_next->prev = &c2->link;
|
||||||
|
tmp2_prev->next = &c1->link;
|
||||||
|
tmp2_next->prev = &c1->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 跨显示器交换处理 */
|
||||||
|
if (config.exchange_cross_monitor && m1 != m2) {
|
||||||
|
tmp_mon = m2;
|
||||||
|
tmp_tags = c2->tags;
|
||||||
|
setmon(c2, m1, c1->tags, false);
|
||||||
|
setmon(c1, tmp_mon, tmp_tags, false);
|
||||||
|
|
||||||
|
if (m1->pertag->ltidxs[tag1]->id == DWINDLE)
|
||||||
|
dwindle_swap_clients(&m1->pertag->dwindle_root[tag1], c1, c2);
|
||||||
|
if (m2->pertag->ltidxs[tag2]->id == DWINDLE)
|
||||||
|
dwindle_swap_clients(&m2->pertag->dwindle_root[tag2], c2, c1);
|
||||||
|
|
||||||
|
sync_scroller_state_to_clients(m1, tag1);
|
||||||
|
sync_scroller_state_to_clients(m2, tag2);
|
||||||
|
arrange(m1, false, false);
|
||||||
|
arrange(m2, false, false);
|
||||||
|
} else {
|
||||||
|
if (m1->pertag->ltidxs[tag1]->id == DWINDLE)
|
||||||
|
dwindle_swap_clients(&m1->pertag->dwindle_root[tag1], c1, c2);
|
||||||
|
sync_scroller_state_to_clients(m1, tag1);
|
||||||
|
arrange(m1, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_remove(&c2->flink);
|
||||||
|
wl_list_insert(&c1->flink, &c2->flink);
|
||||||
|
focusclient(c1, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
non_scroller_fallback:
|
||||||
|
/* 原有的非 scroller 逻辑保持不变 */
|
||||||
if (c1->mon != c2->mon && (c1->prev_in_stack || c2->prev_in_stack ||
|
if (c1->mon != c2->mon && (c1->prev_in_stack || c2->prev_in_stack ||
|
||||||
c1->next_in_stack || c2->next_in_stack))
|
c1->next_in_stack || c2->next_in_stack))
|
||||||
return;
|
return;
|
||||||
|
|
@ -5048,11 +5335,9 @@ void exchange_two_client(Client *c1, Client *c2) {
|
||||||
Client *c1head = get_scroll_stack_head(c1);
|
Client *c1head = get_scroll_stack_head(c1);
|
||||||
Client *c2head = get_scroll_stack_head(c2);
|
Client *c2head = get_scroll_stack_head(c2);
|
||||||
|
|
||||||
// 交换布局参数
|
|
||||||
if (c1head == c2head) {
|
if (c1head == c2head) {
|
||||||
scroller_proportion = c1->scroller_proportion;
|
scroller_proportion = c1->scroller_proportion;
|
||||||
stack_proportion = c1->stack_proportion;
|
stack_proportion = c1->stack_proportion;
|
||||||
|
|
||||||
c1->scroller_proportion = c2->scroller_proportion;
|
c1->scroller_proportion = c2->scroller_proportion;
|
||||||
c1->stack_proportion = c2->stack_proportion;
|
c1->stack_proportion = c2->stack_proportion;
|
||||||
c2->scroller_proportion = scroller_proportion;
|
c2->scroller_proportion = scroller_proportion;
|
||||||
|
|
@ -5062,22 +5347,18 @@ void exchange_two_client(Client *c1, Client *c2) {
|
||||||
master_inner_per = c1->master_inner_per;
|
master_inner_per = c1->master_inner_per;
|
||||||
master_mfact_per = c1->master_mfact_per;
|
master_mfact_per = c1->master_mfact_per;
|
||||||
stack_inner_per = c1->stack_inner_per;
|
stack_inner_per = c1->stack_inner_per;
|
||||||
|
|
||||||
c1->master_inner_per = c2->master_inner_per;
|
c1->master_inner_per = c2->master_inner_per;
|
||||||
c1->master_mfact_per = c2->master_mfact_per;
|
c1->master_mfact_per = c2->master_mfact_per;
|
||||||
c1->stack_inner_per = c2->stack_inner_per;
|
c1->stack_inner_per = c2->stack_inner_per;
|
||||||
|
|
||||||
c2->master_inner_per = master_inner_per;
|
c2->master_inner_per = master_inner_per;
|
||||||
c2->master_mfact_per = master_mfact_per;
|
c2->master_mfact_per = master_mfact_per;
|
||||||
c2->stack_inner_per = stack_inner_per;
|
c2->stack_inner_per = stack_inner_per;
|
||||||
|
|
||||||
// 交换栈链表连接
|
|
||||||
Client *tmp1_next_in_stack = c1->next_in_stack;
|
Client *tmp1_next_in_stack = c1->next_in_stack;
|
||||||
Client *tmp1_prev_in_stack = c1->prev_in_stack;
|
Client *tmp1_prev_in_stack = c1->prev_in_stack;
|
||||||
Client *tmp2_next_in_stack = c2->next_in_stack;
|
Client *tmp2_next_in_stack = c2->next_in_stack;
|
||||||
Client *tmp2_prev_in_stack = c2->prev_in_stack;
|
Client *tmp2_prev_in_stack = c2->prev_in_stack;
|
||||||
|
|
||||||
// 处理相邻节点的情况
|
|
||||||
if (c1->next_in_stack == c2) {
|
if (c1->next_in_stack == c2) {
|
||||||
c1->next_in_stack = tmp2_next_in_stack;
|
c1->next_in_stack = tmp2_next_in_stack;
|
||||||
c2->next_in_stack = c1;
|
c2->next_in_stack = c1;
|
||||||
|
|
@ -5105,13 +5386,11 @@ void exchange_two_client(Client *c1, Client *c2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 交换全局链表连接
|
|
||||||
struct wl_list *tmp1_prev = c1->link.prev;
|
struct wl_list *tmp1_prev = c1->link.prev;
|
||||||
struct wl_list *tmp2_prev = c2->link.prev;
|
struct wl_list *tmp2_prev = c2->link.prev;
|
||||||
struct wl_list *tmp1_next = c1->link.next;
|
struct wl_list *tmp1_next = c1->link.next;
|
||||||
struct wl_list *tmp2_next = c2->link.next;
|
struct wl_list *tmp2_next = c2->link.next;
|
||||||
|
|
||||||
// 处理相邻节点的情况
|
|
||||||
if (c1->link.next == &c2->link) {
|
if (c1->link.next == &c2->link) {
|
||||||
c1->link.next = c2->link.next;
|
c1->link.next = c2->link.next;
|
||||||
c1->link.prev = &c2->link;
|
c1->link.prev = &c2->link;
|
||||||
|
|
@ -5126,19 +5405,17 @@ void exchange_two_client(Client *c1, Client *c2) {
|
||||||
c1->link.prev = tmp2_prev;
|
c1->link.prev = tmp2_prev;
|
||||||
tmp2_prev->next = &c1->link;
|
tmp2_prev->next = &c1->link;
|
||||||
tmp1_next->prev = &c2->link;
|
tmp1_next->prev = &c2->link;
|
||||||
} else { // 不为相邻节点
|
} else {
|
||||||
c2->link.next = tmp1_next;
|
c2->link.next = tmp1_next;
|
||||||
c2->link.prev = tmp1_prev;
|
c2->link.prev = tmp1_prev;
|
||||||
c1->link.next = tmp2_next;
|
c1->link.next = tmp2_next;
|
||||||
c1->link.prev = tmp2_prev;
|
c1->link.prev = tmp2_prev;
|
||||||
|
|
||||||
tmp1_prev->next = &c2->link;
|
tmp1_prev->next = &c2->link;
|
||||||
tmp1_next->prev = &c2->link;
|
tmp1_next->prev = &c2->link;
|
||||||
tmp2_prev->next = &c1->link;
|
tmp2_prev->next = &c1->link;
|
||||||
tmp2_next->prev = &c1->link;
|
tmp2_next->prev = &c1->link;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理跨监视器交换
|
|
||||||
if (config.exchange_cross_monitor) {
|
if (config.exchange_cross_monitor) {
|
||||||
tmp_mon = c2->mon;
|
tmp_mon = c2->mon;
|
||||||
tmp_tags = c2->tags;
|
tmp_tags = c2->tags;
|
||||||
|
|
@ -5161,8 +5438,6 @@ void exchange_two_client(Client *c1, Client *c2) {
|
||||||
arrange(c1->mon, false, false);
|
arrange(c1->mon, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In order to facilitate repeated exchanges for get_focused_stack_client
|
|
||||||
// set c2 focus order behind c1
|
|
||||||
wl_list_remove(&c2->flink);
|
wl_list_remove(&c2->flink);
|
||||||
wl_list_insert(&c1->flink, &c2->flink);
|
wl_list_insert(&c1->flink, &c2->flink);
|
||||||
}
|
}
|
||||||
|
|
@ -5427,24 +5702,28 @@ void reset_maximizescreen_size(Client *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void exit_scroller_stack(Client *c) {
|
void exit_scroller_stack(Client *c) {
|
||||||
// If c is already in a stack, remove it.
|
if (!c || !c->mon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t tag = c->mon->pertag->curtag;
|
||||||
|
struct TagScrollerState *st = c->mon->pertag->scroller_state[tag];
|
||||||
|
if (st) {
|
||||||
|
struct ScrollerStackNode *n = find_scroller_node(st, c);
|
||||||
|
if (n) {
|
||||||
|
scroller_node_remove(st, n);
|
||||||
|
return; /* 节点已移除,客户端指针已在函数内清空 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 没有节点时也要确保客户端脱离堆叠 */
|
||||||
if (c->prev_in_stack) {
|
if (c->prev_in_stack) {
|
||||||
c->prev_in_stack->next_in_stack = c->next_in_stack;
|
c->prev_in_stack->next_in_stack = c->next_in_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->prev_in_stack && c->next_in_stack) {
|
|
||||||
c->next_in_stack->scroller_proportion = c->scroller_proportion;
|
|
||||||
wl_list_remove(&c->next_in_stack->link);
|
|
||||||
wl_list_insert(&c->link, &c->next_in_stack->link);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->next_in_stack) {
|
if (c->next_in_stack) {
|
||||||
c->next_in_stack->prev_in_stack = c->prev_in_stack;
|
c->next_in_stack->prev_in_stack = c->prev_in_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->prev_in_stack = NULL;
|
c->prev_in_stack = NULL;
|
||||||
c->next_in_stack = NULL;
|
c->next_in_stack = NULL;
|
||||||
c->stack_proportion = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setmaximizescreen(Client *c, int32_t maximizescreen) {
|
void setmaximizescreen(Client *c, int32_t maximizescreen) {
|
||||||
|
|
@ -6105,7 +6384,6 @@ void tag_client(const Arg *arg, Client *target_client) {
|
||||||
Client *fc = NULL;
|
Client *fc = NULL;
|
||||||
if (target_client && arg->ui & TAGMASK) {
|
if (target_client && arg->ui & TAGMASK) {
|
||||||
|
|
||||||
exit_scroller_stack(target_client);
|
|
||||||
target_client->tags = arg->ui & TAGMASK;
|
target_client->tags = arg->ui & TAGMASK;
|
||||||
target_client->istagswitching = 1;
|
target_client->istagswitching = 1;
|
||||||
|
|
||||||
|
|
@ -6314,7 +6592,8 @@ void unmapnotify(struct wl_listener *listener, void *data) {
|
||||||
c->swallowedby->mon = c->mon;
|
c->swallowedby->mon = c->mon;
|
||||||
swallow(c->swallowedby, c);
|
swallow(c->swallowedby, c);
|
||||||
} else {
|
} else {
|
||||||
exit_scroller_stack(c);
|
scroller_remove_client(c);
|
||||||
|
dwindle_remove_client(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c == grabc) {
|
if (c == grabc) {
|
||||||
|
|
@ -6396,7 +6675,6 @@ void unmapnotify(struct wl_listener *listener, void *data) {
|
||||||
c->next_in_stack = NULL;
|
c->next_in_stack = NULL;
|
||||||
c->prev_in_stack = NULL;
|
c->prev_in_stack = NULL;
|
||||||
|
|
||||||
dwindle_remove_client(c);
|
|
||||||
wlr_scene_node_destroy(&c->scene->node);
|
wlr_scene_node_destroy(&c->scene->node);
|
||||||
printstatus();
|
printstatus();
|
||||||
motionnotify(0, NULL, 0, 0, 0, 0);
|
motionnotify(0, NULL, 0, 0, 0, 0);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue