Skip to content

Commit cd90465

Browse files
committed
feat: scroller resize, focus prev on close
1 parent d87b2fb commit cd90465

4 files changed

Lines changed: 127 additions & 32 deletions

File tree

include/scroller.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ typedef struct scroller_state_t {
4444
int column_count, capacity, active_column_idx;
4545
double view_offset;
4646
struct wlr_box working_area;
47+
bool activate_prev_column_on_removal;
4748
} scroller_state_t;
4849

4950
scroller_state_t *scroller_create(void);
@@ -67,11 +68,13 @@ bool scroller_expel_from_column(desktop_t *d);
6768
bool scroller_is_tiled(const client_t *c);
6869
void scroller_apply_client_rules(client_t *c, float rule_proportion, float rule_proportion_single);
6970

70-
void scroller_resize_width(client_t *client, float delta);
71-
void scroller_resize_stack(client_t *client, float delta);
71+
bool scroller_resize_width(desktop_t *d, float delta);
72+
bool scroller_resize_stack(desktop_t *d, float delta);
7273
void scroller_set_proportion(client_t *client, float proportion);
7374
void scroller_cycle_proportion_preset(client_t *client);
7475

76+
void scroller_apply_active_focus(desktop_t *d, struct output_t *m);
77+
7578
int scroller_collect(desktop_t *d, node_t ***out_nodes);
7679

7780
extern float scroller_default_proportion;

src/keyboard.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "layout.h"
99
#include "master_stack.h"
1010
#include "output.h"
11+
#include "scroller.h"
1112
#include "seat.h"
1213
#include "server.h"
1314
#include "tabs.h"
@@ -1074,6 +1075,13 @@ void resize_left(void) {
10741075
}
10751076
if (resize_master_stack_ratio(true, -RESIZE_AMOUNT))
10761077
return;
1078+
if (mon->desk->layout == LAYOUT_SCROLLER) {
1079+
if (scroller_resize_width(mon->desk, -RESIZE_AMOUNT)) {
1080+
arrange(mon, mon->desk, true);
1081+
wlr_log(WLR_INFO, "Resized left (scroller)");
1082+
}
1083+
return;
1084+
}
10771085

10781086
node_t *n = mon->desk->focus;
10791087
if (n->parent == NULL) {
@@ -1114,6 +1122,13 @@ void resize_right(void) {
11141122
}
11151123
if (resize_master_stack_ratio(true, RESIZE_AMOUNT))
11161124
return;
1125+
if (mon->desk->layout == LAYOUT_SCROLLER) {
1126+
if (scroller_resize_width(mon->desk, RESIZE_AMOUNT)) {
1127+
arrange(mon, mon->desk, true);
1128+
wlr_log(WLR_INFO, "Resized right (scroller)");
1129+
}
1130+
return;
1131+
}
11171132

11181133
node_t *n = mon->desk->focus;
11191134
if (n->parent == NULL) {
@@ -1154,6 +1169,16 @@ void resize_up(void) {
11541169
}
11551170
if (resize_master_stack_ratio(false, -RESIZE_AMOUNT))
11561171
return;
1172+
if (mon->desk->layout == LAYOUT_SCROLLER) {
1173+
float delta = -RESIZE_AMOUNT;
1174+
if (mon->desk->scroller_state)
1175+
delta *= (float)mon->desk->scroller_state->working_area.height;
1176+
if (scroller_resize_stack(mon->desk, delta)) {
1177+
arrange(mon, mon->desk, true);
1178+
wlr_log(WLR_INFO, "Resized up (scroller)");
1179+
}
1180+
return;
1181+
}
11571182

11581183
node_t *n = mon->desk->focus;
11591184
if (n->parent == NULL) {
@@ -1194,6 +1219,16 @@ void resize_down(void) {
11941219
}
11951220
if (resize_master_stack_ratio(false, RESIZE_AMOUNT))
11961221
return;
1222+
if (mon->desk->layout == LAYOUT_SCROLLER) {
1223+
float delta = RESIZE_AMOUNT;
1224+
if (mon->desk->scroller_state)
1225+
delta *= (float)mon->desk->scroller_state->working_area.height;
1226+
if (scroller_resize_stack(mon->desk, delta)) {
1227+
arrange(mon, mon->desk, true);
1228+
wlr_log(WLR_INFO, "Resized down (scroller)");
1229+
}
1230+
return;
1231+
}
11971232

11981233
node_t *n = mon->desk->focus;
11991234
if (n->parent == NULL) {

src/scroller.c

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ scroller_state_t *scroller_create(void) {
5656
if (!s)
5757
return NULL;
5858
s->view_offset = 0.0;
59+
s->activate_prev_column_on_removal = false;
5960
return s;
6061
}
6162

@@ -172,9 +173,12 @@ bool scroller_add_tile(scroller_state_t *s, client_t *client, bool activate) {
172173
new_col->tile_count = 1;
173174

174175
// adjust active_column_idx
175-
if (activate)
176+
if (activate) {
176177
s->active_column_idx = insert_col;
177-
else if (s->active_column_idx >= insert_col)
178+
// Track that we should activate the previous column if this one is
179+
// removed, matching niri's activate_prev_column_on_removal.
180+
s->activate_prev_column_on_removal = true;
181+
} else if (s->active_column_idx >= insert_col)
178182
s->active_column_idx++;
179183

180184
return true;
@@ -238,12 +242,24 @@ void scroller_remove_tile(scroller_state_t *s, client_t *client, struct output_t
238242
// fix active_column_idx
239243
if (s->column_count == 0) {
240244
s->active_column_idx = 0;
245+
s->activate_prev_column_on_removal = false;
241246
return;
242247
}
243-
if (s->active_column_idx >= s->column_count)
244-
s->active_column_idx = s->column_count - 1;
245-
if (col_idx <= s->active_column_idx && s->active_column_idx > 0)
248+
if (col_idx == s->active_column_idx) {
249+
// The active column was removed.
250+
if (s->activate_prev_column_on_removal && s->active_column_idx > 0) {
251+
// Activate the previous column (e.g. open-and-immediately-close).
252+
s->active_column_idx--;
253+
} else {
254+
// Activate the next column, clamped to the last available.
255+
if (s->active_column_idx >= s->column_count)
256+
s->active_column_idx = s->column_count - 1;
257+
}
258+
s->activate_prev_column_on_removal = false;
259+
} else if (col_idx < s->active_column_idx) {
260+
// A column to the left was removed; shift the active index.
246261
s->active_column_idx--;
262+
}
247263
} else {
248264
if (tile_idx < col->active_tile_idx)
249265
col->active_tile_idx--;
@@ -292,6 +308,8 @@ void scroller_arrange(struct output_t *m, desktop_t *d, struct wlr_box available
292308
if (!s)
293309
return;
294310

311+
s->working_area = available;
312+
295313
// populate from BSP tree if scroller state is empty but toplevels exist
296314
if (s->column_count == 0 && d->root)
297315
populate_from_tree(s, d);
@@ -417,7 +435,7 @@ void scroller_arrange(struct output_t *m, desktop_t *d, struct wlr_box available
417435
free(col_xs);
418436
}
419437

420-
static void apply_active_focus(desktop_t *d) {
438+
void scroller_apply_active_focus(desktop_t *d, struct output_t *m) {
421439
scroller_state_t *s = d->scroller_state;
422440
if (!s || s->column_count == 0) {
423441
d->focus = NULL;
@@ -434,8 +452,9 @@ static void apply_active_focus(desktop_t *d) {
434452
if (c && c->toplevel && c->toplevel->node) {
435453
node_t *target = c->toplevel->node;
436454
d->focus = target;
437-
if (d->output)
438-
focus_node(d->output, d, target);
455+
output_t *out = d->output ? d->output : m;
456+
if (out)
457+
focus_node(out, d, target);
439458
}
440459
}
441460

@@ -451,7 +470,8 @@ bool scroller_focus_next(desktop_t *d) {
451470
s->active_column_idx++;
452471
}
453472
s->view_offset = 0.0; // reset scroll for now
454-
apply_active_focus(d);
473+
s->activate_prev_column_on_removal = false;
474+
scroller_apply_active_focus(d, NULL);
455475
return true;
456476
}
457477

@@ -467,7 +487,8 @@ bool scroller_focus_prev(desktop_t *d) {
467487
s->active_column_idx--;
468488
}
469489
s->view_offset = 0.0;
470-
apply_active_focus(d);
490+
s->activate_prev_column_on_removal = false;
491+
scroller_apply_active_focus(d, NULL);
471492
return true;
472493
}
473494

@@ -483,7 +504,7 @@ bool scroller_focus_down(desktop_t *d) {
483504
return false;
484505

485506
col->active_tile_idx++;
486-
apply_active_focus(d);
507+
scroller_apply_active_focus(d, NULL);
487508
return true;
488509
}
489510

@@ -499,7 +520,7 @@ bool scroller_focus_up(desktop_t *d) {
499520
return false;
500521

501522
col->active_tile_idx--;
502-
apply_active_focus(d);
523+
scroller_apply_active_focus(d, NULL);
503524
return true;
504525
}
505526

@@ -515,7 +536,8 @@ void scroller_center_window(desktop_t *d, client_t *client) {
515536
s->active_column_idx = col_idx;
516537
s->columns[col_idx].active_tile_idx = tile_idx;
517538
s->view_offset = 0.0;
518-
apply_active_focus(d);
539+
s->activate_prev_column_on_removal = false;
540+
scroller_apply_active_focus(d, NULL);
519541
}
520542

521543
bool scroller_consume_into_column(desktop_t *d) {
@@ -541,7 +563,12 @@ bool scroller_consume_into_column(desktop_t *d) {
541563
if (target_col >= s->column_count)
542564
return false;
543565

544-
return scroller_add_tile_to_column(s, cl, target_col, true);
566+
bool result = scroller_add_tile_to_column(s, cl, target_col, true);
567+
if (result)
568+
// Track that we should activate the previous column if this one is
569+
// removed, matching niri's activate_prev_column_on_removal.
570+
s->activate_prev_column_on_removal = true;
571+
return result;
545572
}
546573

547574
bool scroller_expel_from_column(desktop_t *d) {
@@ -577,26 +604,48 @@ void scroller_apply_client_rules(client_t *c, float rule_proportion, float rule_
577604
// TODO: store per-client proportion overrides if needed later.
578605
}
579606

580-
void scroller_resize_width(client_t *client, float delta) {
581-
if (!client)
582-
return;
583-
// Find the client's column in the scroller state (needs desktop_t).
584-
// For now, stub – proportion is set via config.
585-
// This will be wired through IPC / keybindings properly.
586-
(void)delta;
587-
wlr_log(WLR_DEBUG, "scroller_resize_width: stub (client=%p delta=%.2f)", (void *)client, delta);
607+
bool scroller_resize_width(desktop_t *d, float delta) {
608+
scroller_state_t *s = d ? d->scroller_state : NULL;
609+
if (!s || s->column_count == 0)
610+
return false;
611+
612+
int col = s->active_column_idx;
613+
double prop = s->columns[col].width.value + (double)delta;
614+
if (prop < 0.1)
615+
prop = 0.1;
616+
if (prop > 1.0)
617+
prop = 1.0;
618+
s->columns[col].width.type = SCROLLER_WIDTH_PROPORTION;
619+
s->columns[col].width.value = prop;
620+
return true;
588621
}
589622

590-
void scroller_resize_stack(client_t *client, float delta) {
591-
(void)client;
592-
(void)delta;
593-
wlr_log(WLR_DEBUG, "scroller_resize_stack: stub");
623+
bool scroller_resize_stack(desktop_t *d, float delta) {
624+
scroller_state_t *s = d ? d->scroller_state : NULL;
625+
if (!s || s->column_count == 0)
626+
return false;
627+
628+
scroller_column_t *col = &s->columns[s->active_column_idx];
629+
if (col->tile_count == 0)
630+
return false;
631+
632+
int tile_idx = col->active_tile_idx;
633+
// Convert auto-height to fixed using the last computed rect height.
634+
if (col->tiles[tile_idx].height.type == SCROLLER_HEIGHT_AUTO) {
635+
col->tiles[tile_idx].height.type = SCROLLER_HEIGHT_FIXED;
636+
col->tiles[tile_idx].height.value = (double)col->tiles[tile_idx].rect.height;
637+
}
638+
639+
double h = col->tiles[tile_idx].height.value + (double)delta;
640+
if (h < 1.0)
641+
h = 1.0;
642+
col->tiles[tile_idx].height.value = h;
643+
return true;
594644
}
595645

596646
void scroller_set_proportion(client_t *client, float proportion) {
597647
if (!client)
598648
return;
599-
// TODO: find column and update its width.
600649
(void)proportion;
601650
wlr_log(WLR_DEBUG, "scroller_set_proportion: stub (client=%p prop=%.2f)", (void *)client,
602651
proportion);
@@ -605,7 +654,6 @@ void scroller_set_proportion(client_t *client, float proportion) {
605654
void scroller_cycle_proportion_preset(client_t *client) {
606655
if (!client || !scroller_proportion_preset || scroller_proportion_preset_count == 0)
607656
return;
608-
// TODO: find column, cycle through presets.
609657
wlr_log(WLR_DEBUG, "scroller_cycle_proportion_preset: stub");
610658
}
611659

src/toplevel.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,8 +811,10 @@ void toplevel_unmap(struct wl_listener *listener, void *data) {
811811
n->client && n->client->title[0] ? n->client->title : "?", n->id);
812812
}
813813
// in scroller layout, remove from scroller state first
814-
if (d->layout == LAYOUT_SCROLLER && d->scroller_state && n->client)
814+
if (d->layout == LAYOUT_SCROLLER && d->scroller_state && n->client) {
815815
scroller_remove_tile(d->scroller_state, n->client, m);
816+
scroller_apply_active_focus(d, m);
817+
}
816818

817819
remove_node(d, n);
818820

@@ -825,7 +827,14 @@ void toplevel_unmap(struct wl_listener *listener, void *data) {
825827
toplevel->node = NULL;
826828

827829
// focus handling after removing node
828-
if (d->focus != NULL && d->focus->client != NULL) {
830+
if (d->layout == LAYOUT_SCROLLER) {
831+
// fall back to tree focus only if croller is empty
832+
if (d->focus == NULL && d->root != NULL) {
833+
d->focus = first_extrema(d->root);
834+
if (d->focus != NULL)
835+
focus_node(d->output ? d->output : m, d, d->focus);
836+
}
837+
} else if (d->focus != NULL && d->focus->client != NULL) {
829838
focus_node(d->output ? d->output : m, d, d->focus);
830839
} else if (d->root != NULL) {
831840
d->focus = first_extrema(d->root);

0 commit comments

Comments
 (0)