diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 19:20:20 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | b08322345b33abcee114c05c85fa4c46c10f4701 (patch) | |
| tree | 645cbacce7ef1d5c7abd71f0b76a3c72e763835d /src/mouse.zig | |
| parent | 8429e7bd8aff1ae0c71d3b1a7a6b9f01ed52e691 (diff) | |
| download | pardes-b08322345b33abcee114c05c85fa4c46c10f4701.tar.gz pardes-b08322345b33abcee114c05c85fa4c46c10f4701.zip | |
A double-click selects as acme's does: a word, a line, a bracketed or quoted text
A second B1 click at the same cell selected nothing more. Now, as acme's textdoubleclick (plan9port text.c:1407), it selects up to the match of a bracket just before or after the point, the whole line at a line's start or end, the quoted text inside ' " `, or else the word, in bodies, tags and terminals in normal mode. A plain click still puts the caret on its cell.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/mouse.zig')
| -rw-r--r-- | src/mouse.zig | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/src/mouse.zig b/src/mouse.zig index 2dcd80ed..ac3e206b 100644 --- a/src/mouse.zig +++ b/src/mouse.zig @@ -755,6 +755,36 @@ fn mirrorTtySelection(p: *Pardes, pane: *Pane) void { edit.setYank(p, text); } +/// acme's double-click (text.c:881 textselect, plan9port text.c:1407 +/// textdoubleclick): a second plain B1 click at the same cell within half a +/// second selects what edit.doubleClickRange says, from the caret the click +/// left. A plain click still puts the caret on its cell. +fn doubleClick(p: *Pardes, id: usize, t: *panes.Text, on_tag: bool, row: i32, col: i32) void { + const now = clockMs(); + const prev = p.last_click; + p.last_click = .{ .id = id, .on_tag = on_tag, .row = row, .col = col, .ms = now }; + const c = prev orelse return; + if (c.id != id or c.on_tag != on_tag or c.row != row or c.col != col or now - c.ms > 500) return; + p.last_click = null; + const text = edit.flatSurface(p, t) catch return; + const q = modal.offsetAt(text, .{ .row = @intCast(@max(0, t.cur_row)), .col = @intCast(@max(0, t.cur_col)) }); + const r = edit.doubleClickRange(text, q); + if (r[1] <= r[0]) return; + const a = modal.positionAt(text, r[0]); + const h = modal.positionAt(text, modal.prevGrapheme(text, r[1])); + t.vsel = .{ .active = true, .row = @intCast(a.row), .col = @intCast(a.col), .explicit = true }; + t.cur_row = @intCast(h.row); + t.cur_col = @intCast(h.col); + t.cur_pinned = true; +} + +fn clockMs() i64 { + if (comptime !pardes.hosted) return 0; + var ts: std.c.timespec = undefined; + if (std.c.clock_gettime(.MONOTONIC, &ts) != 0) return 0; + return @as(i64, ts.sec) * 1000 + @divTrunc(@as(i64, ts.nsec), 1_000_000); +} + fn dragRelease(p: *Pardes, button: Mouse.Button) void { // A different button's release belongs to a chord or another host // stream; only the button which began this gesture may end it. @@ -838,6 +868,7 @@ fn dragRelease(p: *Pardes, button: Mouse.Button) void { } else if (tagline.tagOffsetAt(p, pane, row, col)) |cursor| { pane.tag.cur_row = @intCast(cursor.row); pane.tag.cur_col = @intCast(cursor.col); + if (!dragged) doubleClick(p, s.id, &pane.tag, true, sl.r1, sl.c1); } return; } @@ -863,6 +894,7 @@ fn dragRelease(p: *Pardes, button: Mouse.Button) void { pane.body.select = false; pane.body.restore_cursor = false; pane.body.sticky_col = -1; + if (!dragged) doubleClick(p, s.id, &pane.body, false, sl.r1, sl.c1); if (s.ctrl) p.lspRequest(s.id, .definition, ""); } } else { |
