add upDownKeyJumps option, remove left/right
This commit is contained in:
parent
b22bbaaec0
commit
79dff2775b
|
@ -186,6 +186,10 @@
|
||||||
"message": "Theme",
|
"message": "Theme",
|
||||||
"description": "Label for the style editor's CSS theme."
|
"description": "Label for the style editor's CSS theme."
|
||||||
},
|
},
|
||||||
|
"cm_arrowKeysTraverse": {
|
||||||
|
"message": "Arrow keys ↑↓ traverse sections",
|
||||||
|
"description": "Label for the option in the editor."
|
||||||
|
},
|
||||||
"colorpickerPaletteHint": {
|
"colorpickerPaletteHint": {
|
||||||
"message": "Right-click a swatch to cycle through its source lines"
|
"message": "Right-click a swatch to cycle through its source lines"
|
||||||
},
|
},
|
||||||
|
|
|
@ -338,6 +338,12 @@
|
||||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="option sectioned-only">
|
||||||
|
<label i18n="cm_arrowKeysTraverse">
|
||||||
|
<input id="editor.arrowKeysTraverse" type="checkbox">
|
||||||
|
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="option">
|
<div class="option">
|
||||||
<label i18n="cm_colorpicker">
|
<label i18n="cm_colorpicker">
|
||||||
<input id="editor.colorpicker" type="checkbox">
|
<input id="editor.colorpicker" type="checkbox">
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
k.slice('editor.'.length);
|
k.slice('editor.'.length);
|
||||||
const prefKeys = prefs.knownKeys.filter(k =>
|
const prefKeys = prefs.knownKeys.filter(k =>
|
||||||
k !== 'editor.colorpicker' && // handled in colorpicker-helper.js
|
k !== 'editor.colorpicker' && // handled in colorpicker-helper.js
|
||||||
|
k !== 'editor.arrowKeysTraverse' && // handled in sections-editor.js
|
||||||
prefToCmOpt(k) in CodeMirror.defaults);
|
prefToCmOpt(k) in CodeMirror.defaults);
|
||||||
const {insertTab, insertSoftTab} = CodeMirror.commands;
|
const {insertTab, insertSoftTab} = CodeMirror.commands;
|
||||||
|
|
||||||
|
|
|
@ -75,8 +75,8 @@ html:not(.is-new-style) #heading::before {
|
||||||
right: 24px;
|
right: 24px;
|
||||||
}
|
}
|
||||||
/************ checkbox & select************/
|
/************ checkbox & select************/
|
||||||
.options-column > div[class="option"] {
|
.options-column > .option {
|
||||||
margin-bottom: 4px;
|
margin-bottom: .25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.options-column > .usercss-only {
|
.options-column > .usercss-only {
|
||||||
|
@ -107,6 +107,8 @@ label {
|
||||||
}
|
}
|
||||||
#sections {
|
#sections {
|
||||||
padding-left: var(--header-width);
|
padding-left: var(--header-width);
|
||||||
|
}
|
||||||
|
.usercss #sections {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ function createSection(originalSection, genId, si) {
|
||||||
value: originalSection.code,
|
value: originalSection.code,
|
||||||
});
|
});
|
||||||
el.CodeMirror = cm; // used by getAssociatedEditor
|
el.CodeMirror = cm; // used by getAssociatedEditor
|
||||||
|
cm.el = el;
|
||||||
editor.applyScrollInfo(cm, si);
|
editor.applyScrollInfo(cm, si);
|
||||||
|
|
||||||
const changeListeners = new Set();
|
const changeListeners = new Set();
|
||||||
|
@ -114,49 +115,10 @@ function createSection(originalSection, genId, si) {
|
||||||
changeGeneration = newGeneration;
|
changeGeneration = newGeneration;
|
||||||
emitSectionChange('code');
|
emitSectionChange('code');
|
||||||
});
|
});
|
||||||
cm.display.wrapper.on('keydown', event => handleKeydown(cm, event), true);
|
|
||||||
$('.test-regexp', el).onclick = () => updateRegexpTester(true);
|
$('.test-regexp', el).onclick = () => updateRegexpTester(true);
|
||||||
initBeautifyButton($('.beautify-section', el), [cm]);
|
initBeautifyButton($('.beautify-section', el), [cm]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(cm, event) {
|
|
||||||
if (event.shiftKey || event.altKey || event.metaKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const {key} = event;
|
|
||||||
const {line, ch} = cm.getCursor();
|
|
||||||
switch (key) {
|
|
||||||
case 'ArrowLeft':
|
|
||||||
if (line || ch) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// fallthrough
|
|
||||||
case 'ArrowUp':
|
|
||||||
cm = line === 0 && editor.prevEditor(cm, false);
|
|
||||||
if (!cm) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
cm.setCursor(cm.doc.size - 1, key === 'ArrowLeft' ? 1e20 : ch);
|
|
||||||
break;
|
|
||||||
case 'ArrowRight':
|
|
||||||
if (line < cm.doc.size - 1 || ch < cm.getLine(line).length - 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// fallthrough
|
|
||||||
case 'ArrowDown':
|
|
||||||
cm = line === cm.doc.size - 1 && editor.nextEditor(cm, false);
|
|
||||||
if (!cm) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
cm.setCursor(0, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateRegexpTester(toggle) {
|
async function updateRegexpTester(toggle) {
|
||||||
const isLoaded = typeof regexpTester === 'object' ||
|
const isLoaded = typeof regexpTester === 'object' ||
|
||||||
toggle && await require(['/edit/regexp-tester']); /* global regexpTester */
|
toggle && await require(['/edit/regexp-tester']); /* global regexpTester */
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
/* global createSection */// sections-editor-section.js
|
/* global createSection */// sections-editor-section.js
|
||||||
/* global editor */
|
/* global editor */
|
||||||
/* global linterMan */
|
/* global linterMan */
|
||||||
|
/* global prefs */
|
||||||
/* global styleSectionsEqual */ // sections-util.js
|
/* global styleSectionsEqual */ // sections-util.js
|
||||||
/* global t */// localization.js
|
/* global t */// localization.js
|
||||||
'use strict';
|
'use strict';
|
||||||
|
@ -21,6 +22,7 @@ function SectionsEditor() {
|
||||||
let sectionOrder = '';
|
let sectionOrder = '';
|
||||||
let headerOffset; // in compact mode the header is at the top so it reduces the available height
|
let headerOffset; // in compact mode the header is at the top so it reduces the available height
|
||||||
let cmExtrasHeight; // resize grip + borders
|
let cmExtrasHeight; // resize grip + borders
|
||||||
|
let upDownJumps;
|
||||||
|
|
||||||
updateMeta();
|
updateMeta();
|
||||||
rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror
|
rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror
|
||||||
|
@ -30,6 +32,10 @@ function SectionsEditor() {
|
||||||
$('#from-mozilla').on('click', () => showMozillaFormatImport());
|
$('#from-mozilla').on('click', () => showMozillaFormatImport());
|
||||||
document.on('wheel', scrollEntirePageOnCtrlShift, {passive: false});
|
document.on('wheel', scrollEntirePageOnCtrlShift, {passive: false});
|
||||||
CodeMirror.defaults.extraKeys['Shift-Ctrl-Wheel'] = 'scrollWindow';
|
CodeMirror.defaults.extraKeys['Shift-Ctrl-Wheel'] = 'scrollWindow';
|
||||||
|
prefs.subscribe('editor.arrowKeysTraverse', (_, val) => {
|
||||||
|
for (const {cm} of sections) handleKeydownSetup(cm, val);
|
||||||
|
upDownJumps = val;
|
||||||
|
}, {runNow: true});
|
||||||
|
|
||||||
/** @namespace Editor */
|
/** @namespace Editor */
|
||||||
Object.assign(editor, {
|
Object.assign(editor, {
|
||||||
|
@ -70,15 +76,15 @@ function SectionsEditor() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
nextEditor(cm, cycle = true) {
|
nextEditor(cm, upDown) {
|
||||||
return cycle || cm !== findLast(sections, s => !s.removed).cm
|
return !upDown || cm !== findLast(sections, s => !s.removed).cm
|
||||||
? nextPrevEditor(cm, 1)
|
? nextPrevEditor(cm, 1, upDown)
|
||||||
: null;
|
: null;
|
||||||
},
|
},
|
||||||
|
|
||||||
prevEditor(cm, cycle = true) {
|
prevEditor(cm, upDown) {
|
||||||
return cycle || cm !== sections.find(s => !s.removed).cm
|
return !upDown || cm !== sections.find(s => !s.removed).cm
|
||||||
? nextPrevEditor(cm, -1)
|
? nextPrevEditor(cm, -1, upDown)
|
||||||
: null;
|
: null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -112,14 +118,16 @@ function SectionsEditor() {
|
||||||
editor.useSavedStyle(newStyle);
|
editor.useSavedStyle(newStyle);
|
||||||
},
|
},
|
||||||
|
|
||||||
scrollToEditor(cm) {
|
scrollToEditor(cm, partial) {
|
||||||
const {el} = sections.find(s => s.cm === cm);
|
const cc = partial && cm.cursorCoords(true, 'window');
|
||||||
const r = el.getBoundingClientRect();
|
const {top: y1, bottom: y2} = cm.el.getBoundingClientRect();
|
||||||
const h = window.innerHeight;
|
const rc = container.getBoundingClientRect();
|
||||||
if (r.bottom > h && r.top > 0 ||
|
const rcY1 = Math.max(rc.top, 0);
|
||||||
r.bottom < h && r.top < 0) {
|
const rcY2 = Math.min(rc.bottom, innerHeight);
|
||||||
window.scrollBy(0, (r.top + r.bottom - h) / 2 | 0);
|
const bad = partial
|
||||||
}
|
? cc.top < rcY1 || cc.top > rcY2 - 30
|
||||||
|
: y1 >= rcY1 ^ y2 <= rcY2;
|
||||||
|
if (bad) window.scrollBy(0, (y1 + y2 - rcY2 + rcY1) / 2 | 0);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -291,10 +299,36 @@ function SectionsEditor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextPrevEditor(cm, direction) {
|
function handleKeydown(event) {
|
||||||
|
if (event.shiftKey || event.altKey || event.metaKey ||
|
||||||
|
event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let pos;
|
||||||
|
let cm = this.CodeMirror;
|
||||||
|
const {line, ch} = cm.getCursor();
|
||||||
|
if (event.key === 'ArrowUp') {
|
||||||
|
cm = line === 0 && editor.prevEditor(cm, true);
|
||||||
|
pos = cm && [cm.doc.size - 1, ch];
|
||||||
|
} else {
|
||||||
|
cm = line === cm.doc.size - 1 && editor.nextEditor(cm, true);
|
||||||
|
pos = cm && [0, 0];
|
||||||
|
}
|
||||||
|
if (cm) {
|
||||||
|
cm.setCursor(...pos);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeydownSetup(cm, state) {
|
||||||
|
cm.display.wrapper[state ? 'on' : 'off']('keydown', handleKeydown, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextPrevEditor(cm, direction, upDown) {
|
||||||
const editors = editor.getEditors();
|
const editors = editor.getEditors();
|
||||||
cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length];
|
cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length];
|
||||||
editor.scrollToEditor(cm);
|
editor.scrollToEditor(cm, upDown);
|
||||||
cm.focus();
|
cm.focus();
|
||||||
return cm;
|
return cm;
|
||||||
}
|
}
|
||||||
|
@ -577,6 +611,9 @@ function SectionsEditor() {
|
||||||
cm.focus();
|
cm.focus();
|
||||||
editor.scrollToEditor(cm);
|
editor.scrollToEditor(cm);
|
||||||
}
|
}
|
||||||
|
if (upDownJumps) {
|
||||||
|
handleKeydownSetup(cm, true);
|
||||||
|
}
|
||||||
updateSectionOrder();
|
updateSectionOrder();
|
||||||
updateLivePreview();
|
updateLivePreview();
|
||||||
section.onChange(updateLivePreview);
|
section.onChange(updateLivePreview);
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
// "Delete" item in context menu for browsers that don't have it
|
// "Delete" item in context menu for browsers that don't have it
|
||||||
'editor.contextDelete': false,
|
'editor.contextDelete': false,
|
||||||
'editor.selectByTokens': true,
|
'editor.selectByTokens': true,
|
||||||
|
'editor.arrowKeysTraverse': true,
|
||||||
'editor.appliesToLineWidget': true, // show applies-to line widget on the editor
|
'editor.appliesToLineWidget': true, // show applies-to line widget on the editor
|
||||||
'editor.autosaveDraft': 10, // seconds
|
'editor.autosaveDraft': 10, // seconds
|
||||||
'editor.livePreview': true,
|
'editor.livePreview': true,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user