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,
int32_t offsety, uint32_t time) {
static struct FairState *ensure_fair_state(Monitor *m, uint32_t tag) {
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)
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];
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) {
drag_begin_cursorx = cursor->x;
@ -557,19 +576,14 @@ void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
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;
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->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) {
@ -579,80 +593,37 @@ void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
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;
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;
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;
int adj_c_idx = grabc->grid_col_idx + 1;
int adj_r_idx = grabc->grid_row_idx + 1;
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;
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;
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;
adj_c_idx = grabc->grid_col_idx + 1; sign_x = 1.0f;
adj_r_idx = grabc->grid_row_idx + 1; 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;
}
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;
@ -663,159 +634,121 @@ void resize_tile_grid_fair(Client *grabc, bool isdrag, int32_t offsetx,
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 (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;
}
// 应用列宽调节
int col = grabc->grid_col_idx;
int row = grabc->grid_row_idx;
// Apply column width adjustment
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;
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) {
// Handle asymmetric boundary
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)) {
int32_t l_cols;
for (l_cols = 0; l_cols <= n_tiling; l_cols++)
if (l_cols * l_cols >= n_tiling) break;
int32_t first_group_cols = l_cols - (n_tiling % l_cols);
float p_col =
(grabc->grid_col_idx == base_cols - 1)
if (is_vfair) {
// 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)
: (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;
p_col = fmaxf(0.01f, fminf(0.99f, p_col));
float new_r = p_col / (1.0f - p_col);
new_r = fmaxf(0.1f, fminf(10.0f, new_r));
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; }
}
}
// 反推非线性真实权重值
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;
// Mutate FairState
if (is_vfair) {
fs->cols[row].row_ratios[col] = new_my_col;
fs->cols[row].row_ratios[adj_c_idx] = new_adj_col;
} else {
new_my_col = 1.0f;
new_adj_col = new_r_var_per;
}
fs->cols[col].ratio = new_my_col;
fs->cols[adj_c_idx].ratio = new_adj_col;
}
}
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;
}
}
}
// 应用行高调节
// Apply row height adjustment
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;
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) {
// Handle asymmetric boundary
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;
}
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)
if (is_vfair) {
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)
: (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;
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 == 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; }
}
} else {
new_my_row = 1.0f;
new_adj_row = new_r_var_per;
}
if ((row == base_rows - 1 && adj_r_idx == base_rows) ||
(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) {
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;
}
// Mutate FairState
if (is_vfair) {
fs->cols[row].ratio = new_my_row;
fs->cols[adj_r_idx].ratio = new_adj_row;
} else {
fs->cols[col].row_ratios[row] = new_my_row;
fs->cols[col].row_ratios[adj_r_idx] = new_adj_row;
}
}

View file

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

View file

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

View file

@ -585,6 +585,19 @@ struct DwindleNode {
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 {
Client *client;
float scroller_proportion;
@ -1004,6 +1017,7 @@ struct Pertag {
int32_t no_render_border[LENGTH(tags) + 1];
int32_t open_as_floating[LENGTH(tags) + 1];
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
struct FairState *fair_state[LENGTH(tags) + 1];
const Layout *ltidxs[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_scroller(m);
cleanup_monitor_fair(m);
free(m->pertag);
free(m);