From 65c9ac6dd2290efa3e338c656487561a045287f9 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 16 May 2026 17:32:39 +0800 Subject: [PATCH] feat: grid and fair layout support resize tile window --- src/layout/arrange.h | 301 ++++++++++++++++++++++++++++++++++++++++ src/layout/horizontal.h | 288 +++++++++++++++++++++++++++++++++----- src/layout/vertical.h | 243 +++++++++++++++++++++++--------- src/mango.c | 10 +- 4 files changed, 741 insertions(+), 101 deletions(-) diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 42d15f86..e452708b 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -536,6 +536,302 @@ void resize_tile_dwindle(Client *grabc, bool isdrag, int32_t offsetx, } } +void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx, + int32_t offsety, uint32_t time) { + if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen) + return; + Monitor *m = grabc->mon; + if (m->isoverview) + return; + + if (m->visible_tiling_clients <= 1) + return; + + // 获取当前布局 ID + const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag]; + + if (!start_drag_window && isdrag) { + drag_begin_cursorx = cursor->x; + drag_begin_cursory = cursor->y; + start_drag_window = true; + + Client *c; + wl_list_for_each(c, &clients, link) { + c->old_grid_col_per = + (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f; + c->old_grid_row_per = + (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f; + } + + grabc->old_grid_col_per = grabc->grid_col_per; + grabc->old_grid_row_per = grabc->grid_row_per; + + grabc->cursor_in_left_half = + cursor->x < grabc->geom.x + grabc->geom.width / 2; + grabc->cursor_in_upper_half = + cursor->y < grabc->geom.y + grabc->geom.height / 2; + grabc->drag_begin_geom = grabc->geom; + } else { + if (isdrag) { + offsetx = cursor->x - drag_begin_cursorx; + offsety = cursor->y - drag_begin_cursory; + } else { + grabc->drag_begin_geom = grabc->geom; + Client *c; + wl_list_for_each(c, &clients, link) { + c->old_grid_col_per = + (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f; + c->old_grid_row_per = + (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f; + } + grabc->cursor_in_upper_half = false; + grabc->cursor_in_left_half = false; + } + + // 以屏幕分辨率为基准算出缩放比变化的量 + float delta_x = (float)offsetx * grabc->old_grid_col_per / + grabc->drag_begin_geom.width; + float delta_y = (float)offsety * grabc->old_grid_row_per / + grabc->drag_begin_geom.height; + + int adj_c_idx = grabc->grid_col_idx; + int adj_r_idx = grabc->grid_row_idx; + float sign_x = 1.0f, sign_y = 1.0f; + + if (isdrag) { + if (grabc->cursor_in_left_half) { + adj_c_idx -= 1; + sign_x = -1.0f; + } else { + adj_c_idx += 1; + sign_x = 1.0f; + } + + if (grabc->cursor_in_upper_half) { + adj_r_idx -= 1; + sign_y = -1.0f; + } else { + adj_r_idx += 1; + sign_y = 1.0f; + } + } + // 键盘热键逻辑不变 + int max_col = -1, max_row = -1, min_col = INT32_MAX, + min_row = INT32_MAX; + Client *tmp; + wl_list_for_each(tmp, &clients, link) { + if (tmp->mon != m || !VISIBLEON(tmp, m) || !ISTILED(tmp)) + continue; + if (tmp->grid_col_idx > max_col) + max_col = tmp->grid_col_idx; + if (tmp->grid_row_idx > max_row) + max_row = tmp->grid_row_idx; + if (tmp->grid_col_idx < min_col) + min_col = tmp->grid_col_idx; + if (tmp->grid_row_idx < min_row) + min_row = tmp->grid_row_idx; + } + + adj_c_idx = grabc->grid_col_idx + 1; + adj_r_idx = grabc->grid_row_idx + 1; + sign_x = 1.0f; + sign_y = 1.0f; + + if (grabc->grid_col_idx == max_col) { + adj_c_idx = grabc->grid_col_idx - 1; + sign_x = -1.0f; + } + if (grabc->grid_row_idx == max_row) { + adj_r_idx = grabc->grid_row_idx - 1; + sign_y = -1.0f; + } + if (grabc->grid_col_idx == min_col) { + adj_c_idx = grabc->grid_col_idx + 1; + sign_x = 1.0f; + } + if (grabc->grid_row_idx == min_row) { + adj_r_idx = grabc->grid_row_idx + 1; + sign_y = 1.0f; + } + + float dx = delta_x * sign_x; + float dy = delta_y * sign_y; + + float my_old_col = grabc->old_grid_col_per; + float my_old_row = grabc->old_grid_row_per; + float adj_old_col = -1.0f, adj_old_row = -1.0f; + + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) + continue; + if (c->grid_col_idx == adj_c_idx && adj_old_col < 0) + adj_old_col = c->old_grid_col_per; + if (c->grid_row_idx == adj_r_idx && adj_old_row < 0) + adj_old_row = c->old_grid_row_per; + } + + // 应用列宽调节 + if (adj_old_col > 0.0f) { + float dx_clamped = dx; + if (my_old_col + dx_clamped < 0.1f) + dx_clamped = 0.1f - my_old_col; + if (adj_old_col - dx_clamped < 0.1f) + dx_clamped = adj_old_col - 0.1f; + + float new_my_col = my_old_col + dx_clamped; + float new_adj_col = adj_old_col - dx_clamped; + + // 处理被强行锁死在 1.0f 的列边界,头部是个错位窗口 + if (current_layout && current_layout->id == VERTICAL_FAIR) { + int32_t n_tiling = m->visible_tiling_clients; + int32_t l_rows; + for (l_rows = 0; l_rows <= n_tiling; l_rows++) { + if (l_rows * l_rows >= n_tiling) + break; + } + int32_t base_cols = n_tiling / l_rows; + // 当调节边界恰好处于非对称的锁死列(如 3 窗口下的 col 0 与 col + // 1 之间) + if ((grabc->grid_col_idx == base_cols - 1 && + adj_c_idx == base_cols) || + (grabc->grid_col_idx == base_cols && + adj_c_idx == base_cols - 1)) { + + float p_col = + (grabc->grid_col_idx == base_cols - 1) + ? (my_old_col + dx) / (my_old_col + adj_old_col) + : (adj_old_col - dx) / (my_old_col + adj_old_col); + if (p_col < 0.01f) + p_col = 0.01f; + if (p_col > 0.99f) + p_col = 0.99f; + + // 反推非线性真实权重值 + float new_r_var_per = p_col / (1.0f - p_col); + if (new_r_var_per < 0.1f) + new_r_var_per = 0.1f; + if (new_r_var_per > 10.0f) + new_r_var_per = 10.0f; + + if (grabc->grid_col_idx == base_cols - 1) { + new_my_col = new_r_var_per; + new_adj_col = 1.0f; + } else { + new_my_col = 1.0f; + new_adj_col = new_r_var_per; + } + } + } + + wl_list_for_each(c, &clients, link) { + if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) + continue; + if (c->grid_col_idx == grabc->grid_col_idx) + c->grid_col_per = new_my_col; + if (c->grid_col_idx == adj_c_idx) + c->grid_col_per = new_adj_col; + } + + wl_list_for_each(c, &clients, link) { + if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) + continue; + if (c->grid_row_idx == 0) { + if (c->grid_col_idx == grabc->grid_col_idx) + c->grid_col_per = new_my_col; + else if (c->grid_col_idx == adj_c_idx) + c->grid_col_per = new_adj_col; + } + } + } + + // 应用行高调节 + if (adj_old_row > 0.0f) { + float dy_clamped = dy; + if (my_old_row + dy_clamped < 0.1f) + dy_clamped = 0.1f - my_old_row; + if (adj_old_row - dy_clamped < 0.1f) + dy_clamped = adj_old_row - 0.1f; + + float new_my_row = my_old_row + dy_clamped; + float new_adj_row = adj_old_row - dy_clamped; + + // 处理被强行锁死在 1.0f 的行边界,头部是个错位窗口 + if (current_layout && current_layout->id == FAIR) { + int32_t n_tiling = m->visible_tiling_clients; + int32_t l_cols; + for (l_cols = 0; l_cols <= n_tiling; l_cols++) { + if (l_cols * l_cols >= n_tiling) + break; + } + int32_t base_rows = n_tiling / l_cols; + // 当调节边界恰好处于非对称的锁死行(如 3 窗口下的 row 0 与 row + // 1 之间) + if ((grabc->grid_row_idx == base_rows - 1 && + adj_r_idx == base_rows) || + (grabc->grid_row_idx == base_rows && + adj_r_idx == base_rows - 1)) { + + float p_row = + (grabc->grid_row_idx == base_rows - 1) + ? (my_old_row + dy) / (my_old_row + adj_old_row) + : (adj_old_row - dy) / (my_old_row + adj_old_row); + if (p_row < 0.01f) + p_row = 0.01f; + if (p_row > 0.99f) + p_row = 0.99f; + + // 反推非线性真实权重值 + float new_r_var_per = p_row / (1.0f - p_row); + if (new_r_var_per < 0.1f) + new_r_var_per = 0.1f; + if (new_r_var_per > 10.0f) + new_r_var_per = 10.0f; + + if (grabc->grid_row_idx == base_rows - 1) { + new_my_row = new_r_var_per; + new_adj_row = 1.0f; + } else { + new_my_row = 1.0f; + new_adj_row = new_r_var_per; + } + } + } + + wl_list_for_each(c, &clients, link) { + if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) + continue; + if (c->grid_row_idx == grabc->grid_row_idx) + c->grid_row_per = new_my_row; + if (c->grid_row_idx == adj_r_idx) + c->grid_row_per = new_adj_row; + } + + wl_list_for_each(c, &clients, link) { + if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) + continue; + if (c->grid_col_idx == 0) { + if (c->grid_row_idx == grabc->grid_row_idx) + c->grid_row_per = new_my_row; + else if (c->grid_row_idx == adj_r_idx) + c->grid_row_per = new_adj_row; + } + } + } + + if (!isdrag) { + arrange(m, false, false); + return; + } + + if (last_apply_drap_time == 0 || + time - last_apply_drap_time > config.drag_tile_refresh_interval) { + arrange(m, false, false); + last_apply_drap_time = time; + } + } +} + void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, int32_t offsety, uint32_t time, bool isvertical) { if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen) @@ -772,6 +1068,11 @@ void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx, resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, true); } else if (current_layout->id == DWINDLE) { resize_tile_dwindle(grabc, isdrag, offsetx, offsety, time, true); + } else if (current_layout->id == GRID || + current_layout->id == VERTICAL_GRID || + current_layout->id == FAIR || + current_layout->id == VERTICAL_FAIR) { + resize_tile_grid_fair(grabc, isdrag, offsetx, offsety, time); } } diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index 130d38f8..ee37b168 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -1,5 +1,6 @@ + // 网格布局窗口大小和位置计算 -void grid(Monitor *m) { +void overview(Monitor *m) { int32_t i, n; int32_t cx, cy, cw, ch; int32_t dx; @@ -676,6 +677,195 @@ monocle(Monitor *m) { wlr_scene_node_raise_to_top(&c->scene->node); } +// 网格布局窗口大小和位置计算 +void grid(Monitor *m) { + int32_t i, n; + int32_t cw, ch; + int32_t cols, rows, overcols; + Client *c = NULL; + n = 0; + int32_t target_gappo = + enablegaps ? m->isoverview ? config.overviewgappo : config.gappoh : 0; + int32_t target_gappi = + enablegaps ? m->isoverview ? config.overviewgappi : config.gappih : 0; + float single_width_ratio = m->isoverview ? 0.7 : 0.9; + float single_height_ratio = m->isoverview ? 0.8 : 0.9; + + n = m->isoverview ? m->visible_clients : m->visible_tiling_clients; + + if (n == 0) + return; + + if (n == 1) { + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + cw = (m->w.width - 2 * target_gappo) * single_width_ratio; + ch = (m->w.height - 2 * target_gappo) * single_height_ratio; + c->geom.x = m->w.x + (m->w.width - cw) / 2; + c->geom.y = m->w.y + (m->w.height - ch) / 2; + c->geom.width = cw; + c->geom.height = ch; + resize(c, c->geom, 0); + return; + } + } + } + + if (n == 2) { + float col_pers[2] = {1.0f, 1.0f}; + // 先提取这两个窗口现有的列比例 + i = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + if (i < 2) + col_pers[i] = + (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f; + i++; + } + } + + float sum_col = col_pers[0] + col_pers[1]; + float avail_w = m->w.width - 2 * target_gappo - target_gappi; + ch = + (m->w.height - 2 * target_gappo) * 0.65; // 依然保持 0.65 的美观高度 + + i = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + c->grid_col_idx = i; + c->grid_row_idx = 0; + c->grid_col_per = col_pers[i]; + c->grid_row_per = 1.0f; + + // 根据分配的权重动态计算当前窗口的宽度 + cw = avail_w * (col_pers[i] / sum_col); + + if (i == 0) { + c->geom.x = m->w.x + target_gappo; + } else if (i == 1) { + // 第二个窗口的 X 坐标紧跟第一个窗口后面 + float cw0 = avail_w * (col_pers[0] / sum_col); + c->geom.x = m->w.x + target_gappo + cw0 + target_gappi; + } + c->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo; + c->geom.width = cw; + c->geom.height = ch; + resize(c, c->geom, 0); + i++; + } + } + return; + } + + // 计算列数和行数 + for (cols = 0; cols <= n / 2; cols++) { + if (cols * cols >= n) + break; + } + rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols; + overcols = n % cols; + + float col_pers[cols]; + float row_pers[rows]; + for (i = 0; i < cols; i++) + col_pers[i] = 1.0f; + for (i = 0; i < rows; i++) + row_pers[i] = 1.0f; + + // 提取首个窗口比例 + i = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + int32_t c_idx = i % cols; + int32_t r_idx = i / cols; + if (r_idx == 0) + col_pers[c_idx] = + (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f; + if (c_idx == 0) + row_pers[r_idx] = + (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f; + i++; + } + } + + float sum_col = 0.0f, sum_row = 0.0f; + for (i = 0; i < cols; i++) + sum_col += col_pers[i]; + for (i = 0; i < rows; i++) + sum_row += row_pers[i]; + + float avail_w = m->w.width - 2 * target_gappo - (cols - 1) * target_gappi; + float avail_h = m->w.height - 2 * target_gappo - (rows - 1) * target_gappi; + + // 分配位置与尺寸 + i = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + int32_t c_idx = i % cols; + int32_t r_idx = i / cols; + + // 矫正属性及标记索引 + c->grid_col_per = col_pers[c_idx]; + c->grid_row_per = row_pers[r_idx]; + c->grid_col_idx = c_idx; + c->grid_row_idx = r_idx; + + // X 坐标及宽度计算 + float fl_cx = m->w.x + target_gappo; + float fl_cw = 0.0f; + + if (overcols && i >= n - overcols) { + float over_w = 0.0f; + for (int j = 0; j < overcols; j++) + over_w += avail_w * (col_pers[j] / sum_col); + over_w += (overcols - 1) * target_gappi; + float dx = (m->w.width - over_w) / 2.0f - target_gappo; + + fl_cx += dx; + for (int j = 0; j < c_idx; j++) + fl_cx += avail_w * (col_pers[j] / sum_col) + target_gappi; + fl_cw = avail_w * (col_pers[c_idx] / sum_col); + } else { + for (int j = 0; j < c_idx; j++) + fl_cx += avail_w * (col_pers[j] / sum_col) + target_gappi; + fl_cw = (c_idx == cols - 1) + ? (m->w.x + m->w.width - target_gappo - fl_cx) + : avail_w * (col_pers[c_idx] / sum_col); + } + + // Y 坐标及高度计算 + float fl_cy = m->w.y + target_gappo; + for (int j = 0; j < r_idx; j++) + fl_cy += avail_h * (row_pers[j] / sum_row) + target_gappi; + float fl_ch = (r_idx == rows - 1) + ? (m->w.y + m->w.height - target_gappo - fl_cy) + : avail_h * (row_pers[r_idx] / sum_row); + + c->geom.x = (int32_t)fl_cx; + c->geom.y = (int32_t)fl_cy; + c->geom.width = (int32_t)fl_cw; + c->geom.height = (int32_t)fl_ch; + resize(c, c->geom, 0); + i++; + } + } +} + void fair(Monitor *m) { int32_t i, n = 0; Client *c = NULL; @@ -684,35 +874,62 @@ void fair(Monitor *m) { if (n == 0) return; - // 间隙参数处理 int32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; - // 智能间隙 cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv; cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih; cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov; cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh; - // 计算最佳列数 cols = ceil(sqrt(n)) int32_t cols; for (cols = 0; cols <= n; cols++) { if (cols * cols >= n) break; } - int32_t base_rows = n / cols; // 每列的基础行数 - int32_t remainder = n % cols; // 多出来的窗口 - - // 计算前半部分(大窗口)的列数和总窗口数 + int32_t base_rows = n / cols; + int32_t remainder = n % cols; int32_t first_group_cols = cols - remainder; int32_t first_group_count = first_group_cols * base_rows; + int32_t max_rows = base_rows + (remainder > 0 ? 1 : 0); - // 计算标准列宽 - int32_t col_width = - (m->w.width - 2 * cur_gappoh - (cols - 1) * cur_gappih) / cols; + float col_pers[cols]; + float row_pers[max_rows]; + for (i = 0; i < cols; i++) + col_pers[i] = 1.0f; + for (i = 0; i < max_rows; i++) + row_pers[i] = 1.0f; + + i = 0; + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || !ISTILED(c)) + continue; + int32_t col_idx, row_idx; + if (i < first_group_count) { + col_idx = i / base_rows; + row_idx = i % base_rows; + } else { + int32_t offset = i - first_group_count; + col_idx = first_group_cols + (offset / (base_rows + 1)); + row_idx = offset % (base_rows + 1); + } + + if (row_idx == 0) + col_pers[col_idx] = + (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f; + if (col_idx == 0) + row_pers[row_idx] = + (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f; + i++; + } + + float sum_col = 0.0f; + for (i = 0; i < cols; i++) + sum_col += col_pers[i]; + float avail_w = m->w.width - 2 * cur_gappoh - (cols - 1) * cur_gappih; i = 0; wl_list_for_each(c, &clients, link) { @@ -720,9 +937,6 @@ void fair(Monitor *m) { continue; int32_t col_idx, row_idx, rows_in_this_col; - - // 判断当前窗口属于哪一列、哪一行 - // 前半部分列拥有较少的行数(窗口更大),后半部分列承担余数(窗口更小) if (i < first_group_count) { col_idx = i / base_rows; row_idx = i % base_rows; @@ -734,25 +948,37 @@ void fair(Monitor *m) { rows_in_this_col = base_rows + 1; } - // 计算 X 坐标和宽度 (最后一列吃掉剩余像素,防止缝隙) - int32_t cx = m->w.x + cur_gappoh + col_idx * (col_width + cur_gappih); - int32_t cw = (col_idx == cols - 1) - ? (m->w.width - 2 * cur_gappoh - - col_idx * (col_width + cur_gappih)) - : col_width; + c->grid_col_per = col_pers[col_idx]; + c->grid_row_per = row_pers[row_idx]; + c->grid_col_idx = col_idx; + c->grid_row_idx = row_idx; - // 计算 Y 坐标和高度 (最后一行吃掉剩余像素) - int32_t base_ch = (m->w.height - 2 * cur_gappov - - (rows_in_this_col - 1) * cur_gappiv) / - rows_in_this_col; - int32_t cy = m->w.y + cur_gappov + row_idx * (base_ch + cur_gappiv); - int32_t ch = (row_idx == rows_in_this_col - 1) - ? (m->w.height - 2 * cur_gappov - - row_idx * (base_ch + cur_gappiv)) - : base_ch; + float fl_cx = m->w.x + cur_gappoh; + for (int j = 0; j < col_idx; j++) + fl_cx += avail_w * (col_pers[j] / sum_col) + cur_gappih; + float fl_cw = (col_idx == cols - 1) + ? (m->w.x + m->w.width - cur_gappoh - fl_cx) + : avail_w * (col_pers[col_idx] / sum_col); - resize(c, (struct wlr_box){.x = cx, .y = cy, .width = cw, .height = ch}, + float sum_row_this_col = 0.0f; + for (int j = 0; j < rows_in_this_col; j++) + sum_row_this_col += row_pers[j]; + + float avail_h = + m->w.height - 2 * cur_gappov - (rows_in_this_col - 1) * cur_gappiv; + float fl_cy = m->w.y + cur_gappov; + for (int j = 0; j < row_idx; j++) + fl_cy += avail_h * (row_pers[j] / sum_row_this_col) + cur_gappiv; + float fl_ch = (row_idx == rows_in_this_col - 1) + ? (m->w.y + m->w.height - cur_gappov - fl_cy) + : avail_h * (row_pers[row_idx] / sum_row_this_col); + + resize(c, + (struct wlr_box){.x = (int32_t)fl_cx, + .y = (int32_t)fl_cy, + .width = (int32_t)fl_cw, + .height = (int32_t)fl_ch}, 0); i++; } -} \ No newline at end of file +} diff --git a/src/layout/vertical.h b/src/layout/vertical.h index 74ed98a4..f9c19690 100644 --- a/src/layout/vertical.h +++ b/src/layout/vertical.h @@ -178,8 +178,7 @@ void vertical_deck(Monitor *m) { void vertical_grid(Monitor *m) { int32_t i, n; - int32_t cx, cy, cw, ch; - int32_t dy; + int32_t cw, ch; int32_t rows, cols, overrows; Client *c = NULL; int32_t target_gappo = @@ -190,17 +189,13 @@ void vertical_grid(Monitor *m) { float single_height_ratio = m->isoverview ? 0.8 : 0.9; n = m->isoverview ? m->visible_clients : m->visible_tiling_clients; - - if (n == 0) { + if (n == 0) return; - } if (n == 1) { wl_list_for_each(c, &clients, link) { - if (c->mon != m) continue; - if (VISIBLEON(c, m) && !c->isunglobal && ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { ch = (m->w.height - 2 * target_gappo) * single_height_ratio; @@ -216,67 +211,143 @@ void vertical_grid(Monitor *m) { } if (n == 2) { - ch = (m->w.height - 2 * target_gappo - target_gappi) / 2; - cw = (m->w.width - 2 * target_gappo) * 0.65; + float row_pers[2] = {1.0f, 1.0f}; + // 先提取这两个窗口现有的行比例 i = 0; wl_list_for_each(c, &clients, link) { - if (c->mon != m) continue; - if (VISIBLEON(c, m) && !c->isunglobal && ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + if (i < 2) + row_pers[i] = + (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f; + i++; + } + } + + float sum_row = row_pers[0] + row_pers[1]; + float avail_h = m->w.height - 2 * target_gappo - target_gappi; + cw = (m->w.width - 2 * target_gappo) * 0.65; // 依然保持 0.65 的美观宽度 + + i = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + c->grid_col_idx = 0; + c->grid_row_idx = i; + c->grid_col_per = 1.0f; + c->grid_row_per = row_pers[i]; + + // 根据分配的权重动态计算当前窗口的高度 + ch = avail_h * (row_pers[i] / sum_row); + + c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo; if (i == 0) { - c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo; c->geom.y = m->w.y + target_gappo; - c->geom.width = cw; - c->geom.height = ch; - resize(c, c->geom, 0); } else if (i == 1) { - c->geom.x = m->w.x + (m->w.width - cw) / 2 + target_gappo; - c->geom.y = m->w.y + ch + target_gappo + target_gappi; - c->geom.width = cw; - c->geom.height = ch; - resize(c, c->geom, 0); + // 第二个窗口的 Y 坐标紧跟第一个窗口下面 + float ch0 = avail_h * (row_pers[0] / sum_row); + c->geom.y = m->w.y + target_gappo + ch0 + target_gappi; } + c->geom.width = cw; + c->geom.height = ch; + resize(c, c->geom, 0); i++; } } return; } - for (rows = 0; rows <= n / 2; rows++) { - if (rows * rows >= n) { + if (rows * rows >= n) break; - } } cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; - - cw = (m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols; - ch = (m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows; - overrows = n % rows; - if (overrows) { - dy = (m->w.height - overrows * ch - (overrows - 1) * target_gappi) / 2 - - target_gappo; - } + + float col_pers[cols]; + float row_pers[rows]; + for (i = 0; i < cols; i++) + col_pers[i] = 1.0f; + for (i = 0; i < rows; i++) + row_pers[i] = 1.0f; i = 0; wl_list_for_each(c, &clients, link) { if (c->mon != m) continue; - if (VISIBLEON(c, m) && !c->isunglobal && ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { - cx = m->w.x + (i / rows) * (cw + target_gappi); - cy = m->w.y + (i % rows) * (ch + target_gappi); + int32_t c_idx = i / rows; + int32_t r_idx = i % rows; + if (r_idx == 0) + col_pers[c_idx] = + (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f; + if (c_idx == 0) + row_pers[r_idx] = + (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f; + i++; + } + } + + float sum_col = 0.0f, sum_row = 0.0f; + for (i = 0; i < cols; i++) + sum_col += col_pers[i]; + for (i = 0; i < rows; i++) + sum_row += row_pers[i]; + + float avail_w = m->w.width - 2 * target_gappo - (cols - 1) * target_gappi; + float avail_h = m->w.height - 2 * target_gappo - (rows - 1) * target_gappi; + + i = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (VISIBLEON(c, m) && !c->isunglobal && + ((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) { + int32_t c_idx = i / rows; + int32_t r_idx = i % rows; + + c->grid_col_per = col_pers[c_idx]; + c->grid_row_per = row_pers[r_idx]; + c->grid_col_idx = c_idx; + c->grid_row_idx = r_idx; + + float fl_cy = m->w.y + target_gappo; + float fl_ch = 0.0f; + if (overrows && i >= n - overrows) { - cy += dy; + float over_h = 0.0f; + for (int j = 0; j < overrows; j++) + over_h += avail_h * (row_pers[j] / sum_row); + over_h += (overrows - 1) * target_gappi; + float dy = (m->w.height - over_h) / 2.0f - target_gappo; + + fl_cy += dy; + for (int j = 0; j < r_idx; j++) + fl_cy += avail_h * (row_pers[j] / sum_row) + target_gappi; + fl_ch = avail_h * (row_pers[r_idx] / sum_row); + } else { + for (int j = 0; j < r_idx; j++) + fl_cy += avail_h * (row_pers[j] / sum_row) + target_gappi; + fl_ch = (r_idx == rows - 1) + ? (m->w.y + m->w.height - target_gappo - fl_cy) + : avail_h * (row_pers[r_idx] / sum_row); } - c->geom.x = cx + target_gappo; - c->geom.y = cy + target_gappo; - c->geom.width = cw; - c->geom.height = ch; + + float fl_cx = m->w.x + target_gappo; + for (int j = 0; j < c_idx; j++) + fl_cx += avail_w * (col_pers[j] / sum_col) + target_gappi; + float fl_cw = (c_idx == cols - 1) + ? (m->w.x + m->w.width - target_gappo - fl_cx) + : avail_w * (col_pers[c_idx] / sum_col); + + c->geom.x = (int32_t)fl_cx; + c->geom.y = (int32_t)fl_cy; + c->geom.width = (int32_t)fl_cw; + c->geom.height = (int32_t)fl_ch; resize(c, c->geom, 0); i++; } @@ -291,35 +362,62 @@ void vertical_fair(Monitor *m) { if (n == 0) return; - // 间隙参数处理 int32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; - // 智能间隙 cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv; cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih; cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov; cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh; - // 计算最佳行数 rows = ceil(sqrt(n)) int32_t rows; for (rows = 0; rows <= n; rows++) { if (rows * rows >= n) break; } - int32_t base_cols = n / rows; // 每行的基础列数 - int32_t remainder = n % rows; // 多出来的窗口 - - // 计算上半部分(大窗口)的行数和总窗口数 + int32_t base_cols = n / rows; + int32_t remainder = n % rows; int32_t first_group_rows = rows - remainder; int32_t first_group_count = first_group_rows * base_cols; + int32_t max_cols = base_cols + (remainder > 0 ? 1 : 0); - // 计算标准行高 - int32_t row_height = - (m->w.height - 2 * cur_gappov - (rows - 1) * cur_gappiv) / rows; + float row_pers[rows]; + float col_pers[max_cols]; + for (i = 0; i < rows; i++) + row_pers[i] = 1.0f; + for (i = 0; i < max_cols; i++) + col_pers[i] = 1.0f; + + i = 0; + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || !ISTILED(c)) + continue; + int32_t row_idx, col_idx; + if (i < first_group_count) { + row_idx = i / base_cols; + col_idx = i % base_cols; + } else { + int32_t offset = i - first_group_count; + row_idx = first_group_rows + (offset / (base_cols + 1)); + col_idx = offset % (base_cols + 1); + } + + if (col_idx == 0) + row_pers[row_idx] = + (c->grid_row_per > 0.0f) ? c->grid_row_per : 1.0f; + if (row_idx == 0) + col_pers[col_idx] = + (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f; + i++; + } + + float sum_row = 0.0f; + for (i = 0; i < rows; i++) + sum_row += row_pers[i]; + float avail_h = m->w.height - 2 * cur_gappov - (rows - 1) * cur_gappiv; i = 0; wl_list_for_each(c, &clients, link) { @@ -327,9 +425,6 @@ void vertical_fair(Monitor *m) { continue; int32_t row_idx, col_idx, cols_in_this_row; - - // 判断当前窗口属于哪一行、哪一列 - // 上半部分行拥有较少的列数(窗口更宽),下半部分行承担余数(窗口更窄) if (i < first_group_count) { row_idx = i / base_cols; col_idx = i % base_cols; @@ -341,24 +436,36 @@ void vertical_fair(Monitor *m) { cols_in_this_row = base_cols + 1; } - // 计算 Y 坐标和高度 (最后一行吃掉剩余像素) - int32_t cy = m->w.y + cur_gappov + row_idx * (row_height + cur_gappiv); - int32_t ch = (row_idx == rows - 1) - ? (m->w.height - 2 * cur_gappov - - row_idx * (row_height + cur_gappiv)) - : row_height; + c->grid_row_per = row_pers[row_idx]; + c->grid_col_per = col_pers[col_idx]; + c->grid_row_idx = row_idx; + c->grid_col_idx = col_idx; - // 计算 X 坐标和宽度 (最后一列吃掉剩余像素,防止缝隙) - int32_t base_cw = (m->w.width - 2 * cur_gappoh - - (cols_in_this_row - 1) * cur_gappih) / - cols_in_this_row; - int32_t cx = m->w.x + cur_gappoh + col_idx * (base_cw + cur_gappih); - int32_t cw = (col_idx == cols_in_this_row - 1) - ? (m->w.width - 2 * cur_gappoh - - col_idx * (base_cw + cur_gappih)) - : base_cw; + float fl_cy = m->w.y + cur_gappov; + for (int j = 0; j < row_idx; j++) + fl_cy += avail_h * (row_pers[j] / sum_row) + cur_gappiv; + float fl_ch = (row_idx == rows - 1) + ? (m->w.y + m->w.height - cur_gappov - fl_cy) + : avail_h * (row_pers[row_idx] / sum_row); - resize(c, (struct wlr_box){.x = cx, .y = cy, .width = cw, .height = ch}, + float sum_col_this_row = 0.0f; + for (int j = 0; j < cols_in_this_row; j++) + sum_col_this_row += col_pers[j]; + + float avail_w = + m->w.width - 2 * cur_gappoh - (cols_in_this_row - 1) * cur_gappih; + float fl_cx = m->w.x + cur_gappoh; + for (int j = 0; j < col_idx; j++) + fl_cx += avail_w * (col_pers[j] / sum_col_this_row) + cur_gappih; + float fl_cw = (col_idx == cols_in_this_row - 1) + ? (m->w.x + m->w.width - cur_gappoh - fl_cx) + : avail_w * (col_pers[col_idx] / sum_col_this_row); + + resize(c, + (struct wlr_box){.x = (int32_t)fl_cx, + .y = (int32_t)fl_cy, + .width = (int32_t)fl_cw, + .height = (int32_t)fl_ch}, 0); i++; } diff --git a/src/mango.c b/src/mango.c index 05da99a5..bc6b4c66 100644 --- a/src/mango.c +++ b/src/mango.c @@ -427,6 +427,12 @@ struct Client { bool enable_drop_area_draw; int32_t drop_direction; struct wlr_box drag_tile_float_backup_geom; + float grid_col_per; + float grid_row_per; + float old_grid_col_per; + float old_grid_row_per; + int32_t grid_col_idx; + int32_t grid_row_idx; }; typedef struct { @@ -4270,6 +4276,8 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx, } void init_client_properties(Client *c) { + c->grid_col_per = 1.0f; + c->grid_row_per = 1.0f; c->drop_direction = UNDIR; c->enable_drop_area_draw = false; c->isfocusing = false; @@ -6207,8 +6215,6 @@ void tag_client(const Arg *arg, Client *target_client) { printstatus(); } -void overview(Monitor *m) { grid(m); } - // 目标窗口有其他窗口和它同个tag就返回0 uint32_t want_restore_fullscreen(Client *target_client) { Client *c = NULL;