aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/GlobalKeyHandler.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/GlobalKeyHandler.ts')
-rw-r--r--src/client/views/GlobalKeyHandler.ts196
1 files changed, 109 insertions, 87 deletions
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 2f64ea28c..c73da35bc 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -8,11 +8,10 @@ import { Cast, PromiseValue } from '../../fields/Types';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DragManager } from '../util/DragManager';
import { GroupManager } from '../util/GroupManager';
-import { SelectionManager } from '../util/SelectionManager';
import { SettingsManager } from '../util/SettingsManager';
import { SharingManager } from '../util/SharingManager';
import { SnappingManager } from '../util/SnappingManager';
-import { UndoManager } from '../util/UndoManager';
+import { UndoManager, undoable } from '../util/UndoManager';
import { ContextMenu } from './ContextMenu';
import { DocumentDecorations } from './DocumentDecorations';
import { InkStrokeProperties } from './InkStrokeProperties';
@@ -21,24 +20,26 @@ import { MainView } from './MainView';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
+import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { OpenWhereMod } from './nodes/DocumentView';
+import { DocumentView } from './nodes/DocumentView';
+import { OpenWhereMod } from './nodes/OpenWhere';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { AnchorMenu } from './pdf/AnchorMenu';
const modifiers = ['control', 'meta', 'shift', 'alt'];
-type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo;
type KeyControlInfo = {
preventDefault: boolean;
stopPropagation: boolean;
};
-
-export let CtrlKey = false;
+type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo;
export class KeyManager {
- public static Instance: KeyManager = new KeyManager();
+ // eslint-disable-next-line no-use-before-define
+ public static Instance: KeyManager;
private router = new Map<string, KeyHandler>();
constructor() {
+ KeyManager.Instance = this;
const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0;
// SHIFT CONTROL ALT META
@@ -47,10 +48,20 @@ export class KeyManager {
this.router.set(isMac ? '0100' : '0010', this.alt);
this.router.set(isMac ? '1001' : '1100', this.ctrl_shift);
this.router.set('1000', this.shift);
+
+ window.removeEventListener('keydown', KeyManager.Instance.handleModifiers, true);
+ window.addEventListener('keydown', KeyManager.Instance.handleModifiers, true);
+ window.removeEventListener('keyup', KeyManager.Instance.unhandleModifiers);
+ window.addEventListener('keyup', KeyManager.Instance.unhandleModifiers);
+ window.removeEventListener('keydown', KeyManager.Instance.handle);
+ window.addEventListener('keydown', KeyManager.Instance.handle);
+ window.removeEventListener('keyup', KeyManager.Instance.unhandle);
+ window.addEventListener('keyup', KeyManager.Instance.unhandle);
+ window.addEventListener('paste', KeyManager.Instance.paste as any);
}
- public unhandle = action((e: KeyboardEvent) => {
- e.key === 'Control' && (CtrlKey = false);
+ public unhandle = action((/* e: KeyboardEvent */) => {
+ /* empty */
});
public handleModifiers = action((e: KeyboardEvent) => {
if (e.shiftKey) SnappingManager.SetShiftKey(true);
@@ -65,11 +76,9 @@ export class KeyManager {
public handle = action((e: KeyboardEvent) => {
// accumulate buffer of characters to insert in a new text note. once the note is created, it will stop keyboard events from reaching this function.
- if (FormattedTextBox.SelectOnLoadChar) FormattedTextBox.SelectOnLoadChar = FormattedTextBox.SelectOnLoadChar + (e.key === 'Enter' ? '\n' : e.key);
- e.key === 'Control' && (CtrlKey = true);
- //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
+ if (FormattedTextBox.SelectOnLoadChar) FormattedTextBox.SelectOnLoadChar += e.key === 'Enter' ? '\n' : e.key;
const keyname = e.key && e.key.toLowerCase();
- this.handleGreedy(keyname);
+ this.handleGreedy(/* keyname */);
if (modifiers.includes(keyname)) {
return;
@@ -89,14 +98,11 @@ export class KeyManager {
control.preventDefault && e.preventDefault();
});
- private handleGreedy = action((keyname: string) => {
- switch (keyname) {
- }
- });
+ private handleGreedy = action((/* keyname: string */) => {});
nudge = (x: number, y: number, label: string) => {
- const nudgeable = SelectionManager.Views.some(dv => dv.CollectionFreeFormDocumentView?.nudge);
- nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => dv.CollectionFreeFormDocumentView?.nudge(x, y)), label);
+ const nudgeable = DocumentView.Selected().some(dv => CollectionFreeFormDocumentView.from(dv)?.nudge);
+ nudgeable && UndoManager.RunInBatch(() => DocumentView.Selected().map(dv => CollectionFreeFormDocumentView.from(dv)?.nudge(x, y)), label);
return { stopPropagation: nudgeable, preventDefault: nudgeable };
};
@@ -104,67 +110,62 @@ export class KeyManager {
switch (keyname) {
case 'u':
if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
- const ungroupings = SelectionManager.Views;
- UndoManager.RunInBatch(() => ungroupings.map(dv => (dv.layoutDoc.group = undefined)), 'ungroup');
- SelectionManager.DeselectAll();
+ const ungroupings = DocumentView.Selected();
+ undoable(() => () => ungroupings.forEach(dv => { dv.layoutDoc.group = undefined; }), 'ungroup');
+ DocumentView.DeselectAll();
}
break;
case 'g':
if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
- const selected = SelectionManager.Views;
- const collectionView = selected.reduce((col, dv) => (col === null || dv.CollectionFreeFormView === col ? dv.CollectionFreeFormView : undefined), null as null | undefined | CollectionFreeFormView);
- if (collectionView) {
- UndoManager.RunInBatch(() =>
- collectionView._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs)
- , 'grouping');
- break;
- }
+ const selected = DocumentView.Selected();
+ const cv = selected.reduce((col, dv) => (!col || CollectionFreeFormView.from(dv) === col ? CollectionFreeFormView.from(dv) : undefined), undefined as undefined | CollectionFreeFormView);
+ cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, DocumentView.SelectedDocs()), 'grouping');
}
break;
case ' ':
// MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
break;
- case 'escape':
- DocumentLinksButton.StartLink = undefined;
- DocumentLinksButton.StartLinkView = undefined;
- InkStrokeProperties.Instance._controlButton = false;
- Doc.ActiveTool = InkTool.None;
- DragManager.CompleteWindowDrag?.(true);
- var doDeselect = true;
- if (SnappingManager.IsDragging) {
- DragManager.AbortDrag();
- }
- if (CollectionDockingView.Instance?.HasFullScreen) {
- CollectionDockingView.Instance?.CloseFullScreen();
- }
- if (CollectionStackedTimeline.SelectingRegions.size) {
- CollectionStackedTimeline.StopSelecting();
- doDeselect = false;
- } else {
- doDeselect = !ContextMenu.Instance.closeMenu();
- }
- if (doDeselect) {
- SelectionManager.DeselectAll();
- LightboxView.Instance.SetLightboxDoc(undefined);
+ case 'escape': {
+ DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ InkStrokeProperties.Instance._controlButton = false;
+ Doc.ActiveTool = InkTool.None;
+ DragManager.CompleteWindowDrag?.(true);
+ let doDeselect = true;
+ if (SnappingManager.IsDragging) {
+ DragManager.AbortDrag();
+ }
+ if (CollectionDockingView.Instance?.HasFullScreen) {
+ CollectionDockingView.Instance?.CloseFullScreen();
+ }
+ if (CollectionStackedTimeline.SelectingRegions.size) {
+ CollectionStackedTimeline.StopSelecting();
+ doDeselect = false;
+ } else {
+ doDeselect = !ContextMenu.Instance.closeMenu();
+ }
+ if (doDeselect) {
+ DocumentView.DeselectAll();
+ LightboxView.Instance.SetLightboxDoc(undefined);
+ }
+ // DictationManager.Controls.stop();
+ GoogleAuthenticationManager.Instance.cancel();
+ SharingManager.Instance.close();
+ if (!GroupManager.Instance.isOpen) SettingsManager.Instance.closeMgr();
+ GroupManager.Instance.close();
+ window.getSelection()?.empty();
+ document.body.focus();
}
- // DictationManager.Controls.stop();
- GoogleAuthenticationManager.Instance.cancel();
- SharingManager.Instance.close();
- if (!GroupManager.Instance.isOpen) SettingsManager.Instance.close();
- GroupManager.Instance.close();
- window.getSelection()?.empty();
- document.body.focus();
break;
- case 'enter': {
- //DocumentDecorations.Instance.onCloseClick(false);
+ case 'enter':
+ // DocumentDecorations.Instance.onCloseClick(false);
break;
- }
case 'delete':
case 'backspace':
if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
if (LightboxView.LightboxDoc) {
LightboxView.Instance.SetLightboxDoc(undefined);
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
} else if (!window.getSelection()?.toString()) DocumentDecorations.Instance.onCloseClick(true);
return { stopPropagation: true, preventDefault: true };
}
@@ -173,6 +174,7 @@ export class KeyManager {
case 'arrowright': return this.nudge(1,0, 'nudge right');
case 'arrowup': return this.nudge(0, -1, 'nudge up');
case 'arrowdown': return this.nudge(0, 1, 'nudge down');
+ default:
} // prettier-ignore
return {
@@ -189,17 +191,18 @@ export class KeyManager {
case 'arrowdown': return this.nudge(0, 10, 'nudge down');
case 'u' :
if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
- UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => (doc.group = undefined)), 'unggroup');
- SelectionManager.DeselectAll();
+ UndoManager.RunInBatch(() => DocumentView.Selected().map(dv => dv.Document).forEach(doc => {doc.group = undefined}), 'unggroup');
+ DocumentView.DeselectAll();
}
break;
case 'g':
if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
const randomGroup = random(0, 1000);
- UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => (doc.group = randomGroup)), 'group');
- SelectionManager.DeselectAll();
+ UndoManager.RunInBatch(() => DocumentView.Selected().forEach(dv => {dv.Document.group = randomGroup}), 'group');
+ DocumentView.DeselectAll();
}
break;
+ default:
} // prettier-ignore
return {
@@ -215,8 +218,9 @@ export class KeyManager {
switch (keyname) {
case 'ƒ':
case 'f':
- const dv = SelectionManager.Views?.[0];
- UndoManager.RunInBatch(() => dv.CollectionFreeFormDocumentView?.float(), 'float');
+ UndoManager.RunInBatch(() => CollectionFreeFormDocumentView.from(DocumentView.Selected()?.[0])?.float(), 'float');
+ break;
+ default:
}
return {
@@ -251,20 +255,24 @@ export class KeyManager {
PromiseValue(Cast(Doc.UserDoc()['tabs-button-tools'], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
break;
case 'i':
- const importBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImports);
- if (importBtn) {
- MainView.Instance.selectMenu(importBtn);
+ {
+ const importBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImports);
+ if (importBtn) {
+ MainView.Instance.selectMenu(importBtn);
+ }
}
break;
case 's':
- const trailsBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyTrails);
- if (trailsBtn) {
- MainView.Instance.selectMenu(trailsBtn);
+ {
+ const trailsBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyTrails);
+ if (trailsBtn) {
+ MainView.Instance.selectMenu(trailsBtn);
+ }
}
break;
case 'f':
- if (SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView?.search) {
- SelectionManager.Views[0].ComponentView?.search?.('', false, false);
+ if (DocumentView.Selected().length === 1 && DocumentView.Selected()[0].ComponentView?.search) {
+ DocumentView.Selected()[0].ComponentView?.search?.('', false, false);
} else {
const searchBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MySearcher);
if (searchBtn) {
@@ -283,14 +291,14 @@ export class KeyManager {
break;
case 'y':
if (Doc.ActivePage !== 'home') {
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
UndoManager.Redo();
}
stopPropagation = false;
break;
case 'z':
if (Doc.ActivePage !== 'home') {
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
UndoManager.Undo();
}
stopPropagation = false;
@@ -306,11 +314,17 @@ export class KeyManager {
preventDefault = false;
break;
case 'x':
- if (SelectionManager.Views.length) {
+ if (DocumentView.Selected().length) {
const bds = DocumentDecorations.Instance.Bounds;
- const pt = SelectionManager.Views[0].screenToViewTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
- const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views.map(dv => dv.Document[Id]).join(':');
- SelectionManager.Views.length && navigator.clipboard.writeText(text);
+ const pt = DocumentView.Selected()[0] //
+ .screenToViewTransform()
+ .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
+ const text =
+ `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + //
+ DocumentView.Selected()
+ .map(dv => dv.Document[Id])
+ .join(':'); // prettier-ignore
+ DocumentView.Selected().length && navigator.clipboard.writeText(text);
DocumentDecorations.Instance.onCloseClick(true);
stopPropagation = false;
preventDefault = false;
@@ -319,13 +333,20 @@ export class KeyManager {
case 'c':
if ((document.activeElement as any)?.type !== 'text' && !AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) {
const bds = DocumentDecorations.Instance.Bounds;
- const pt = SelectionManager.Views[0].screenToViewTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
- const text = `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views.map(dv => dv.Document[Id]).join(':');
- SelectionManager.Views.length && navigator.clipboard.writeText(text);
+ const pt = DocumentView.Selected()[0]
+ .screenToViewTransform()
+ .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); // prettier-ignore
+ const text =
+ `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` +
+ DocumentView.SelectedDocs()
+ .map(doc => doc[Id])
+ .join(':'); // prettier-ignore
+ DocumentView.Selected().length && navigator.clipboard.writeText(text);
stopPropagation = false;
}
preventDefault = false;
break;
+ default:
}
return {
@@ -339,7 +360,7 @@ export class KeyManager {
if (!plain) return;
const clone = plain.startsWith('__DashCloneId(');
const docids = plain.split(':'); // hack! docids[0] is the top left of the selection rectangle
- const addDocument = SelectionManager.Views.lastElement()?.ComponentView?.addDocument;
+ const addDocument = DocumentView.Selected().lastElement()?.ComponentView?.addDocument;
if (addDocument && (plain.startsWith('__DashDocId(') || clone)) {
Doc.Paste(docids.slice(1), clone, addDocument);
}
@@ -360,6 +381,7 @@ export class KeyManager {
case 'p':
Doc.ActiveTool = InkTool.Write;
break;
+ default:
}
return {