refactor: change fair/fairv to use a 2 level tree

the idea is that we can step once through the tree to get the layout
(dfs) and since this makes it declarative resize can simply mutate the
tree and the arranger will handle the rest.

the tree looks a bit like:

FairState (root)
├── cols[0]  { ratio: col_width_weight }
│   ├── row_ratios[0]  row_height_weight
│   ├── row_ratios[1]  row_height_weight
│   └── row_ratios[2]  row_height_weight
├── cols[1]  { ratio: col_width_weight }
│   ├── row_ratios[0]  row_height_weight
│   └── row_ratios[1]  row_height_weight
└── cols[2]  { ratio: col_width_weight }
    └── row_ratios[0]  row_height_weight
This commit is contained in:
DuckTapeMan35 2026-05-16 19:50:52 +01:00
parent 65c9ac6dd2
commit b56cab98da
4 changed files with 396 additions and 480 deletions

View file

@ -536,19 +536,38 @@ void resize_tile_dwindle(Client *grabc, bool isdrag, int32_t offsetx,
} }
} }
void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx, static struct FairState *ensure_fair_state(Monitor *m, uint32_t tag) {
int32_t offsety, uint32_t time) { if (!m->pertag->fair_state[tag]) {
m->pertag->fair_state[tag] = calloc(1, sizeof(struct FairState));
}
return m->pertag->fair_state[tag];
}
static void fair_state_adapt(struct FairState *fs, int cols, int base_rows, int remainder) {
for (int c = 0; c < cols; c++) {
if (fs->cols[c].ratio <= 0.0f) fs->cols[c].ratio = 1.0f;
int rows = (c < cols - remainder) ? base_rows : base_rows + 1;
for (int r = 0; r < rows; r++)
if (fs->cols[c].row_ratios[r] <= 0.0f)
fs->cols[c].row_ratios[r] = 1.0f;
}
fs->n_cols = cols;
}
void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx, int32_t offsety, uint32_t time) {
if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen) if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen)
return; return;
Monitor *m = grabc->mon; Monitor *m = grabc->mon;
if (m->isoverview) if (m->isoverview)
return; return;
if (m->visible_tiling_clients <= 1) if (m->visible_tiling_clients <= 1)
return; return;
// 获取当前布局 ID
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag]; const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
bool is_vfair = current_layout->id == VERTICAL_FAIR;
uint32_t tag = m->pertag->curtag;
struct FairState *fs = ensure_fair_state(m, tag);
if (!start_drag_window && isdrag) { if (!start_drag_window && isdrag) {
drag_begin_cursorx = cursor->x; drag_begin_cursorx = cursor->x;
@ -557,19 +576,14 @@ void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
Client *c; Client *c;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
c->old_grid_col_per = c->old_grid_col_per = (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
(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;
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_col_per = grabc->grid_col_per;
grabc->old_grid_row_per = grabc->grid_row_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_left_half = grabc->cursor_in_upper_half = cursor->y < grabc->geom.y + grabc->geom.height / 2;
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; grabc->drag_begin_geom = grabc->geom;
} else { } else {
if (isdrag) { if (isdrag) {
@ -579,80 +593,37 @@ void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
grabc->drag_begin_geom = grabc->geom; grabc->drag_begin_geom = grabc->geom;
Client *c; Client *c;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
c->old_grid_col_per = c->old_grid_col_per = (c->grid_col_per > 0.0f) ? c->grid_col_per : 1.0f;
(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;
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_upper_half = false;
grabc->cursor_in_left_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_x = (float)offsetx * grabc->old_grid_col_per / float delta_y = (float)offsety * grabc->old_grid_row_per / grabc->drag_begin_geom.height;
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_c_idx = grabc->grid_col_idx + 1;
int adj_r_idx = grabc->grid_row_idx; int adj_r_idx = grabc->grid_row_idx + 1;
float sign_x = 1.0f, sign_y = 1.0f; float sign_x = 1.0f, sign_y = 1.0f;
if (isdrag) { int max_col = -1, max_row = -1, min_col = INT32_MAX, min_row = INT32_MAX;
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; Client *tmp;
wl_list_for_each(tmp, &clients, link) { wl_list_for_each(tmp, &clients, link) {
if (tmp->mon != m || !VISIBLEON(tmp, m) || !ISTILED(tmp)) if (tmp->mon != m || !VISIBLEON(tmp, m) || !ISTILED(tmp)) continue;
continue; if (tmp->grid_col_idx > max_col) max_col = tmp->grid_col_idx;
if (tmp->grid_col_idx > max_col) if (tmp->grid_row_idx > max_row) max_row = tmp->grid_row_idx;
max_col = tmp->grid_col_idx; if (tmp->grid_col_idx < min_col) min_col = tmp->grid_col_idx;
if (tmp->grid_row_idx > max_row) if (tmp->grid_row_idx < min_row) min_row = tmp->grid_row_idx;
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_c_idx = grabc->grid_col_idx + 1; sign_x = 1.0f;
adj_r_idx = grabc->grid_row_idx + 1; adj_r_idx = grabc->grid_row_idx + 1; sign_y = 1.0f;
sign_x = 1.0f;
sign_y = 1.0f;
if (grabc->grid_col_idx == max_col) { if (grabc->grid_col_idx == max_col) { adj_c_idx = grabc->grid_col_idx - 1; sign_x = -1.0f; }
adj_c_idx = grabc->grid_col_idx - 1; if (grabc->grid_row_idx == max_row) { adj_r_idx = grabc->grid_row_idx - 1; sign_y = -1.0f; }
sign_x = -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; }
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 dx = delta_x * sign_x;
float dy = delta_y * sign_y; float dy = delta_y * sign_y;
@ -663,159 +634,121 @@ void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
Client *c; Client *c;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) continue;
continue; if (c->grid_col_idx == adj_c_idx && adj_old_col < 0) adj_old_col = c->old_grid_col_per;
if (c->grid_col_idx == adj_c_idx && adj_old_col < 0) if (c->grid_row_idx == adj_r_idx && adj_old_row < 0) adj_old_row = c->old_grid_row_per;
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;
} }
// 应用列宽调节 int col = grabc->grid_col_idx;
int row = grabc->grid_row_idx;
// Apply column width adjustment
if (adj_old_col > 0.0f) { if (adj_old_col > 0.0f) {
float dx_clamped = dx; float dx_clamped = dx;
if (my_old_col + dx_clamped < 0.1f) if (my_old_col + dx_clamped < 0.1f) dx_clamped = 0.1f - my_old_col;
dx_clamped = 0.1f - my_old_col; if (adj_old_col - dx_clamped < 0.1f) dx_clamped = adj_old_col - 0.1f;
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_my_col = my_old_col + dx_clamped;
float new_adj_col = adj_old_col - dx_clamped; float new_adj_col = adj_old_col - dx_clamped;
// 处理被强行锁死在 1.0f 的列边界,头部是个错位窗口 // Handle asymmetric boundary
if (current_layout && current_layout->id == VERTICAL_FAIR) {
int32_t n_tiling = m->visible_tiling_clients; int32_t n_tiling = m->visible_tiling_clients;
int32_t l_rows; int32_t l_cols;
for (l_rows = 0; l_rows <= n_tiling; l_rows++) { for (l_cols = 0; l_cols <= n_tiling; l_cols++)
if (l_rows * l_rows >= n_tiling) if (l_cols * l_cols >= n_tiling) break;
break; int32_t first_group_cols = l_cols - (n_tiling % l_cols);
}
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 = if (is_vfair) {
(grabc->grid_col_idx == base_cols - 1) // in vfair, col boundary asymmetry is within a row
// base_cols = n/rows, first_group_rows = rows - remainder
int32_t l_rows;
for (l_rows = 0; l_rows <= n_tiling; l_rows++)
if (l_rows * l_rows >= n_tiling) break;
int32_t vbase_cols = n_tiling / l_rows;
int32_t vfirst_group_rows = l_rows - (n_tiling % l_rows);
// cols_in_this_row depends on which row we're in
int32_t cols_in_row = (row < vfirst_group_rows) ? vbase_cols : vbase_cols + 1;
if ((col == cols_in_row - 2 && adj_c_idx == cols_in_row - 1) ||
(col == cols_in_row - 1 && adj_c_idx == cols_in_row - 2)) {
// last col boundary — no asymmetry issue in vfair cols
}
} else {
if ((col == first_group_cols - 1 && adj_c_idx == first_group_cols) ||
(col == first_group_cols && adj_c_idx == first_group_cols - 1)) {
float p_col = (col == first_group_cols - 1)
? (my_old_col + dx) / (my_old_col + adj_old_col) ? (my_old_col + dx) / (my_old_col + adj_old_col)
: (adj_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 = fmaxf(0.01f, fminf(0.99f, p_col));
p_col = 0.01f; float new_r = p_col / (1.0f - p_col);
if (p_col > 0.99f) new_r = fmaxf(0.1f, fminf(10.0f, new_r));
p_col = 0.99f; if (col == first_group_cols - 1) { new_my_col = new_r; new_adj_col = 1.0f; }
else { new_my_col = 1.0f; new_adj_col = new_r; }
}
}
// 反推非线性真实权重值 // Mutate FairState
float new_r_var_per = p_col / (1.0f - p_col); if (is_vfair) {
if (new_r_var_per < 0.1f) fs->cols[row].row_ratios[col] = new_my_col;
new_r_var_per = 0.1f; fs->cols[row].row_ratios[adj_c_idx] = new_adj_col;
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 { } else {
new_my_col = 1.0f; fs->cols[col].ratio = new_my_col;
new_adj_col = new_r_var_per; fs->cols[adj_c_idx].ratio = new_adj_col;
}
} }
} }
wl_list_for_each(c, &clients, link) { // Apply row height adjustment
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) { if (adj_old_row > 0.0f) {
float dy_clamped = dy; float dy_clamped = dy;
if (my_old_row + dy_clamped < 0.1f) if (my_old_row + dy_clamped < 0.1f) dy_clamped = 0.1f - my_old_row;
dy_clamped = 0.1f - my_old_row; if (adj_old_row - dy_clamped < 0.1f) dy_clamped = adj_old_row - 0.1f;
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_my_row = my_old_row + dy_clamped;
float new_adj_row = adj_old_row - dy_clamped; float new_adj_row = adj_old_row - dy_clamped;
// 处理被强行锁死在 1.0f 的行边界,头部是个错位窗口 // Handle asymmetric boundary
if (current_layout && current_layout->id == FAIR) {
int32_t n_tiling = m->visible_tiling_clients; int32_t n_tiling = m->visible_tiling_clients;
int32_t l_cols; int32_t l_cols;
for (l_cols = 0; l_cols <= n_tiling; l_cols++) { for (l_cols = 0; l_cols <= n_tiling; l_cols++)
if (l_cols * l_cols >= n_tiling) if (l_cols * l_cols >= n_tiling) break;
break;
}
int32_t base_rows = n_tiling / l_cols; 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 = if (is_vfair) {
(grabc->grid_row_idx == base_rows - 1) int32_t l_rows;
for (l_rows = 0; l_rows <= n_tiling; l_rows++)
if (l_rows * l_rows >= n_tiling) break;
int32_t vfirst_group_rows = l_rows - (n_tiling % l_rows);
if ((row == vfirst_group_rows - 1 && adj_r_idx == vfirst_group_rows) ||
(row == vfirst_group_rows && adj_r_idx == vfirst_group_rows - 1)) {
float p_row = (row == vfirst_group_rows - 1)
? (my_old_row + dy) / (my_old_row + adj_old_row) ? (my_old_row + dy) / (my_old_row + adj_old_row)
: (adj_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 = fmaxf(0.01f, fminf(0.99f, p_row));
p_row = 0.01f; float new_r = p_row / (1.0f - p_row);
if (p_row > 0.99f) new_r = fmaxf(0.1f, fminf(10.0f, new_r));
p_row = 0.99f; if (row == vfirst_group_rows - 1) { new_my_row = new_r; new_adj_row = 1.0f; }
else { new_my_row = 1.0f; new_adj_row = new_r; }
// 反推非线性真实权重值 }
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 { } else {
new_my_row = 1.0f; if ((row == base_rows - 1 && adj_r_idx == base_rows) ||
new_adj_row = new_r_var_per; (row == base_rows && adj_r_idx == base_rows - 1)) {
} float p_row = (row == base_rows - 1)
? (my_old_row + dy) / (my_old_row + adj_old_row)
: (adj_old_row - dy) / (my_old_row + adj_old_row);
p_row = fmaxf(0.01f, fminf(0.99f, p_row));
float new_r = p_row / (1.0f - p_row);
new_r = fmaxf(0.1f, fminf(10.0f, new_r));
if (row == base_rows - 1) { new_my_row = new_r; new_adj_row = 1.0f; }
else { new_my_row = 1.0f; new_adj_row = new_r; }
} }
} }
wl_list_for_each(c, &clients, link) { // Mutate FairState
if (c->mon != m || !VISIBLEON(c, m) || !ISTILED(c)) if (is_vfair) {
continue; fs->cols[row].ratio = new_my_row;
if (c->grid_row_idx == grabc->grid_row_idx) fs->cols[adj_r_idx].ratio = new_adj_row;
c->grid_row_per = new_my_row; } else {
if (c->grid_row_idx == adj_r_idx) fs->cols[col].row_ratios[row] = new_my_row;
c->grid_row_per = new_adj_row; fs->cols[col].row_ratios[adj_r_idx] = 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;
}
} }
} }

View file

@ -866,119 +866,100 @@ void grid(Monitor *m) {
} }
} }
void fair(Monitor *m) { void cleanup_monitor_fair(Monitor *m) {
int32_t i, n = 0; for (int i = 0; i <= LENGTH(tags); i++) {
Client *c = NULL; free(m->pertag->fair_state[i]);
m->pertag->fair_state[i] = NULL;
}
}
n = m->visible_tiling_clients; void fair(Monitor *m) {
if (n == 0) int32_t n = m->visible_tiling_clients;
return; if (n == 0) return;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv; cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv;
cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih; cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih;
cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov; cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov;
cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh; cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh;
int32_t cols; int32_t cols;
for (cols = 0; cols <= n; cols++) { for (cols = 0; cols <= n; cols++)
if (cols * cols >= n) if (cols * cols >= n) break;
break;
}
int32_t base_rows = n / cols; int32_t base_rows = n / cols;
int32_t remainder = n % cols; int32_t remainder = n % cols;
int32_t first_group_cols = cols - remainder; int32_t first_group_cols = cols - remainder;
int32_t first_group_count = first_group_cols * base_rows; int32_t first_group_count = first_group_cols * base_rows;
int32_t max_rows = base_rows + (remainder > 0 ? 1 : 0);
float col_pers[cols]; uint32_t tag = m->pertag->curtag;
float row_pers[max_rows]; struct FairState *fs = ensure_fair_state(m, tag);
for (i = 0; i < cols; i++) fair_state_adapt(fs, cols, base_rows, remainder);
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; float avail_w = m->w.width - 2*cur_gappoh - (cols-1)*cur_gappih;
float sum_col = 0.0f;
for (int c = 0; c < cols; c++) sum_col += fs->cols[c].ratio;
int i = 0;
int prev_col = -1;
float col_x = 0, row_y = 0, avail_h = 0, sum_row = 0;
int rows_in_col = 0;
Client *c;
i = 0;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) if (!VISIBLEON(c, m) || !ISTILED(c)) continue;
continue;
int32_t col_idx, row_idx, rows_in_this_col; int col, row;
if (i < first_group_count) { if (i < first_group_count) {
col_idx = i / base_rows; col = i / base_rows;
row_idx = i % base_rows; row = i % base_rows;
rows_in_this_col = base_rows; rows_in_col = base_rows;
} else { } else {
int32_t offset = i - first_group_count; int off = i - first_group_count;
col_idx = first_group_cols + (offset / (base_rows + 1)); col = first_group_cols + off / (base_rows + 1);
row_idx = offset % (base_rows + 1); row = off % (base_rows + 1);
rows_in_this_col = base_rows + 1; rows_in_col = base_rows + 1;
} }
c->grid_col_per = col_pers[col_idx]; if (col != prev_col) {
c->grid_row_per = row_pers[row_idx]; col_x = m->w.x + cur_gappoh;
c->grid_col_idx = col_idx; for (int pc = 0; pc < col; pc++)
c->grid_row_idx = row_idx; col_x += avail_w * (fs->cols[pc].ratio / sum_col) + cur_gappih;
float fl_cx = m->w.x + cur_gappoh; struct FairColNode *cn = &fs->cols[col];
for (int j = 0; j < col_idx; j++) sum_row = 0.0f;
fl_cx += avail_w * (col_pers[j] / sum_col) + cur_gappih; avail_h = m->w.height - 2*cur_gappov - (rows_in_col-1)*cur_gappiv;
float fl_cw = (col_idx == cols - 1) for (int r = 0; r < rows_in_col; r++) sum_row += cn->row_ratios[r];
? (m->w.x + m->w.width - cur_gappoh - fl_cx) row_y = m->w.y + cur_gappov;
: avail_w * (col_pers[col_idx] / sum_col); prev_col = col;
}
float sum_row_this_col = 0.0f; struct FairColNode *cn = &fs->cols[col];
for (int j = 0; j < rows_in_this_col; j++)
sum_row_this_col += row_pers[j];
float avail_h = float col_w = (col == cols - 1)
m->w.height - 2 * cur_gappov - (rows_in_this_col - 1) * cur_gappiv; ? (m->w.x + m->w.width - cur_gappoh - col_x)
float fl_cy = m->w.y + cur_gappov; : avail_w * (cn->ratio / sum_col);
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, float row_h = (row == rows_in_col - 1)
(struct wlr_box){.x = (int32_t)fl_cx, ? (m->w.y + m->w.height - cur_gappov - row_y)
.y = (int32_t)fl_cy, : avail_h * (cn->row_ratios[row] / sum_row);
.width = (int32_t)fl_cw,
.height = (int32_t)fl_ch}, c->grid_col_idx = col;
0); c->grid_row_idx = row;
c->grid_col_per = cn->ratio;
c->grid_row_per = cn->row_ratios[row];
resize(c, (struct wlr_box){
.x = (int32_t)col_x,
.y = (int32_t)row_y,
.width = (int32_t)col_w,
.height = (int32_t)row_h,
}, 0);
row_y += row_h + cur_gappiv;
i++; i++;
} }
} }

View file

@ -355,118 +355,105 @@ void vertical_grid(Monitor *m) {
} }
void vertical_fair(Monitor *m) { void vertical_fair(Monitor *m) {
int32_t i, n = 0; int32_t n = m->visible_tiling_clients;
Client *c = NULL; if (n == 0) return;
n = m->visible_tiling_clients;
if (n == 0)
return;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv; cur_gappiv = config.smartgaps && n == 1 ? 0 : cur_gappiv;
cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih; cur_gappih = config.smartgaps && n == 1 ? 0 : cur_gappih;
cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov; cur_gappov = config.smartgaps && n == 1 ? 0 : cur_gappov;
cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh; cur_gappoh = config.smartgaps && n == 1 ? 0 : cur_gappoh;
int32_t rows; int32_t rows;
for (rows = 0; rows <= n; rows++) { for (rows = 0; rows <= n; rows++)
if (rows * rows >= n) if (rows * rows >= n) break;
break;
}
int32_t base_cols = n / rows; int32_t base_cols = n / rows;
int32_t remainder = n % rows; int32_t remainder = n % rows;
int32_t first_group_rows = rows - remainder; int32_t first_group_rows = rows - remainder;
int32_t first_group_count = first_group_rows * base_cols; int32_t first_group_count = first_group_rows * base_cols;
int32_t max_cols = base_cols + (remainder > 0 ? 1 : 0);
float row_pers[rows]; uint32_t tag = m->pertag->curtag;
float col_pers[max_cols]; struct FairState *fs = ensure_fair_state(m, tag);
for (i = 0; i < rows; i++) // adapt with transposed semantics: rows are top-level
row_pers[i] = 1.0f; if (fs->n_cols != rows) {
for (i = 0; i < max_cols; i++) for (int r = fs->n_cols; r < rows; r++) {
col_pers[i] = 1.0f; if (fs->cols[r].ratio <= 0.0f) fs->cols[r].ratio = 1.0f;
int cols_in_row = (r < first_group_rows) ? base_cols : base_cols + 1;
i = 0; for (int c = 0; c < cols_in_row; c++)
wl_list_for_each(c, &clients, link) { if (fs->cols[r].row_ratios[c] <= 0.0f)
if (!VISIBLEON(c, m) || !ISTILED(c)) fs->cols[r].row_ratios[c] = 1.0f;
continue; }
int32_t row_idx, col_idx; fs->n_cols = rows;
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; float avail_h = m->w.height - 2*cur_gappov - (rows-1)*cur_gappiv;
float sum_row = 0.0f;
for (int r = 0; r < rows; r++) sum_row += fs->cols[r].ratio;
int i = 0;
int prev_row = -1;
float row_y = 0, col_x = 0, avail_w = 0, sum_col = 0;
int cols_in_row = 0;
Client *c;
i = 0;
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || !ISTILED(c)) if (!VISIBLEON(c, m) || !ISTILED(c)) continue;
continue;
int32_t row_idx, col_idx, cols_in_this_row; int row, col;
if (i < first_group_count) { if (i < first_group_count) {
row_idx = i / base_cols; row = i / base_cols;
col_idx = i % base_cols; col = i % base_cols;
cols_in_this_row = base_cols; cols_in_row = base_cols;
} else { } else {
int32_t offset = i - first_group_count; int off = i - first_group_count;
row_idx = first_group_rows + (offset / (base_cols + 1)); row = first_group_rows + off / (base_cols + 1);
col_idx = offset % (base_cols + 1); col = off % (base_cols + 1);
cols_in_this_row = base_cols + 1; cols_in_row = base_cols + 1;
} }
c->grid_row_per = row_pers[row_idx]; if (row != prev_row) {
c->grid_col_per = col_pers[col_idx]; row_y = m->w.y + cur_gappov;
c->grid_row_idx = row_idx; for (int pr = 0; pr < row; pr++)
c->grid_col_idx = col_idx; row_y += avail_h * (fs->cols[pr].ratio / sum_row) + cur_gappiv;
float fl_cy = m->w.y + cur_gappov; struct FairColNode *rn = &fs->cols[row];
for (int j = 0; j < row_idx; j++) sum_col = 0.0f;
fl_cy += avail_h * (row_pers[j] / sum_row) + cur_gappiv; avail_w = m->w.width - 2*cur_gappoh - (cols_in_row-1)*cur_gappih;
float fl_ch = (row_idx == rows - 1) for (int ci = 0; ci < cols_in_row; ci++) {
? (m->w.y + m->w.height - cur_gappov - fl_cy) if (rn->row_ratios[ci] <= 0.0f) rn->row_ratios[ci] = 1.0f;
: avail_h * (row_pers[row_idx] / sum_row); sum_col += rn->row_ratios[ci];
}
col_x = m->w.x + cur_gappoh;
prev_row = row;
}
float sum_col_this_row = 0.0f; struct FairColNode *rn = &fs->cols[row];
for (int j = 0; j < cols_in_this_row; j++)
sum_col_this_row += col_pers[j];
float avail_w = float row_h = (row == rows - 1)
m->w.width - 2 * cur_gappoh - (cols_in_this_row - 1) * cur_gappih; ? (m->w.y + m->w.height - cur_gappov - row_y)
float fl_cx = m->w.x + cur_gappoh; : avail_h * (rn->ratio / sum_row);
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, float col_w = (col == cols_in_row - 1)
(struct wlr_box){.x = (int32_t)fl_cx, ? (m->w.x + m->w.width - cur_gappoh - col_x)
.y = (int32_t)fl_cy, : avail_w * (rn->row_ratios[col] / sum_col);
.width = (int32_t)fl_cw,
.height = (int32_t)fl_ch}, c->grid_row_idx = row;
0); c->grid_col_idx = col;
c->grid_row_per = rn->ratio;
c->grid_col_per = rn->row_ratios[col];
resize(c, (struct wlr_box){
.x = (int32_t)col_x,
.y = (int32_t)row_y,
.width = (int32_t)col_w,
.height = (int32_t)row_h,
}, 0);
col_x += col_w + cur_gappih;
i++; i++;
} }
} }

View file

@ -585,6 +585,19 @@ struct DwindleNode {
Client *client; Client *client;
}; };
typedef struct FairColNode FairColNode;
struct FairColNode {
float ratio;
int n_rows;
float row_ratios[32];
};
typedef struct FairState FairState;
struct FairState {
int n_cols;
struct FairColNode cols[32];
};
struct ScrollerStackNode { struct ScrollerStackNode {
Client *client; Client *client;
float scroller_proportion; float scroller_proportion;
@ -1004,6 +1017,7 @@ struct Pertag {
int32_t no_render_border[LENGTH(tags) + 1]; int32_t no_render_border[LENGTH(tags) + 1];
int32_t open_as_floating[LENGTH(tags) + 1]; int32_t open_as_floating[LENGTH(tags) + 1];
struct DwindleNode *dwindle_root[LENGTH(tags) + 1]; struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
struct FairState *fair_state[LENGTH(tags) + 1];
const Layout *ltidxs[LENGTH(tags) + 1]; const Layout *ltidxs[LENGTH(tags) + 1];
struct TagScrollerState *scroller_state[LENGTH(tags) + 1]; struct TagScrollerState *scroller_state[LENGTH(tags) + 1];
}; };
@ -2539,6 +2553,7 @@ void cleanupmon(struct wl_listener *listener, void *data) {
cleanup_monitor_dwindle(m); cleanup_monitor_dwindle(m);
cleanup_monitor_scroller(m); cleanup_monitor_scroller(m);
cleanup_monitor_fair(m);
free(m->pertag); free(m->pertag);
free(m); free(m);