maomaowm/src/layout/scroll.h
2026-05-10 23:46:44 +08:00

829 lines
No EOL
25 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 获取或创建指定 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);
}
}
}
}