diff options
author | Sam Wilkins <samuel_wilkins@brown.edu> | 2019-06-28 21:47:26 -0400 |
---|---|---|
committer | Sam Wilkins <samuel_wilkins@brown.edu> | 2019-06-28 21:47:26 -0400 |
commit | 79f301a2f74e88f1cf59064de320c199b5154827 (patch) | |
tree | 172edf11a799425c0aeaeb445ef8e6296f8c6eb2 /src | |
parent | eab5284d6982b6fa1ac796a8dbe8ac524298cb66 (diff) |
implemented global key handler
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/SelectionManager.ts | 5 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 141 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 62 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 20 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 4 |
5 files changed, 176 insertions, 56 deletions
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 7dbb81e76..3bc71ad42 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,11 +1,13 @@ -import { observable, action, runInAction } from "mobx"; +import { observable, action, runInAction, IReactionDisposer, reaction, autorun } from "mobx"; import { Doc } from "../../new_fields/Doc"; import { DocumentView } from "../views/nodes/DocumentView"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; import { NumCast } from "../../new_fields/Types"; export namespace SelectionManager { + class Manager { + @observable IsDragging: boolean = false; @observable SelectedDocuments: Array<DocumentView> = []; @@ -18,6 +20,7 @@ export namespace SelectionManager { } manager.SelectedDocuments.push(docView); + // console.log(manager.SelectedDocuments); docView.props.whenActiveChanged(true); } } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts new file mode 100644 index 000000000..bb10f27cf --- /dev/null +++ b/src/client/views/GlobalKeyHandler.ts @@ -0,0 +1,141 @@ +import { UndoManager } from "../util/UndoManager"; +import { SelectionManager } from "../util/SelectionManager"; +import { CollectionDockingView } from "./collections/CollectionDockingView"; +import { MainView } from "./MainView"; +import { DragManager } from "../util/DragManager"; +import { action } from "mobx"; + +const modifiers = ["Control", "Meta", "Shift", "Alt"]; +type KeyHandler = (keycode: string) => KeyControlInfo; +type KeyControlInfo = { + preventDefault: boolean, + stopPropagation: boolean +}; + +export default class KeyManager { + public static Handler: KeyManager; + private mainView: MainView; + private router = new Map<string, KeyHandler>(); + + constructor(mainView: MainView) { + this.mainView = mainView; + this.router.set("0000", this.unmodified); + this.router.set("0100", this.ctrl); + this.router.set("0010", this.alt); + this.router.set("1100", this.ctrl_shift); + } + + public handle = (e: KeyboardEvent) => { + let keyname = e.key.toLowerCase(); + this.handleGreedy(keyname); + + if (modifiers.includes(keyname)) { + return; + } + + let bit = (value: boolean) => value ? "1" : "0"; + let modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey); + + let handleConstrained = this.router.get(modifierIndex); + if (!handleConstrained) { + return; + } + + let control = handleConstrained(keyname); + + control.stopPropagation && e.stopPropagation(); + control.preventDefault && e.preventDefault(); + } + + private handleGreedy = action((keyname: string) => { + switch (keyname) { + } + }); + + private unmodified = action((keyname: string) => { + switch (keyname) { + case "escape": + if (CollectionDockingView.Instance.HasFullScreen()) { + CollectionDockingView.Instance.CloseFullScreen(); + } else { + SelectionManager.DeselectAll(); + } + DragManager.AbortDrag(); + break; + } + + return { + stopPropagation: false, + preventDefault: false + }; + }); + + private alt = action((keyname: string) => { + let stopPropagation = true; + let preventDefault = true; + + switch (keyname) { + case "n": + let toggle = this.mainView.addMenuToggle.current!; + toggle.checked = !toggle.checked; + break; + } + + return { + stopPropagation: stopPropagation, + preventDefault: preventDefault + }; + }); + + private ctrl = action((keyname: string) => { + let stopPropagation = true; + let preventDefault = true; + + switch (keyname) { + case "arrowright": + this.mainView.mainFreeform && CollectionDockingView.Instance.AddRightSplit(this.mainView.mainFreeform, undefined); + break; + case "arrowleft": + this.mainView.mainFreeform && CollectionDockingView.Instance.CloseRightSplit(this.mainView.mainFreeform); + break; + case "f": + this.mainView.isSearchVisible = !this.mainView.isSearchVisible; + break; + case "o": + let target = SelectionManager.SelectedDocuments()[0]; + target && target.fullScreenClicked(); + break; + case "r": + preventDefault = false; + break; + case "y": + UndoManager.Redo(); + break; + case "z": + UndoManager.Undo(); + break; + } + + return { + stopPropagation: stopPropagation, + preventDefault: preventDefault + }; + }); + + private ctrl_shift = action((keyname: string) => { + let stopPropagation = true; + let preventDefault = true; + + switch (keyname) { + case "z": + UndoManager.Redo(); + break; + } + + return { + stopPropagation: stopPropagation, + preventDefault: preventDefault + }; + }); + +}
\ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6290d8985..157512aa0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -36,18 +36,20 @@ import { CollectionBaseView } from './collections/CollectionBaseView'; import { List } from '../../new_fields/List'; import PDFMenu from './pdf/PDFMenu'; import { InkTool } from '../../new_fields/InkField'; -import * as _ from "lodash"; +import _ from "lodash"; +import KeyManager from './GlobalKeyHandler'; @observer export class MainView extends React.Component { public static Instance: MainView; + @observable addMenuToggle = React.createRef<HTMLInputElement>(); @observable private _workspacesShown: boolean = false; @observable public pwidth: number = 0; @observable public pheight: number = 0; @computed private get mainContainer(): Opt<Doc> { return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); } - @computed private get mainFreeform(): Opt<Doc> { + @computed get mainFreeform(): Opt<Doc> { let docs = DocListCast(this.mainContainer!.data); return (docs && docs.length > 1) ? docs[1] : undefined; } @@ -64,12 +66,13 @@ export class MainView extends React.Component { } componentWillMount() { - document.removeEventListener("keydown", this.globalKeyHandler); - document.addEventListener("keydown", this.globalKeyHandler); + KeyManager.Handler = new KeyManager(this); + document.removeEventListener("keydown", KeyManager.Handler.handle); + document.addEventListener("keydown", KeyManager.Handler.handle); } componentWillUnMount() { - document.removeEventListener("keydown", this.globalKeyHandler); + document.removeEventListener("keydown", KeyManager.Handler.handle); } constructor(props: Readonly<{}>) { @@ -122,18 +125,6 @@ export class MainView extends React.Component { // window.addEventListener("pointermove", (e) => this.reportLocation(e)) window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler - window.addEventListener("keydown", (e) => { - if (e.key === "Escape") { - DragManager.AbortDrag(); - SelectionManager.DeselectAll(); - } else if (e.key === "z" && e.ctrlKey) { - e.preventDefault(); - UndoManager.Undo(); - } else if ((e.key === "y" && e.ctrlKey) || (e.key === "z" && e.ctrlKey && e.shiftKey)) { - e.preventDefault(); - UndoManager.Redo(); - } - }, false); // drag event handler // click interactions for the context menu document.addEventListener("pointerdown", action(function (e: PointerEvent) { @@ -292,7 +283,7 @@ export class MainView extends React.Component { ]; return < div id="add-nodes-menu" > - <input type="checkbox" id="add-menu-toggle" /> + <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} /> <label htmlFor="add-menu-toggle" title="Add Node"><p>+</p></label> <div id="add-options-content"> @@ -300,8 +291,7 @@ export class MainView extends React.Component { <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> <li key="undo"><button className="add-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li> <li key="redo"><button className="add-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li> - <li key="color"><button className="add-button round-button" title="Redo" onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} > - + <li key="color"><button className="add-button round-button" title="Select Color" onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} > <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}> <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} /> </div> @@ -362,38 +352,6 @@ export class MainView extends React.Component { this.isSearchVisible = !this.isSearchVisible; } - @action - globalKeyHandler = (e: KeyboardEvent) => { - if (e.key === "Control" || !e.ctrlKey) return; - - if (e.key === "v") return; - if (e.key === "c") return; - - e.preventDefault(); - e.stopPropagation(); - - switch (e.key) { - case "ArrowRight": - if (this.mainFreeform) { - CollectionDockingView.Instance.AddRightSplit(this.mainFreeform, undefined); - } - break; - case "ArrowLeft": - if (this.mainFreeform) { - CollectionDockingView.Instance.CloseRightSplit(this.mainFreeform); - } - break; - case "o": - this.globalDisplayFlags.jumpToVisible = true; - break; - case "escape": - _.mapValues(this.globalDisplayFlags, () => false); - break; - case "f": - this.isSearchVisible = !this.isSearchVisible; - } - } - render() { return ( diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6564c38a0..71093df85 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -52,6 +52,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp private _flush: boolean = false; private _ignoreStateChange = ""; private _isPointerDown = false; + private _maximizedSrc: Opt<DocumentView>; constructor(props: SubCollectionViewProps) { super(props); @@ -71,7 +72,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } @action - public OpenFullScreen(document: Doc, dataDoc: Doc) { + public OpenFullScreen(docView: DocumentView) { + let document = Doc.MakeAlias(docView.props.Document); + let dataDoc = docView.dataDoc; let newItemStackConfig = { type: 'stack', content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)] @@ -80,10 +83,25 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp this._goldenLayout.root.contentItems[0].addChild(docconfig); docconfig.callDownwards('_$init'); this._goldenLayout._$maximiseItem(docconfig); + this._maximizedSrc = docView; this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); this.stateChanged(); } + public CloseFullScreen = () => { + let target = this._goldenLayout._maximisedItem; + if (target !== null && this._maximizedSrc) { + this._goldenLayout._maximisedItem.remove(); + SelectionManager.SelectDoc(this._maximizedSrc, false); + this._maximizedSrc = undefined; + this.stateChanged(); + } + } + + public HasFullScreen = () => { + return this._goldenLayout._maximisedItem !== null; + } + @undoBatch @action public CloseRightSplit = (document: Doc): boolean => { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4ac43ef4d..473e79023 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -406,8 +406,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu doc.nativeWidth = doc.nativeHeight = undefined; } } - fullScreenClicked = (): void => { - CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.dataDoc); + public fullScreenClicked = (): void => { + CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this); SelectionManager.DeselectAll(); } |