From 9ce11bfeff73b96704e521b41cffe4625b02f103 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 15 Sep 2026 16:50:50 -0300 Subject: Reorder columns using aligned and distinctly colored tag grips --- src/layout.zig | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'src/layout.zig') diff --git a/src/layout.zig b/src/layout.zig index f789ba02..47b2c1a4 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -707,6 +707,55 @@ pub fn focusDir(p: *Pardes, from: usize, dir: enum { left, right, up, down }) vo } } +pub fn columnBySerial(p: *const Pardes, serial: u32) ?usize { + if (serial == 0) return null; + for (0..p.ncol) |column| if (p.col_serial[column] == serial) return column; + return null; +} + +pub fn columnMoveTarget(p: *const Pardes, source: usize, x: u16) usize { + if (source >= p.ncol) return source; + var insertion: usize = 0; + for (0..p.ncol) |column| { + if (column == source) continue; + if (x >= p.col_x[column] + p.col_w[column] / 2) insertion += 1; + } + return insertion; +} + +pub fn reorderColumn(p: *Pardes, from: usize, to: usize) void { + if (from >= p.ncol or to >= p.ncol or from == to) return; + const ids = p.col_panes[from]; + const count = p.col_n[from]; + const weight = p.col_weight[from]; + const tag = p.col_tags[from]; + const serial = p.col_serial[from]; + var at = from; + while (at != to) { + const next = if (at < to) at + 1 else at - 1; + p.col_panes[at] = p.col_panes[next]; + p.col_n[at] = p.col_n[next]; + p.col_weight[at] = p.col_weight[next]; + p.col_tags[at] = p.col_tags[next]; + p.col_serial[at] = p.col_serial[next]; + at = next; + } + p.col_panes[to] = ids; + p.col_n[to] = count; + p.col_weight[to] = weight; + p.col_tags[to] = tag; + p.col_serial[to] = serial; + if (p.header_column) |column| { + if (column == from) p.header_column = to else if (from < to and column > from and column <= to) { + p.header_column = column - 1; + } else if (from > to and column >= to and column < from) p.header_column = column + 1; + } + p.pointer_tag_hit = null; + p.look_hover_wait = null; + p.look_hover_preview = null; + compute(p); +} + pub fn targetColumn(p: *Pardes, cur_x: u16) usize { var tc: usize = if (p.ncol > 0) p.ncol - 1 else 0; for (0..p.ncol) |c| { @@ -862,9 +911,11 @@ pub fn removePane(p: *Pardes, id: usize) void { p.col_n[j] = p.col_n[j + 1]; p.col_weight[j] = p.col_weight[j + 1]; p.col_tags[j] = p.col_tags[j + 1]; + p.col_serial[j] = p.col_serial[j + 1]; } p.ncol -= 1; p.col_tags[p.ncol] = .{}; + p.col_serial[p.ncol] = 0; } } @@ -883,9 +934,11 @@ pub fn joinCol(p: *Pardes) void { p.col_n[j] = p.col_n[j + 1]; p.col_weight[j] = p.col_weight[j + 1]; p.col_tags[j] = p.col_tags[j + 1]; + p.col_serial[j] = p.col_serial[j + 1]; } p.ncol -= 1; p.col_tags[p.ncol] = .{}; + p.col_serial[p.ncol] = 0; } pub fn canSplitColumn(p: *Pardes, source_id: usize) bool { @@ -931,9 +984,11 @@ pub fn splitColumn(p: *Pardes, source_id: usize, id: usize, before: bool) bool { p.col_n[j] = p.col_n[j - 1]; p.col_weight[j] = p.col_weight[j - 1]; p.col_tags[j] = p.col_tags[j - 1]; + p.col_serial[j] = p.col_serial[j - 1]; } p.col_weight[c] = new_weight; p.col_tags[c] = .{}; + p.col_serial[c] = 0; p.col_panes[c][0] = id; p.col_n[c] = 1; p.ncol += 1; @@ -1103,6 +1158,11 @@ fn computeCollapsedColumn(p: *Pardes, c: usize, x: u16, width: u16, folded: usiz pub fn compute(p: *Pardes) void { p.rects = @splat(.{}); if (p.ncol == 0) return; + for (0..p.ncol) |column| if (p.col_serial[column] == 0) { + p.col_serial[column] = p.next_column_serial; + p.next_column_serial +%= 1; + if (p.next_column_serial == 0) p.next_column_serial = 1; + }; var wsum: u128 = 0; for (0..p.ncol) |c| wsum += p.col_weight[c]; if (wsum == 0) wsum = 1; @@ -2140,3 +2200,35 @@ test "presentation snapshots own compact body and tag rows across later edits" { try std.testing.expectEqual(@as(u8, 'B'), state.previous_body_layers[0].cells[0].text[0]); try std.testing.expectEqual(@as(u8, 'U'), state.previous_tag_layers[0].cells[0].text[0]); } + +test "column reorder moves only crossed state and preserves weights" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 173, .rows = 20 }); + defer p.deinit(); + p.ncol = 4; + p.col_n = @splat(0); + for (0..4) |c| { + p.col_weight[c] = c * 13 + 7; + p.col_serial[c] = @intCast(c + 10); + p.col_panes[c][0] = c; + p.col_tags[c].saved_col = @intCast(c + 20); + } + compute(p); + const original_weights = p.col_weight; + const outer_x = p.col_x[0]; + const outer_w = p.col_w[0]; + p.header_column = 1; + p.topbar_col = 23; + p.header_scroll = 9; + reorderColumn(p, 1, 3); + try std.testing.expectEqual(original_weights[1], p.col_weight[3]); + try std.testing.expectEqual(original_weights[2], p.col_weight[1]); + try std.testing.expectEqual(@as(u32, 11), p.col_serial[3]); + try std.testing.expectEqual(@as(?usize, 3), p.header_column); + try std.testing.expectEqual(@as(?u16, 23), p.topbar_col); + try std.testing.expectEqual(@as(usize, 9), p.header_scroll); + try std.testing.expectEqual(outer_x, p.col_x[0]); + try std.testing.expectEqual(outer_w, p.col_w[0]); + reorderColumn(p, 3, 1); + try std.testing.expectEqualSlices(u64, original_weights[0..4], p.col_weight[0..4]); + try std.testing.expectEqual(@as(?u16, 21), p.col_tags[1].saved_col); +} -- cgit v1.3