aboutsummaryrefslogtreecommitdiffstats
path: root/src/config.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-27 15:09:02 +0200
committeruser <user@clank>2026-08-27 15:09:02 +0200
commit2ee881452aa4b19701a071f0333a15265e3b314e (patch)
tree82fc4fc20ebcff8c27fbebe170534a9bc9213f6e /src/config.c
parentmacos-remote-build: tolerate notarization ticket propagation lag (diff)
downloadslosh-2ee881452aa4b19701a071f0333a15265e3b314e.tar.gz
slosh-2ee881452aa4b19701a071f0333a15265e3b314e.tar.xz
slosh-2ee881452aa4b19701a071f0333a15265e3b314e.zip
selection: drag past an edge scrolls the viewport under it
Text taller than the viewport was not selectable in one gesture: the drag stopped dead at the pane's border. Two faults, one visible and one waiting. The visible one: DRAG_SELECT motion subtracted the content origin in uint16_t, so a pointer above or left of the pane underflowed, the grid lookup failed, and the selection froze. The waiting one: the anchor was a pair of *viewport* coordinates re-resolved on every extend, so the moment anything scrolled mid-drag the anchor would have drifted with the viewport, re-anchoring to whatever slid under the press. The anchor is now a tracked grid ref -- the thing lib-vt documents as being for 'long-lived anchors such as selections' -- so it follows its cell across scrolling, pruning and reflow, and only the near end of the selection moves. If a program flooding output prunes the anchored row out of scrollback entirely, the selection keeps what it had; there is no cell left to anchor a new answer to. Motion clamps into the content rect (a pointer past an edge is pointing at the nearest cell), and holding past the top or bottom edge scrolls on a clock rather than on motion, because a resting pointer sends no events and 'keep going' is exactly what resting it there means. app_tick() does the step, app_next_deadline_ms() wakes the poll loop for it -- the same mechanism toasts and hover guides already use -- and both front ends call the tick, so the behaviour exists under the server and the headless harness alike. Rows per step is how far past the edge the pointer sits: one row past creeps, further hurries, so the hand already dragging is also the throttle. The metronome is select_scroll_ms, a knob so the test can configure the clock down instead of sleeping. The test drags to one row above the pane and simply holds: the viewport walks to the top of scrollback (clamping makes the endpoint exact), the copy starts at a line that was never on screen with the press, and still ends at the pressed row -- that last check is the tracked anchor's.
Diffstat (limited to 'src/config.c')
-rw-r--r--src/config.c10
1 files changed, 10 insertions, 0 deletions
diff --git a/src/config.c b/src/config.c
index 5d9d688..8ca9fcf 100644
--- a/src/config.c
+++ b/src/config.c
@@ -891,6 +891,10 @@ void config_defaults(config_t *c) {
* -- and any key or click ends it early. */
c->splash_ms = 900;
c->hover_delay_ms = 250;
+ /* Slow enough to pick a line at one row past the edge, fast enough to cross
+ * ten thousand lines of scrollback without regretting the gesture -- and the
+ * distance past the edge scales the step, so the hand can hurry it. */
+ c->select_scroll_ms = 50;
c->double_click_ms = 400;
/* Quotes, brackets, and the punctuation that ends a clause or a list.
*
@@ -1609,6 +1613,9 @@ char *config_render(const config_t *c) {
cb_add(&b, "splash_ms %u // the logo, on attach; 0 for never\n",
c->splash_ms);
cb_add(&b, "hover_delay_ms %u\n", c->hover_delay_ms);
+ cb_add(&b,
+ "select_scroll_ms %u // drag past an edge: ms per scroll step\n",
+ c->select_scroll_ms);
cb_add(&b, "double_click_ms %u\n", c->double_click_ms);
cb_qstr(&b, "word_separators", c->word_separators,
"// what a double-click's word stops at");
@@ -1857,6 +1864,7 @@ static const char *const KNOWN_TOP[] = {
"scroll_lines",
"scrollback",
"scrollback_bytes",
+ "select_scroll_ms",
"shader_dir",
"shaders",
"shell",
@@ -2170,6 +2178,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err,
(uint16_t)kdl_arg_int(kdl_child(root, "splash_ms"), 0, c->splash_ms);
c->hover_delay_ms = (uint16_t)kdl_arg_int(kdl_child(root, "hover_delay_ms"),
0, c->hover_delay_ms);
+ c->select_scroll_ms = (uint16_t)kdl_arg_int(
+ kdl_child(root, "select_scroll_ms"), 0, c->select_scroll_ms);
c->double_click_ms = (uint16_t)kdl_arg_int(kdl_child(root, "double_click_ms"),
0, c->double_click_ms);
const char *ws = kdl_arg(kdl_child(root, "word_separators"), 0, NULL);