summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig86
1 files changed, 45 insertions, 41 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index f968511a..4786bd70 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -5155,7 +5155,7 @@ const Drag = union(enum) {
move: struct { id: usize, cur_x: u16, cur_y: u16 },
/// `grab`: how far into the grip it was pressed, so the column's left
/// edge lands at `cur_x - grab`. A release still on the grip moves nothing.
- column_move: struct { column: usize, serial: u32, cur_x: u16, grab: u16 = 0 },
+ column_move: struct { serial: u32, cur_x: u16, grab: u16 = 0 },
tag: struct { id: usize },
/// chorded: a 1-2/1-3 cut/paste chord fired during this left drag —
/// the drag's own release is then inert
@@ -7793,7 +7793,7 @@ pub const Pardes = struct {
.key => |key| p.handleKey(key),
.mouse => |m| {
if (m.kind == .release) switch (p.drag) {
- .border_v, .border_h, .move => p.presentation.snap_once = true,
+ .border_v, .border_h, .move, .column_move => p.presentation.snap_once = true,
else => {},
};
p.handleMouse(m);
@@ -12017,7 +12017,7 @@ pub const Pardes = struct {
if (mcol < p.col_x[column] or mcol >= p.col_x[column] + @min(config.GUTTER, p.col_w[column])) continue;
if (m.button == config.select_button) {
p.header_drag = false;
- p.drag = .{ .column_move = .{ .column = column, .serial = p.col_serial[column], .cur_x = mcol, .grab = mcol - p.col_x[column] } };
+ p.drag = .{ .column_move = .{ .serial = p.col_serial[column], .cur_x = mcol, .grab = mcol - p.col_x[column] } };
}
return;
}
@@ -12488,15 +12488,10 @@ pub const Pardes = struct {
},
.border_h => |d| if (d.cur_y != seamRowOf(p, d.col, d.top_idx))
layout.applyRowSplit(p, d.col, d.top_idx, d.cur_y),
- .column_move => |d| if (layout.columnBySerial(p, d.serial)) |column| {
- const target = layout.columnMoveTarget(p, column, d.cur_x);
- if (target != column) {
- layout.reorderColumn(p, column, target);
- } else if (column > 0 and (d.cur_x < p.col_x[column] or d.cur_x >= p.col_x[column] + config.GUTTER)) {
- // Dropped short of another column's place: as in acme,
- // the column's left edge moves to where it was dropped.
- layout.setColumnPairWidth(p, column - 1, (d.cur_x -| d.grab) -| p.col_x[column - 1]);
- }
+ .column_move => |d| if (layout.columnBySerial(p, d.serial)) |column| switch (layout.columnDrop(p, column, d.cur_x, d.grab)) {
+ .stay => {},
+ .reorder => |target| layout.reorderColumn(p, column, target),
+ .edge => |x| layout.setColumnPairWidth(p, column - 1, x - p.col_x[column - 1]),
},
.move => |d| {
layout.movePane(p, d.id, d.cur_x, d.cur_y);
@@ -14091,7 +14086,10 @@ pub const Pardes = struct {
const active_col = if (layout.findPane(p, p.active)) |f| f.col == c else false;
const bg = if (active_col and p.settings.focus_tint) chrome.tag_active_bg else chrome.tag_bg;
const fg = if (active_col and p.settings.focus_tint) chrome.tag_active_fg else chrome.tag_fg;
- const grip_bg = chrome.column_box_dim;
+ // A grip is muted until it is grabbed; then it lights, as a pane's
+ // box does for the active pane, so the drag shows what it holds.
+ const grabbed = p.drag == .column_move and layout.columnBySerial(p, p.drag.column_move.serial) == c;
+ const grip_bg = if (grabbed) chrome.column_box else chrome.column_box_dim;
const lum = (@as(u16, grip_bg[0]) * 3 + @as(u16, grip_bg[1]) * 6 + @as(u16, grip_bg[2])) / 10;
const ink: [3]u8 = if (lum > 140) .{ 0, 0, 0 } else .{ 255, 255, 255 };
const grip_style: CellStyle = .{ .bg = .{ .rgb = grip_bg }, .fg = .{ .rgb = ink }, .font_role = .tagline };
@@ -14106,7 +14104,8 @@ pub const Pardes = struct {
.bg = .{ .rgb = bg },
.font_role = .tagline,
});
- if (p.pointer_inside and p.hover_row == p.topBarHeight() and p.hover_col >= x and p.hover_col < x + w) {
+ // A carried column's pointer is aiming at a place, not a word.
+ if (p.pointer_inside and p.drag != .column_move and p.hover_row == p.topBarHeight() and p.hover_col >= x and p.hover_col < x + w) {
const bar = p.columnTag(c);
if (wordBoundsAtCol(bar, panes.File.rawAtDisplay(bar, p.hover_col - x + bar_scroll))) |bounds| {
var col = panes.File.rawDisplayCol(bar, bounds.lo) -| bar_scroll;
@@ -14154,33 +14153,38 @@ pub const Pardes = struct {
var col = p.col_x[d.col];
while (col < p.col_x[d.col] + p.col_w[d.col]) : (col += 1) s.overlayDash(col, d.cur_y, "╌");
},
- .column_move => |d| {
- const source = layout.columnBySerial(p, d.serial) orelse d.column;
- const target = layout.columnMoveTarget(p, source, d.cur_x);
- if (source < p.ncol and target != source) {
- const boundary = if (target < source) p.col_x[target] else p.col_x[target] + p.col_w[target];
- const x = @min(boundary, s.cols -| 1);
- // Leave the outside insertion rail visible above compact
- // text layers. This clips presentation only, never reflows.
- if (boundary == s.cols) {
- for (&s.body_layers) |*layer| if (layer.rows > 0 and layer.viewport.x + layer.viewport.w == s.cols) {
- layer.viewport.w -|= 1;
- };
- for (&s.tag_layers) |*layer| if (layer.cols > 0 and layer.viewport.x + layer.viewport.w == s.cols and layer.viewport.y >= p.topBarHeight()) {
- layer.viewport.w -|= 1;
- };
- }
- var row: u16 = p.topBarHeight();
- while (row < s.rows) : (row += 1) s.overlayDash(x, row, "╎");
- } else if (source > 0 and source < p.ncol and (d.cur_x < p.col_x[source] or d.cur_x >= p.col_x[source] + config.GUTTER) and
- p.col_w[source - 1] + p.col_w[source] >= 2 * config.MINW)
- {
- // Where the left edge would land: the border drag's rail.
- const lo = p.col_x[source - 1] + config.MINW;
- const hi = (p.col_x[source] + p.col_w[source]) -| config.MINW;
- const x = @min(std.math.clamp(d.cur_x -| d.grab, lo, @max(lo, hi)), s.cols -| 1);
- var row: u16 = p.topBarHeight();
- while (row < s.rows) : (row += 1) s.overlayDash(x, row, "╎");
+ .column_move => |d| rail: {
+ const source = layout.columnBySerial(p, d.serial) orelse break :rail;
+ // Where the column's left edge would land, reordered or not.
+ const edge: u16 = switch (layout.columnDrop(p, source, d.cur_x, d.grab)) {
+ .stay => break :rail,
+ .reorder => |target| if (target < source) p.col_x[target] else p.col_x[target] + p.col_w[target],
+ .edge => |x| x,
+ };
+ // Leave the outside insertion rail visible above compact
+ // text layers. This clips presentation only, never reflows.
+ if (edge == s.cols) {
+ for (&s.body_layers) |*layer| if (layer.rows > 0 and layer.viewport.x + layer.viewport.w == s.cols) {
+ layer.viewport.w -|= 1;
+ };
+ for (&s.tag_layers) |*layer| if (layer.cols > 0 and layer.viewport.x + layer.viewport.w == s.cols and layer.viewport.y >= p.topBarHeight()) {
+ layer.viewport.w -|= 1;
+ };
+ }
+ // The rail runs in the seam cell left of the edge, where a
+ // border drag's does: the edge cell itself is a gutter, which
+ // the GUI paints as a scroll rail that swallowed the dashes.
+ // At the window's left edge there is no seam, so it runs just
+ // past that scroll rail instead. It takes the lit grip's
+ // color: this is where the grabbed column lands.
+ const x = @min(if (edge == 0) config.GUTTER - 1 else edge - 1, s.cols -| 1);
+ var row: u16 = p.topBarHeight();
+ while (row < s.rows) : (row += 1) {
+ // Past the left edge's scroll rail the rail would cut
+ // through column 0's grip and pane boxes: leave them whole.
+ if (edge == 0 and s.at(x, row).style.font_role == .tagline) continue;
+ s.overlayDash(x, row, "╎");
+ s.at(x, row).style.fg = .{ .rgb = chrome.column_box };
}
},
.move => |d| if (d.cur_x < s.cols) {