diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 3c1d2154..44f3d3c2 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -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); diff --git a/src/layout/arrange.h b/src/layout/arrange.h index b822a729..9ae56691 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -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); } diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index e69b4cdd..37c8793e 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -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; diff --git a/src/layout/scroller.h b/src/layout/scroller.h new file mode 100644 index 00000000..2f0af871 --- /dev/null +++ b/src/layout/scroller.h @@ -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); + } + } +} diff --git a/src/layout/vertical.h b/src/layout/vertical.h index 3d8863af..36114dde 100644 --- a/src/layout/vertical.h +++ b/src/layout/vertical.h @@ -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; diff --git a/src/mango.c b/src/mango.c index 34d4d764..6d986a2f 100644 --- a/src/mango.c +++ b/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);