summaryrefslogtreecommitdiff
path: root/src/layout.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout.zig')
-rw-r--r--src/layout.zig92
1 files changed, 92 insertions, 0 deletions
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);
+}