Skip to content

Commit b8dbbcd

Browse files
committed
feat(editor): click-to-jump on the scrollbar track
Clicking an empty part of the editor's scrollbar track now jumps straight to that proportional position, instead of the browser default of paging one viewport at a time - which is painfully slow to reach a far-off spot in a large file. A click on the thumb is left to the native drag. Logic lives in a new EditorHelper/ScrollbarHelper.js (installClickToJump), wired from the Editor constructor so it applies to every editor, including inline ones. Works on both the vertical and horizontal scrollbars.
1 parent b15ee8a commit b8dbbcd

2 files changed

Lines changed: 96 additions & 1 deletion

File tree

src/editor/Editor.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ define(function (require, exports, module) {
9696
EditorPreferences = require("./EditorHelper/EditorPreferences"),
9797
ChangeHelper = require("./EditorHelper/ChangeHelper"),
9898
ErrorPopupHelper = require("./EditorHelper/ErrorPopupHelper"),
99-
InlineWidgetHelper = require("./EditorHelper/InlineWidgetHelper");
99+
InlineWidgetHelper = require("./EditorHelper/InlineWidgetHelper"),
100+
ScrollbarHelper = require("./EditorHelper/ScrollbarHelper");
100101

101102
/* Editor preferences */
102103

@@ -408,6 +409,9 @@ define(function (require, exports, module) {
408409
// Fail silently; drag image override is non-critical.
409410
}
410411

412+
// Click-to-jump on the native scrollbars (instead of slow viewport-at-a-time paging).
413+
ScrollbarHelper.installClickToJump(self);
414+
411415
// Can't get CodeMirror's focused state without searching for
412416
// CodeMirror-focused. Instead, track focus via onFocus and onBlur
413417
// options and track state with this._focused
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
* Original work Copyright (c) 2012 - 2021 Adobe Systems Incorporated. All rights reserved.
6+
*
7+
* This program is free software: you can redistribute it and/or modify it
8+
* under the terms of the GNU Affero General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
15+
* for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
19+
*
20+
*/
21+
22+
/**
23+
* Editor scrollbar behaviour helpers. Only to be used from Editor.js.
24+
*/
25+
26+
define(function (require, exports, module) {
27+
28+
/**
29+
* Install click-to-jump on an editor's native scrollbars: clicking an empty part of the track
30+
* jumps straight to that proportional position, instead of the browser default of paging one
31+
* viewport at a time (painfully slow to reach a far-off spot in a large file). A click on the
32+
* thumb is left to the native drag.
33+
*
34+
* CodeMirror's "native" scrollbars are real overflow:scroll <div>s (.CodeMirror-vscrollbar /
35+
* .CodeMirror-hscrollbar); setting their scroll offset syncs the editor, since CodeMirror listens
36+
* to their scroll event. Unlike most native scrollbars, this webview still delivers mousedown on
37+
* them, so we can intercept a track click.
38+
*
39+
* @param {!Editor} editor
40+
*/
41+
function installClickToJump(editor) {
42+
const cm = editor._codeMirror;
43+
const wrapper = cm && cm.getWrapperElement();
44+
if (!wrapper) {
45+
return;
46+
}
47+
48+
// Capture phase so we can suppress the native paging before it runs.
49+
wrapper.addEventListener("mousedown", function (e) {
50+
const el = e.target;
51+
if (e.button !== 0 || !el || !el.classList) {
52+
return;
53+
}
54+
let axis;
55+
if (el.classList.contains("CodeMirror-vscrollbar")) {
56+
axis = "v";
57+
} else if (el.classList.contains("CodeMirror-hscrollbar")) {
58+
axis = "h";
59+
} else {
60+
return;
61+
}
62+
63+
const rect = el.getBoundingClientRect();
64+
const view = (axis === "v") ? el.clientHeight : el.clientWidth; // visible track px
65+
const full = (axis === "v") ? el.scrollHeight : el.scrollWidth; // scrollable px
66+
if (full <= view) {
67+
return; // nothing to scroll
68+
}
69+
const cur = (axis === "v") ? el.scrollTop : el.scrollLeft;
70+
const thumbStart = (cur / full) * view;
71+
const thumbSize = (view / full) * view;
72+
const clickPos = (axis === "v") ? (e.clientY - rect.top) : (e.clientX - rect.left);
73+
if (clickPos >= thumbStart && clickPos <= thumbStart + thumbSize) {
74+
return; // on the thumb - let the native drag handle it
75+
}
76+
77+
// Track click: centre the thumb on the cursor and jump there immediately.
78+
let target = (clickPos / view) * full - view / 2;
79+
target = Math.max(0, Math.min(target, full - view));
80+
e.preventDefault();
81+
if (axis === "v") {
82+
el.scrollTop = target;
83+
} else {
84+
el.scrollLeft = target;
85+
}
86+
}, true);
87+
// No explicit removal: the wrapper element is removed on editor.destroy().
88+
}
89+
90+
exports.installClickToJump = installClickToJump;
91+
});

0 commit comments

Comments
 (0)