mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-05-09 23:50:21 -04:00
reimpl scroller
This commit is contained in:
parent
cabafb2393
commit
df187d9384
6 changed files with 899 additions and 646 deletions
|
|
@ -380,7 +380,6 @@ int32_t moveresize(const Arg *arg) {
|
|||
/* Float the window and tell motionnotify to grab it */
|
||||
if (grabc->isfloating == 0 && arg->ui == CurMove) {
|
||||
grabc->drag_to_tile = true;
|
||||
exit_scroller_stack(grabc);
|
||||
setfloating(grabc, 1);
|
||||
grabc->drag_tile_float_backup_geom = grabc->float_geom;
|
||||
grabc->old_stack_inner_per = 0.0f;
|
||||
|
|
@ -1202,7 +1201,6 @@ int32_t tagsilent(const Arg *arg) {
|
|||
clear_fullscreen_flag(fc);
|
||||
}
|
||||
}
|
||||
exit_scroller_stack(target_client);
|
||||
focusclient(focustop(selmon), 1);
|
||||
arrange(target_client->mon, false, false);
|
||||
return 0;
|
||||
|
|
@ -1355,7 +1353,6 @@ int32_t toggleglobal(const Arg *arg) {
|
|||
selmon->sel->isglobal ^= 1;
|
||||
if (selmon->sel->isglobal &&
|
||||
(selmon->sel->prev_in_stack || selmon->sel->next_in_stack)) {
|
||||
exit_scroller_stack(selmon->sel);
|
||||
arrange(selmon, false, false);
|
||||
}
|
||||
setborder_color(selmon->sel);
|
||||
|
|
|
|||
|
|
@ -836,10 +836,6 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation,
|
|||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
|
||||
if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) {
|
||||
exit_scroller_stack(c);
|
||||
}
|
||||
|
||||
if (from_view && (c->isglobal || c->isunglobal)) {
|
||||
set_size_per(m, c);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
int32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw;
|
||||
Client *c = NULL;
|
||||
|
|
|
|||
891
src/layout/scroller.h
Normal file
891
src/layout/scroller.h
Normal file
|
|
@ -0,0 +1,891 @@
|
|||
/* 滚动布局树节点类型 */
|
||||
typedef enum ScrollerNodeType {
|
||||
SCROLLER_LEAF, /* 叶子,直接持有一个 Client */
|
||||
SCROLLER_H_LIST, /* 水平滚动列表(子节点横向排列) */
|
||||
SCROLLER_V_LIST, /* 垂直滚动列表(子节点纵向排列) */
|
||||
SCROLLER_H_STACK, /* 水平堆栈(子节点横向分割同一区域) */
|
||||
SCROLLER_V_STACK /* 垂直堆栈(子节点纵向分割同一区域) */
|
||||
} ScrollerNodeType;
|
||||
|
||||
typedef struct ScrollerNode ScrollerNode;
|
||||
struct ScrollerNode {
|
||||
ScrollerNodeType type;
|
||||
bool locked; /* 暂未使用,为 resize 预留 */
|
||||
float ratio; /* 当前容器内的尺寸比例 (0~1) */
|
||||
float init_ratio; /* 拖拽开始时的比例 */
|
||||
int32_t container_x, container_y, container_w, container_h;
|
||||
|
||||
ScrollerNode *parent;
|
||||
ScrollerNode *first_child; /* 第一个子节点 */
|
||||
ScrollerNode *next_sibling; /* 兄弟节点 */
|
||||
|
||||
Client *client; /* 仅 SCROLLER_LEAF 有效 */
|
||||
float item_proportion; /* 列表项占滚动方向的宽度/高度比例 */
|
||||
};
|
||||
|
||||
static ScrollerNode *scroller_new_leaf(Client *c);
|
||||
static ScrollerNode *scroller_new_container(ScrollerNodeType type);
|
||||
static ScrollerNode *scroller_find_leaf(ScrollerNode *node, Client *c);
|
||||
static void scroller_free_tree(ScrollerNode *node);
|
||||
static void scroller_remove_from_tree(ScrollerNode **root, Client *c); // 可选
|
||||
static ScrollerNode *scroller_find_item(ScrollerNode *list_node,
|
||||
Client *c); // 查找列表项
|
||||
static void scroller_rebuild_tree(Monitor *m, uint32_t tag, bool horizontal);
|
||||
static void scroller_assign(ScrollerNode *node, Monitor *m, int32_t ax,
|
||||
int32_t ay, int32_t aw, int32_t ah, int32_t gap_h,
|
||||
int32_t gap_v);
|
||||
static void scroller_horizontal_assign(ScrollerNode *list_node, Monitor *m,
|
||||
int32_t ax, int32_t ay, int32_t aw,
|
||||
int32_t ah, int32_t gap_h,
|
||||
int32_t gap_v);
|
||||
static void scroller_vertical_assign(ScrollerNode *list_node, Monitor *m,
|
||||
int32_t ax, int32_t ay, int32_t aw,
|
||||
int32_t ah, int32_t gap_h, int32_t gap_v);
|
||||
|
||||
/* 创建叶子节点 */
|
||||
static ScrollerNode *scroller_new_leaf(Client *c) {
|
||||
ScrollerNode *n = calloc(1, sizeof(ScrollerNode));
|
||||
n->type = SCROLLER_LEAF;
|
||||
n->client = c;
|
||||
n->item_proportion = c->scroller_proportion;
|
||||
/* 若未设置 scroller_proportion,使用单窗口默认比例(也可设为 1.0) */
|
||||
n->item_proportion = (c->scroller_proportion > 0.0f)
|
||||
? c->scroller_proportion
|
||||
: config.scroller_default_proportion_single;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* 创建容器节点 */
|
||||
static ScrollerNode *scroller_new_container(ScrollerNodeType type) {
|
||||
ScrollerNode *n = calloc(1, sizeof(ScrollerNode));
|
||||
n->type = type;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* 在树中查找持有指定 Client 的叶子节点 */
|
||||
static ScrollerNode *scroller_find_leaf(ScrollerNode *node, Client *c) {
|
||||
if (!node)
|
||||
return NULL;
|
||||
if (node->type == SCROLLER_LEAF)
|
||||
return node->client == c ? node : NULL;
|
||||
ScrollerNode *child;
|
||||
for (child = node->first_child; child; child = child->next_sibling) {
|
||||
ScrollerNode *found = scroller_find_leaf(child, c);
|
||||
if (found)
|
||||
return found;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 在滚动列表节点中查找包含指定 Client 的顶层子节点(可能是叶子或堆栈容器) */
|
||||
static ScrollerNode *scroller_find_item(ScrollerNode *list_node, Client *c) {
|
||||
if (!list_node || (list_node->type != SCROLLER_H_LIST &&
|
||||
list_node->type != SCROLLER_V_LIST))
|
||||
return NULL;
|
||||
ScrollerNode *child;
|
||||
for (child = list_node->first_child; child; child = child->next_sibling) {
|
||||
if (scroller_find_leaf(child, c))
|
||||
return child;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 递归释放整个树 */
|
||||
static void scroller_free_tree(ScrollerNode *node) {
|
||||
if (!node)
|
||||
return;
|
||||
ScrollerNode *child = node->first_child;
|
||||
while (child) {
|
||||
ScrollerNode *next = child->next_sibling;
|
||||
scroller_free_tree(child);
|
||||
child = next;
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
|
||||
/* 从树中移除指定 Client 的叶子节点(用于客户端关闭时手动清理) */
|
||||
static void scroller_remove_from_tree(ScrollerNode **root, Client *c) {
|
||||
if (!*root)
|
||||
return;
|
||||
ScrollerNode *leaf = scroller_find_leaf(*root, c);
|
||||
if (!leaf)
|
||||
return;
|
||||
|
||||
ScrollerNode *parent = leaf->parent;
|
||||
if (!parent) {
|
||||
/* 根节点就是叶子 */
|
||||
free(leaf);
|
||||
*root = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 将 leaf 从其父节点的孩子链中移除 */
|
||||
ScrollerNode **indirect = &parent->first_child;
|
||||
while (*indirect && *indirect != leaf)
|
||||
indirect = &(*indirect)->next_sibling;
|
||||
if (*indirect == leaf)
|
||||
*indirect = leaf->next_sibling;
|
||||
free(leaf);
|
||||
|
||||
/* 如果父节点是堆栈容器,且移除后只剩一个孩子,则用该孩子替换父节点 */
|
||||
if ((parent->type == SCROLLER_V_STACK ||
|
||||
parent->type == SCROLLER_H_STACK) &&
|
||||
parent->first_child && !parent->first_child->next_sibling) {
|
||||
/* 唯一的孩子提上来 */
|
||||
ScrollerNode *only_child = parent->first_child;
|
||||
only_child->parent = parent->parent;
|
||||
|
||||
if (!parent->parent) {
|
||||
*root = only_child;
|
||||
} else {
|
||||
ScrollerNode **pp = &parent->parent->first_child;
|
||||
while (*pp && *pp != parent)
|
||||
pp = &(*pp)->next_sibling;
|
||||
if (*pp == parent)
|
||||
*pp = only_child;
|
||||
}
|
||||
/* 释放父节点本身 */
|
||||
parent->first_child = NULL;
|
||||
scroller_free_tree(parent);
|
||||
}
|
||||
/* 若父节点是列表节点,移除后孩子为空也无妨 */
|
||||
}
|
||||
|
||||
/* 构建树时对单个堆栈头处理的辅助函数(原 arrange_stack 的 fallback) */
|
||||
static void
|
||||
scroller_fix_stack_proportions(Client *head)
|
||||
{
|
||||
int stack_size = 0;
|
||||
Client *s = head;
|
||||
while (s) {
|
||||
stack_size++;
|
||||
s = s->next_in_stack;
|
||||
}
|
||||
if (stack_size == 0) return;
|
||||
|
||||
/* 检查和修正 stack_proportion */
|
||||
for (s = head; s; s = s->next_in_stack) {
|
||||
if (s->stack_proportion <= 0.0f || s->stack_proportion >= 1.0f) {
|
||||
s->stack_proportion = (stack_size == 1) ? 1.0f : 1.0f / (stack_size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 归一化(原 arrange_stack 逻辑) */
|
||||
float total = 0.0f;
|
||||
for (s = head; s; s = s->next_in_stack) total += s->stack_proportion;
|
||||
if (total > 0.0f) {
|
||||
for (s = head; s; s = s->next_in_stack)
|
||||
s->stack_proportion /= total;
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
static void scroller_rebuild_tree(Monitor *m, uint32_t tag, bool horizontal) {
|
||||
ScrollerNode **root = horizontal ? &m->pertag->scroller_root[tag]
|
||||
: &m->pertag->v_scroller_root[tag];
|
||||
|
||||
/* 释放旧树 */
|
||||
scroller_free_tree(*root);
|
||||
*root = NULL;
|
||||
|
||||
/* 收集当前 tag 中可见的滚动客户端堆栈头 */
|
||||
Client *vis_heads[512];
|
||||
int32_t n_heads = 0;
|
||||
Client *c;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) {
|
||||
if (n_heads < 512)
|
||||
vis_heads[n_heads++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_heads == 0)
|
||||
return;
|
||||
|
||||
/* 创建滚动列表节点 */
|
||||
ScrollerNode *list_node =
|
||||
scroller_new_container(horizontal ? SCROLLER_H_LIST : SCROLLER_V_LIST);
|
||||
*root = list_node;
|
||||
|
||||
/* 逐个插入堆栈头(保持全局 clients 的顺序) */
|
||||
for (int i = 0; i < n_heads; i++) {
|
||||
Client *head = vis_heads[i];
|
||||
ScrollerNode *item;
|
||||
|
||||
if (head->next_in_stack && head->prev_in_stack == NULL) {
|
||||
|
||||
/* 先用原函数修正并归一化堆栈内比例 */
|
||||
scroller_fix_stack_proportions(head);
|
||||
|
||||
/* 有多个客户端在堆栈中,创建堆栈容器 */
|
||||
ScrollerNode *stack = scroller_new_container(
|
||||
horizontal ? SCROLLER_V_STACK : SCROLLER_H_STACK);
|
||||
stack->item_proportion = head->scroller_proportion;
|
||||
|
||||
/* 遍历堆栈中的所有客户端,创建叶子并计算总比例 */
|
||||
Client *s = head;
|
||||
float total_proportion = 0.0f;
|
||||
ScrollerNode **tail = &stack->first_child;
|
||||
while (s) {
|
||||
ScrollerNode *leaf = scroller_new_leaf(s);
|
||||
leaf->ratio = s->stack_proportion; /* 暂存原始比例 */
|
||||
leaf->parent = stack;
|
||||
*tail = leaf;
|
||||
tail = &leaf->next_sibling;
|
||||
total_proportion += leaf->ratio;
|
||||
s = s->next_in_stack;
|
||||
}
|
||||
|
||||
/* 归一化堆栈内部比例 */
|
||||
if (total_proportion > 0.0f) {
|
||||
for (ScrollerNode *kid = stack->first_child; kid;
|
||||
kid = kid->next_sibling)
|
||||
kid->ratio /= total_proportion;
|
||||
}
|
||||
item = stack;
|
||||
} else {
|
||||
/* 单个客户端,直接作为叶子 */
|
||||
ScrollerNode *leaf = scroller_new_leaf(head);
|
||||
item = leaf;
|
||||
}
|
||||
|
||||
/* 将该项添加到列表末尾 */
|
||||
if (!list_node->first_child) {
|
||||
list_node->first_child = item;
|
||||
} else {
|
||||
ScrollerNode *last = list_node->first_child;
|
||||
while (last->next_sibling)
|
||||
last = last->next_sibling;
|
||||
last->next_sibling = item;
|
||||
}
|
||||
item->parent = list_node;
|
||||
}
|
||||
}
|
||||
|
||||
static void scroller_assign(ScrollerNode *node, Monitor *m, int32_t ax,
|
||||
int32_t ay, int32_t aw, int32_t ah, int32_t gap_h,
|
||||
int32_t gap_v) {
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
node->container_x = ax;
|
||||
node->container_y = ay;
|
||||
node->container_w = aw;
|
||||
node->container_h = ah;
|
||||
|
||||
switch (node->type) {
|
||||
case SCROLLER_LEAF:
|
||||
if (node->client) {
|
||||
struct wlr_box box = {ax, ay, MAX(1, aw), MAX(1, ah)};
|
||||
resize(node->client, box, 0);
|
||||
}
|
||||
return;
|
||||
|
||||
case SCROLLER_H_LIST:
|
||||
scroller_horizontal_assign(node, m, ax, ay, aw, ah, gap_h, gap_v);
|
||||
return;
|
||||
|
||||
case SCROLLER_V_LIST:
|
||||
scroller_vertical_assign(node, m, ax, ay, aw, ah, gap_h, gap_v);
|
||||
return;
|
||||
|
||||
case SCROLLER_V_STACK: {
|
||||
int32_t n = 0;
|
||||
for (ScrollerNode *c = node->first_child; c; c = c->next_sibling)
|
||||
n++;
|
||||
if (n == 0)
|
||||
return;
|
||||
int32_t total_h = ah - (n - 1) * gap_v;
|
||||
if (total_h <= 0)
|
||||
total_h = 1;
|
||||
int32_t cy = ay;
|
||||
ScrollerNode *child = node->first_child;
|
||||
while (child) {
|
||||
int32_t h = (child->next_sibling == NULL)
|
||||
? (ay + ah - cy)
|
||||
: (int32_t)(total_h * child->ratio + 0.5f);
|
||||
h = MAX(1, h);
|
||||
scroller_assign(child, m, ax, cy, aw, h, gap_h, gap_v);
|
||||
cy += h + gap_v;
|
||||
child = child->next_sibling;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case SCROLLER_H_STACK: {
|
||||
int32_t n = 0;
|
||||
for (ScrollerNode *c = node->first_child; c; c = c->next_sibling)
|
||||
n++;
|
||||
if (n == 0)
|
||||
return;
|
||||
int32_t total_w = aw - (n - 1) * gap_h;
|
||||
if (total_w <= 0)
|
||||
total_w = 1;
|
||||
int32_t cx = ax;
|
||||
ScrollerNode *child = node->first_child;
|
||||
while (child) {
|
||||
int32_t w = (child->next_sibling == NULL)
|
||||
? (ax + aw - cx)
|
||||
: (int32_t)(total_w * child->ratio + 0.5f);
|
||||
w = MAX(1, w);
|
||||
scroller_assign(child, m, cx, ay, w, ah, gap_h, gap_v);
|
||||
cx += w + gap_h;
|
||||
child = child->next_sibling;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
scroller_horizontal_assign(ScrollerNode *list_node, Monitor *m,
|
||||
int32_t ax, int32_t ay, int32_t aw, int32_t ah,
|
||||
int32_t gap_h, int32_t gap_v)
|
||||
{
|
||||
ScrollerNode *children[512];
|
||||
int32_t n = 0;
|
||||
for (ScrollerNode *c = list_node->first_child; c && n < 512; c = c->next_sibling)
|
||||
children[n++] = c;
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
/* 单窗口特殊处理(恢复原 scroller 逻辑) */
|
||||
if (n == 1 && !config.scroller_ignore_proportion_single) {
|
||||
Client *c = (children[0]->type == SCROLLER_LEAF)
|
||||
? children[0]->client
|
||||
: children[0]->first_child->client;
|
||||
if (c && !c->isfullscreen && !c->ismaximizescreen) {
|
||||
float single_proportion = c->scroller_proportion_single > 0.0f
|
||||
? c->scroller_proportion_single
|
||||
: config.scroller_default_proportion_single;
|
||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && m->visible_scroll_tiling_clients == 1) {
|
||||
cur_gappoh = cur_gappov = 0;
|
||||
}
|
||||
struct wlr_box target = {
|
||||
.height = m->w.height - 2 * cur_gappov,
|
||||
.width = (m->w.width - 2 * cur_gappoh) * single_proportion,
|
||||
.x = m->w.x + (m->w.width - target.width) / 2,
|
||||
.y = m->w.y + (m->w.height - target.height) / 2,
|
||||
};
|
||||
horizontal_check_scroller_root_inside_mon(c, &target);
|
||||
scroller_assign(children[0], m, target.x, target.y,
|
||||
target.width, target.height, gap_h, gap_v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* 多窗口原逻辑 */
|
||||
Client *root_client = NULL;
|
||||
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);
|
||||
|
||||
if (root_client)
|
||||
root_client = get_scroll_stack_head(root_client);
|
||||
|
||||
int focus_idx = -1;
|
||||
if (root_client) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (scroller_find_leaf(children[i], root_client)) {
|
||||
focus_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (focus_idx < 0)
|
||||
focus_idx = 0;
|
||||
|
||||
/* 间隙与最大宽度 */
|
||||
int32_t cur_gappih = enablegaps ? m->gappih : 0;
|
||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && m->visible_scroll_tiling_clients == 1) {
|
||||
cur_gappih = cur_gappoh = cur_gappov = 0;
|
||||
}
|
||||
int32_t max_client_width = m->w.width - 2 * config.scroller_structs - cur_gappih;
|
||||
|
||||
/* 计算各项目标区域(已通过 rebuild 保证 item_proportion 有效) */
|
||||
struct wlr_box target[512];
|
||||
for (int i = 0; i < n; i++) {
|
||||
target[i] = (struct wlr_box){0};
|
||||
target[i].height = m->w.height - 2 * cur_gappov;
|
||||
target[i].width = max_client_width * children[i]->item_proportion;
|
||||
target[i].y = m->w.y + (m->w.height - target[i].height) / 2;
|
||||
}
|
||||
|
||||
/* 安全获取焦点客户端 */
|
||||
Client *focus_client = (children[focus_idx]->type == SCROLLER_LEAF)
|
||||
? children[focus_idx]->client
|
||||
: children[focus_idx]->first_child->client;
|
||||
if (!focus_client)
|
||||
focus_client = children[focus_idx]->first_child->client;
|
||||
|
||||
bool need_scroller = false;
|
||||
bool over_overspread_to_left = false;
|
||||
bool need_apply_overspread = false;
|
||||
bool need_apply_center = false;
|
||||
|
||||
/* 判断是否需要滚动 */
|
||||
if (focus_client->geom.x >= m->w.x + config.scroller_structs &&
|
||||
focus_client->geom.x + focus_client->geom.width <= m->w.x + m->w.width - config.scroller_structs)
|
||||
need_scroller = false;
|
||||
else
|
||||
need_scroller = true;
|
||||
|
||||
/* 判断是否溢出 */
|
||||
need_apply_overspread =
|
||||
config.scroller_prefer_overspread &&
|
||||
m->visible_scroll_tiling_clients > 1 &&
|
||||
(focus_idx == 0 || focus_idx == n - 1) &&
|
||||
children[focus_idx]->item_proportion < 1.0f;
|
||||
if (need_apply_overspread) {
|
||||
over_overspread_to_left = (focus_idx == 0);
|
||||
Client *check_c = NULL;
|
||||
if (over_overspread_to_left) {
|
||||
check_c = children[1]->first_child ?
|
||||
children[1]->first_child->client : children[1]->client;
|
||||
if (!check_c || !INSIDEMON(check_c) ||
|
||||
(children[1]->item_proportion + children[focus_idx]->item_proportion >= 1.0f))
|
||||
need_scroller = true;
|
||||
else
|
||||
need_apply_overspread = false;
|
||||
} else {
|
||||
check_c = children[focus_idx - 1]->first_child ?
|
||||
children[focus_idx - 1]->first_child->client :
|
||||
children[focus_idx - 1]->client;
|
||||
if (!check_c || !INSIDEMON(check_c) ||
|
||||
(children[focus_idx - 1]->item_proportion +
|
||||
children[focus_idx]->item_proportion >= 1.0f))
|
||||
need_scroller = true;
|
||||
else
|
||||
need_apply_overspread = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 判断是否居中 */
|
||||
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 +
|
||||
children[focus_idx]->item_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;
|
||||
|
||||
horizontal_scroll_adjust_fullandmax(focus_client, &target[focus_idx]);
|
||||
if (focus_client->isfullscreen) {
|
||||
target[focus_idx].x = m->m.x;
|
||||
} else if (focus_client->ismaximizescreen) {
|
||||
target[focus_idx].x = m->w.x + cur_gappoh;
|
||||
} else if (need_scroller) {
|
||||
if (need_apply_center) {
|
||||
target[focus_idx].x = m->w.x + (m->w.width - target[focus_idx].width) / 2;
|
||||
} else if (need_apply_overspread) {
|
||||
if (over_overspread_to_left)
|
||||
target[focus_idx].x = m->w.x + config.scroller_structs;
|
||||
else
|
||||
target[focus_idx].x = m->w.x + (m->w.width -
|
||||
children[focus_idx]->item_proportion * max_client_width -
|
||||
config.scroller_structs);
|
||||
} else {
|
||||
if (focus_client->geom.x > m->w.x + m->w.width / 2)
|
||||
target[focus_idx].x = m->w.x + (m->w.width -
|
||||
children[focus_idx]->item_proportion * max_client_width -
|
||||
config.scroller_structs);
|
||||
else
|
||||
target[focus_idx].x = m->w.x + config.scroller_structs;
|
||||
}
|
||||
} else {
|
||||
target[focus_idx].x = focus_client->geom.x;
|
||||
}
|
||||
horizontal_check_scroller_root_inside_mon(focus_client, &target[focus_idx]);
|
||||
|
||||
scroller_assign(children[focus_idx], m,
|
||||
target[focus_idx].x, target[focus_idx].y,
|
||||
target[focus_idx].width, target[focus_idx].height,
|
||||
cur_gappih, gap_v);
|
||||
|
||||
/* 向左分配 */
|
||||
for (int i = 1; i <= focus_idx; i++) {
|
||||
ScrollerNode *item = children[focus_idx - i];
|
||||
Client *c = (item->type == SCROLLER_LEAF) ? item->client
|
||||
: item->first_child->client;
|
||||
target[focus_idx - i].height = m->w.height - 2 * cur_gappov;
|
||||
target[focus_idx - i].width = max_client_width * item->item_proportion;
|
||||
horizontal_scroll_adjust_fullandmax(c, &target[focus_idx - i]);
|
||||
target[focus_idx - i].x = target[focus_idx - i + 1].x -
|
||||
cur_gappih - target[focus_idx - i].width;
|
||||
scroller_assign(item, m,
|
||||
target[focus_idx - i].x, target[focus_idx - i].y,
|
||||
target[focus_idx - i].width, target[focus_idx - i].height,
|
||||
cur_gappih, gap_v);
|
||||
}
|
||||
|
||||
/* 向右分配 */
|
||||
for (int i = 1; i < n - focus_idx; i++) {
|
||||
ScrollerNode *item = children[focus_idx + i];
|
||||
Client *c = (item->type == SCROLLER_LEAF) ? item->client
|
||||
: item->first_child->client;
|
||||
target[focus_idx + i].height = m->w.height - 2 * cur_gappov;
|
||||
target[focus_idx + i].width = max_client_width * item->item_proportion;
|
||||
horizontal_scroll_adjust_fullandmax(c, &target[focus_idx + i]);
|
||||
target[focus_idx + i].x = target[focus_idx + i - 1].x +
|
||||
cur_gappih + target[focus_idx + i - 1].width;
|
||||
scroller_assign(item, m,
|
||||
target[focus_idx + i].x, target[focus_idx + i].y,
|
||||
target[focus_idx + i].width, target[focus_idx + i].height,
|
||||
cur_gappih, gap_v);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
scroller_vertical_assign(ScrollerNode *list_node, Monitor *m,
|
||||
int32_t ax, int32_t ay, int32_t aw, int32_t ah,
|
||||
int32_t gap_h, int32_t gap_v)
|
||||
{
|
||||
ScrollerNode *children[512];
|
||||
int32_t n = 0;
|
||||
for (ScrollerNode *c = list_node->first_child; c && n < 512; c = c->next_sibling)
|
||||
children[n++] = c;
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
/* 单窗口特殊处理 */
|
||||
if (n == 1 && !config.scroller_ignore_proportion_single) {
|
||||
Client *c = (children[0]->type == SCROLLER_LEAF)
|
||||
? children[0]->client
|
||||
: children[0]->first_child->client;
|
||||
if (c && !c->isfullscreen && !c->ismaximizescreen) {
|
||||
float single_proportion = c->scroller_proportion_single > 0.0f
|
||||
? c->scroller_proportion_single
|
||||
: config.scroller_default_proportion_single;
|
||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && m->visible_scroll_tiling_clients == 1) {
|
||||
cur_gappoh = cur_gappov = 0;
|
||||
}
|
||||
struct wlr_box target = {
|
||||
.width = m->w.width - 2 * cur_gappoh,
|
||||
.height = (m->w.height - 2 * cur_gappov) * single_proportion,
|
||||
.x = m->w.x + (m->w.width - target.width) / 2,
|
||||
.y = m->w.y + (m->w.height - target.height) / 2,
|
||||
};
|
||||
vertical_check_scroller_root_inside_mon(c, &target);
|
||||
scroller_assign(children[0], m, target.x, target.y,
|
||||
target.width, target.height, gap_h, gap_v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* 多窗口原逻辑(与水平对称) */
|
||||
Client *root_client = NULL;
|
||||
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);
|
||||
|
||||
if (root_client)
|
||||
root_client = get_scroll_stack_head(root_client);
|
||||
|
||||
int focus_idx = -1;
|
||||
if (root_client) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (scroller_find_leaf(children[i], root_client)) {
|
||||
focus_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (focus_idx < 0)
|
||||
focus_idx = 0;
|
||||
|
||||
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
|
||||
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
|
||||
int32_t cur_gappov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && m->visible_scroll_tiling_clients == 1) {
|
||||
cur_gappiv = cur_gappoh = cur_gappov = 0;
|
||||
}
|
||||
int32_t max_client_height = m->w.height - 2 * config.scroller_structs - cur_gappiv;
|
||||
|
||||
struct wlr_box target[512];
|
||||
for (int i = 0; i < n; i++) {
|
||||
target[i] = (struct wlr_box){0};
|
||||
target[i].width = m->w.width - 2 * cur_gappoh;
|
||||
target[i].height = max_client_height * children[i]->item_proportion;
|
||||
target[i].x = m->w.x + (m->w.width - target[i].width) / 2;
|
||||
}
|
||||
|
||||
Client *focus_client = (children[focus_idx]->type == SCROLLER_LEAF)
|
||||
? children[focus_idx]->client
|
||||
: children[focus_idx]->first_child->client;
|
||||
if (!focus_client)
|
||||
focus_client = children[focus_idx]->first_child->client;
|
||||
|
||||
bool need_scroller = false;
|
||||
bool over_overspread_to_up = false;
|
||||
bool need_apply_overspread = false;
|
||||
bool need_apply_center = false;
|
||||
|
||||
if (focus_client->geom.y >= m->w.y + config.scroller_structs &&
|
||||
focus_client->geom.y + focus_client->geom.height <= m->w.y + m->w.height - config.scroller_structs)
|
||||
need_scroller = false;
|
||||
else
|
||||
need_scroller = true;
|
||||
|
||||
need_apply_overspread =
|
||||
config.scroller_prefer_overspread &&
|
||||
m->visible_scroll_tiling_clients > 1 &&
|
||||
(focus_idx == 0 || focus_idx == n - 1) &&
|
||||
children[focus_idx]->item_proportion < 1.0f;
|
||||
if (need_apply_overspread) {
|
||||
over_overspread_to_up = (focus_idx == 0);
|
||||
Client *check_c = NULL;
|
||||
if (over_overspread_to_up) {
|
||||
check_c = children[1]->first_child ?
|
||||
children[1]->first_child->client : children[1]->client;
|
||||
if (!check_c || !INSIDEMON(check_c) ||
|
||||
(children[1]->item_proportion + children[focus_idx]->item_proportion >= 1.0f))
|
||||
need_scroller = true;
|
||||
else
|
||||
need_apply_overspread = false;
|
||||
} else {
|
||||
check_c = children[focus_idx - 1]->first_child ?
|
||||
children[focus_idx - 1]->first_child->client :
|
||||
children[focus_idx - 1]->client;
|
||||
if (!check_c || !INSIDEMON(check_c) ||
|
||||
(children[focus_idx - 1]->item_proportion +
|
||||
children[focus_idx]->item_proportion >= 1.0f))
|
||||
need_scroller = true;
|
||||
else
|
||||
need_apply_overspread = false;
|
||||
}
|
||||
}
|
||||
|
||||
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 +
|
||||
children[focus_idx]->item_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;
|
||||
|
||||
vertical_scroll_adjust_fullandmax(focus_client, &target[focus_idx]);
|
||||
if (focus_client->isfullscreen) {
|
||||
target[focus_idx].y = m->m.y;
|
||||
} else if (focus_client->ismaximizescreen) {
|
||||
target[focus_idx].y = m->w.y + cur_gappov;
|
||||
} else if (need_scroller) {
|
||||
if (need_apply_center) {
|
||||
target[focus_idx].y = m->w.y + (m->w.height - target[focus_idx].height) / 2;
|
||||
} else if (need_apply_overspread) {
|
||||
if (over_overspread_to_up)
|
||||
target[focus_idx].y = m->w.y + config.scroller_structs;
|
||||
else
|
||||
target[focus_idx].y = m->w.y + (m->w.height -
|
||||
children[focus_idx]->item_proportion * max_client_height -
|
||||
config.scroller_structs);
|
||||
} else {
|
||||
if (focus_client->geom.y > m->w.y + m->w.height / 2)
|
||||
target[focus_idx].y = m->w.y + (m->w.height -
|
||||
children[focus_idx]->item_proportion * max_client_height -
|
||||
config.scroller_structs);
|
||||
else
|
||||
target[focus_idx].y = m->w.y + config.scroller_structs;
|
||||
}
|
||||
} else {
|
||||
target[focus_idx].y = focus_client->geom.y;
|
||||
}
|
||||
vertical_check_scroller_root_inside_mon(focus_client, &target[focus_idx]);
|
||||
|
||||
scroller_assign(children[focus_idx], m,
|
||||
target[focus_idx].x, target[focus_idx].y,
|
||||
target[focus_idx].width, target[focus_idx].height,
|
||||
gap_h, cur_gappiv);
|
||||
|
||||
/* 向上分配 */
|
||||
for (int i = 1; i <= focus_idx; i++) {
|
||||
ScrollerNode *item = children[focus_idx - i];
|
||||
Client *c = (item->type == SCROLLER_LEAF) ? item->client
|
||||
: item->first_child->client;
|
||||
target[focus_idx - i].width = m->w.width - 2 * cur_gappoh;
|
||||
target[focus_idx - i].height = max_client_height * item->item_proportion;
|
||||
vertical_scroll_adjust_fullandmax(c, &target[focus_idx - i]);
|
||||
target[focus_idx - i].y = target[focus_idx - i + 1].y -
|
||||
cur_gappiv - target[focus_idx - i].height;
|
||||
scroller_assign(item, m,
|
||||
target[focus_idx - i].x, target[focus_idx - i].y,
|
||||
target[focus_idx - i].width, target[focus_idx - i].height,
|
||||
gap_h, cur_gappiv);
|
||||
}
|
||||
|
||||
/* 向下分配 */
|
||||
for (int i = 1; i < n - focus_idx; i++) {
|
||||
ScrollerNode *item = children[focus_idx + i];
|
||||
Client *c = (item->type == SCROLLER_LEAF) ? item->client
|
||||
: item->first_child->client;
|
||||
target[focus_idx + i].width = m->w.width - 2 * cur_gappoh;
|
||||
target[focus_idx + i].height = max_client_height * item->item_proportion;
|
||||
vertical_scroll_adjust_fullandmax(c, &target[focus_idx + i]);
|
||||
target[focus_idx + i].y = target[focus_idx + i - 1].y +
|
||||
cur_gappiv + target[focus_idx + i - 1].height;
|
||||
scroller_assign(item, m,
|
||||
target[focus_idx + i].x, target[focus_idx + i].y,
|
||||
target[focus_idx + i].width, target[focus_idx + i].height,
|
||||
gap_h, cur_gappiv);
|
||||
}
|
||||
}
|
||||
|
||||
void scroller(Monitor *m) {
|
||||
uint32_t tag = m->pertag->curtag;
|
||||
|
||||
/* 重建当前 tag 的水平滚动树 */
|
||||
scroller_rebuild_tree(m, tag, true);
|
||||
ScrollerNode *root = m->pertag->scroller_root[tag];
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
/* 获取间隙 */
|
||||
int32_t gap_ih = enablegaps ? m->gappih : 0;
|
||||
int32_t gap_iv = enablegaps ? m->gappiv : 0;
|
||||
int32_t gap_oh = enablegaps ? m->gappoh : 0;
|
||||
int32_t gap_ov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && m->visible_scroll_tiling_clients == 1)
|
||||
gap_ih = gap_iv = gap_oh = gap_ov = 0;
|
||||
|
||||
/* 分配 */
|
||||
scroller_assign(root, m, m->w.x + gap_oh, m->w.y + gap_ov,
|
||||
m->w.width - 2 * gap_oh, m->w.height - 2 * gap_ov, gap_ih,
|
||||
gap_iv);
|
||||
}
|
||||
|
||||
void vertical_scroller(Monitor *m) {
|
||||
uint32_t tag = m->pertag->curtag;
|
||||
|
||||
/* 重建当前 tag 的垂直滚动树 */
|
||||
scroller_rebuild_tree(m, tag, false);
|
||||
ScrollerNode *root = m->pertag->v_scroller_root[tag];
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
int32_t gap_ih = enablegaps ? m->gappih : 0;
|
||||
int32_t gap_iv = enablegaps ? m->gappiv : 0;
|
||||
int32_t gap_oh = enablegaps ? m->gappoh : 0;
|
||||
int32_t gap_ov = enablegaps ? m->gappov : 0;
|
||||
if (config.smartgaps && m->visible_scroll_tiling_clients == 1)
|
||||
gap_ih = gap_iv = gap_oh = gap_ov = 0;
|
||||
|
||||
scroller_assign(root, m, m->w.x + gap_oh, m->w.y + gap_ov,
|
||||
m->w.width - 2 * gap_oh, m->w.height - 2 * gap_ov, gap_ih,
|
||||
gap_iv);
|
||||
}
|
||||
|
||||
static 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++) {
|
||||
scroller_remove_from_tree(&m->pertag->scroller_root[t], c);
|
||||
scroller_remove_from_tree(&m->pertag->v_scroller_root[t], c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
int32_t i, n;
|
||||
int32_t cx, cy, cw, ch;
|
||||
|
|
|
|||
10
src/mango.c
10
src/mango.c
|
|
@ -561,6 +561,7 @@ typedef struct {
|
|||
} SessionLock;
|
||||
|
||||
typedef struct DwindleNode DwindleNode;
|
||||
typedef struct ScrollerNode ScrollerNode;
|
||||
|
||||
/* function declarations */
|
||||
static void applybounds(
|
||||
|
|
@ -954,6 +955,8 @@ struct Pertag {
|
|||
int32_t no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */
|
||||
int32_t open_as_floating[LENGTH(tags) + 1]; /* open_as_floating per tag */
|
||||
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
|
||||
struct ScrollerNode *scroller_root[LENGTH(tags) + 1];
|
||||
struct ScrollerNode *v_scroller_root[LENGTH(tags) + 1];
|
||||
const Layout
|
||||
*ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */
|
||||
};
|
||||
|
|
@ -1028,6 +1031,7 @@ static struct wl_event_source *sync_keymap;
|
|||
#include "layout/arrange.h"
|
||||
#include "layout/dwindle.h"
|
||||
#include "layout/horizontal.h"
|
||||
#include "layout/scroller.h"
|
||||
#include "layout/vertical.h"
|
||||
|
||||
void client_change_mon(Client *c, Monitor *m) {
|
||||
|
|
@ -2548,8 +2552,10 @@ void cleanupmon(struct wl_listener *listener, void *data) {
|
|||
}
|
||||
m->wlr_output->data = NULL;
|
||||
|
||||
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]);
|
||||
scroller_free_tree(m->pertag->scroller_root[t]);
|
||||
}
|
||||
free(m->pertag);
|
||||
free(m);
|
||||
}
|
||||
|
|
@ -6105,7 +6111,6 @@ void tag_client(const Arg *arg, Client *target_client) {
|
|||
Client *fc = NULL;
|
||||
if (target_client && arg->ui & TAGMASK) {
|
||||
|
||||
exit_scroller_stack(target_client);
|
||||
target_client->tags = arg->ui & TAGMASK;
|
||||
target_client->istagswitching = 1;
|
||||
|
||||
|
|
@ -6397,6 +6402,7 @@ void unmapnotify(struct wl_listener *listener, void *data) {
|
|||
c->prev_in_stack = NULL;
|
||||
|
||||
dwindle_remove_client(c);
|
||||
scroller_remove_client(c);
|
||||
wlr_scene_node_destroy(&c->scene->node);
|
||||
printstatus();
|
||||
motionnotify(0, NULL, 0, 0, 0, 0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue