diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 86 |
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) { |
