Merge pull request #15186 from calixteman/freetext_commit

[Editor] Move the keyboard manager at the container level
This commit is contained in:
calixteman 2022-07-20 12:38:31 +02:00 committed by GitHub
commit f18a27bee9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 105 deletions

View File

@ -21,8 +21,8 @@
/** @typedef {import("../../web/interfaces").IL10n} IL10n */ /** @typedef {import("../../web/interfaces").IL10n} IL10n */
import { AnnotationEditorType, shadow } from "../../shared/util.js"; import { AnnotationEditorType, shadow } from "../../shared/util.js";
import { bindEvents, KeyboardManager } from "./tools.js";
import { binarySearchFirstItem } from "../display_utils.js"; import { binarySearchFirstItem } from "../display_utils.js";
import { bindEvents } from "./tools.js";
import { FreeTextEditor } from "./freetext.js"; import { FreeTextEditor } from "./freetext.js";
import { InkEditor } from "./ink.js"; import { InkEditor } from "./ink.js";
@ -61,33 +61,6 @@ class AnnotationEditorLayer {
static _initialized = false; static _initialized = false;
static _keyboardManager = new KeyboardManager([
[["ctrl+a", "mac+meta+a"], AnnotationEditorLayer.prototype.selectAll],
[["ctrl+c", "mac+meta+c"], AnnotationEditorLayer.prototype.copy],
[["ctrl+v", "mac+meta+v"], AnnotationEditorLayer.prototype.paste],
[["ctrl+x", "mac+meta+x"], AnnotationEditorLayer.prototype.cut],
[["ctrl+z", "mac+meta+z"], AnnotationEditorLayer.prototype.undo],
[
["ctrl+y", "ctrl+shift+Z", "mac+meta+shift+Z"],
AnnotationEditorLayer.prototype.redo,
],
[
[
"Backspace",
"alt+Backspace",
"ctrl+Backspace",
"shift+Backspace",
"mac+Backspace",
"mac+alt+Backspace",
"mac+ctrl+Backspace",
"Delete",
"ctrl+Delete",
"shift+Delete",
],
AnnotationEditorLayer.prototype.delete,
],
]);
/** /**
* @param {AnnotationEditorLayerOptions} options * @param {AnnotationEditorLayerOptions} options
*/ */
@ -205,62 +178,6 @@ class AnnotationEditorLayer {
this.#uiManager.addCommands(params); this.#uiManager.addCommands(params);
} }
/**
* Undo the last command.
*/
undo() {
this.#uiManager.undo();
}
/**
* Redo the last command.
*/
redo() {
this.#uiManager.redo();
}
/**
* Suppress the selected editor or all editors.
*/
delete() {
this.#uiManager.delete();
}
/**
* Copy the selected editor.
*/
copy() {
this.#uiManager.copy();
}
/**
* Cut the selected editor.
*/
cut() {
this.#uiManager.cut();
}
/**
* Paste a previously copied editor.
*/
paste() {
this.#uiManager.paste();
}
/**
* Select all the editors.
*/
selectAll() {
this.#uiManager.selectAll();
}
/**
* Unselect all the editors.
*/
unselectAll() {
this.#uiManager.unselectAll();
}
/** /**
* Enable pointer events on the main div in order to enable * Enable pointer events on the main div in order to enable
* editor creation. * editor creation.
@ -299,7 +216,7 @@ class AnnotationEditorLayer {
} }
if (editor) { if (editor) {
this.unselectAll(); this.#uiManager.unselectAll();
} }
} }
@ -691,16 +608,6 @@ class AnnotationEditorLayer {
event.preventDefault(); event.preventDefault();
} }
/**
* Keydown callback.
* @param {KeyboardEvent} event
*/
keydown(event) {
if (!this.#uiManager.getActive()?.shouldGetKeyboardEvents()) {
AnnotationEditorLayer._keyboardManager.exec(this, event);
}
}
/** /**
* Destroy the main editor. * Destroy the main editor.
*/ */
@ -741,7 +648,7 @@ class AnnotationEditorLayer {
*/ */
render(parameters) { render(parameters) {
this.viewport = parameters.viewport; this.viewport = parameters.viewport;
bindEvents(this, this.div, ["dragover", "drop", "keydown"]); bindEvents(this, this.div, ["dragover", "drop"]);
this.setDimensions(); this.setDimensions();
for (const editor of this.#uiManager.getEditors(this.pageIndex)) { for (const editor of this.#uiManager.getEditors(this.pageIndex)) {
this.add(editor); this.add(editor);

View File

@ -23,13 +23,15 @@ import {
LINE_FACTOR, LINE_FACTOR,
Util, Util,
} from "../../shared/util.js"; } from "../../shared/util.js";
import { bindEvents, KeyboardManager } from "./tools.js";
import { AnnotationEditor } from "./editor.js"; import { AnnotationEditor } from "./editor.js";
import { bindEvents } from "./tools.js";
/** /**
* Basic text editor in order to create a FreeTex annotation. * Basic text editor in order to create a FreeTex annotation.
*/ */
class FreeTextEditor extends AnnotationEditor { class FreeTextEditor extends AnnotationEditor {
#boundEditorDivKeydown = this.editorDivKeydown.bind(this);
#color; #color;
#content = ""; #content = "";
@ -50,6 +52,13 @@ class FreeTextEditor extends AnnotationEditor {
static _defaultFontSize = 10; static _defaultFontSize = 10;
static _keyboardManager = new KeyboardManager([
[
["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"],
FreeTextEditor.prototype.commitOrRemove,
],
]);
constructor(params) { constructor(params) {
super({ ...params, name: "freeTextEditor" }); super({ ...params, name: "freeTextEditor" });
this.#color = this.#color =
@ -210,6 +219,7 @@ class FreeTextEditor extends AnnotationEditor {
this.editorDiv.contentEditable = true; this.editorDiv.contentEditable = true;
this.div.draggable = false; this.div.draggable = false;
this.div.removeAttribute("tabIndex"); this.div.removeAttribute("tabIndex");
this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown);
} }
/** @inheritdoc */ /** @inheritdoc */
@ -220,6 +230,7 @@ class FreeTextEditor extends AnnotationEditor {
this.editorDiv.contentEditable = false; this.editorDiv.contentEditable = false;
this.div.draggable = true; this.div.draggable = true;
this.div.tabIndex = 0; this.div.tabIndex = 0;
this.editorDiv.removeEventListener("keydown", this.#boundEditorDivKeydown);
} }
/** @inheritdoc */ /** @inheritdoc */
@ -311,13 +322,17 @@ class FreeTextEditor extends AnnotationEditor {
* onkeydown callback. * onkeydown callback.
* @param {MouseEvent} event * @param {MouseEvent} event
*/ */
keyup(event) { keydown(event) {
if (event.key === "Enter") { if (event.target === this.div && event.key === "Enter") {
this.enableEditMode(); this.enableEditMode();
this.editorDiv.focus(); this.editorDiv.focus();
} }
} }
editorDivKeydown(event) {
FreeTextEditor._keyboardManager.exec(this, event);
}
/** @inheritdoc */ /** @inheritdoc */
disableEditing() { disableEditing() {
this.editorDiv.setAttribute("role", "comment"); this.editorDiv.setAttribute("role", "comment");
@ -376,7 +391,7 @@ class FreeTextEditor extends AnnotationEditor {
// TODO: implement paste callback. // TODO: implement paste callback.
// The goal is to sanitize and have something suitable for this // The goal is to sanitize and have something suitable for this
// editor. // editor.
bindEvents(this, this.div, ["dblclick", "keyup"]); bindEvents(this, this.div, ["dblclick", "keydown"]);
if (this.width) { if (this.width) {
// This editor was created in using copy (ctrl+c). // This editor was created in using copy (ctrl+c).

View File

@ -261,12 +261,12 @@ class KeyboardManager {
/** /**
* Execute a callback, if any, for a given keyboard event. * Execute a callback, if any, for a given keyboard event.
* The page is used as `this` in the callback. * The self is used as `this` in the callback.
* @param {AnnotationEditorLayer} page. * @param {Object} self.
* @param {KeyboardEvent} event * @param {KeyboardEvent} event
* @returns * @returns
*/ */
exec(page, event) { exec(self, event) {
if (!this.allKeys.has(event.key)) { if (!this.allKeys.has(event.key)) {
return; return;
} }
@ -274,7 +274,7 @@ class KeyboardManager {
if (!callback) { if (!callback) {
return; return;
} }
callback.bind(page)(); callback.bind(self)();
event.preventDefault(); event.preventDefault();
} }
} }
@ -422,6 +422,8 @@ class AnnotationEditorUIManager {
#previousActiveEditor = null; #previousActiveEditor = null;
#boundKeydown = this.keydown.bind(this);
#boundOnEditingAction = this.onEditingAction.bind(this); #boundOnEditingAction = this.onEditingAction.bind(this);
#boundOnPageChanging = this.onPageChanging.bind(this); #boundOnPageChanging = this.onPageChanging.bind(this);
@ -437,7 +439,37 @@ class AnnotationEditorUIManager {
hasSelectedEditor: false, hasSelectedEditor: false,
}; };
constructor(eventBus) { #container = null;
static _keyboardManager = new KeyboardManager([
[["ctrl+a", "mac+meta+a"], AnnotationEditorUIManager.prototype.selectAll],
[["ctrl+c", "mac+meta+c"], AnnotationEditorUIManager.prototype.copy],
[["ctrl+v", "mac+meta+v"], AnnotationEditorUIManager.prototype.paste],
[["ctrl+x", "mac+meta+x"], AnnotationEditorUIManager.prototype.cut],
[["ctrl+z", "mac+meta+z"], AnnotationEditorUIManager.prototype.undo],
[
["ctrl+y", "ctrl+shift+Z", "mac+meta+shift+Z"],
AnnotationEditorUIManager.prototype.redo,
],
[
[
"Backspace",
"alt+Backspace",
"ctrl+Backspace",
"shift+Backspace",
"mac+Backspace",
"mac+alt+Backspace",
"mac+ctrl+Backspace",
"Delete",
"ctrl+Delete",
"shift+Delete",
],
AnnotationEditorUIManager.prototype.delete,
],
]);
constructor(container, eventBus) {
this.#container = container;
this.#eventBus = eventBus; this.#eventBus = eventBus;
this.#eventBus._on("editingaction", this.#boundOnEditingAction); this.#eventBus._on("editingaction", this.#boundOnEditingAction);
this.#eventBus._on("pagechanging", this.#boundOnPageChanging); this.#eventBus._on("pagechanging", this.#boundOnPageChanging);
@ -445,6 +477,7 @@ class AnnotationEditorUIManager {
} }
destroy() { destroy() {
this.#removeKeyboardManager();
this.#eventBus._off("editingaction", this.#boundOnEditingAction); this.#eventBus._off("editingaction", this.#boundOnEditingAction);
this.#eventBus._off("pagechanging", this.#boundOnPageChanging); this.#eventBus._off("pagechanging", this.#boundOnPageChanging);
this.#eventBus._off("textlayerrendered", this.#boundOnTextLayerRendered); this.#eventBus._off("textlayerrendered", this.#boundOnTextLayerRendered);
@ -468,6 +501,26 @@ class AnnotationEditorUIManager {
layer?.onTextLayerRendered(); layer?.onTextLayerRendered();
} }
#addKeyboardManager() {
// The keyboard events are caught at the container level in order to be able
// to execute some callbacks even if the current page doesn't have focus.
this.#container.addEventListener("keydown", this.#boundKeydown);
}
#removeKeyboardManager() {
this.#container.removeEventListener("keydown", this.#boundKeydown);
}
/**
* Keydown callback.
* @param {KeyboardEvent} event
*/
keydown(event) {
if (!this.getActive()?.shouldGetKeyboardEvents()) {
AnnotationEditorUIManager._keyboardManager.exec(this, event);
}
}
/** /**
* Execute an action for a given name. * Execute an action for a given name.
* For example, the user can click on the "Undo" entry in the context menu * For example, the user can click on the "Undo" entry in the context menu
@ -517,6 +570,7 @@ class AnnotationEditorUIManager {
*/ */
setEditingState(isEditing) { setEditingState(isEditing) {
if (isEditing) { if (isEditing) {
this.#addKeyboardManager();
this.#dispatchUpdateStates({ this.#dispatchUpdateStates({
isEditing: this.#mode !== AnnotationEditorType.NONE, isEditing: this.#mode !== AnnotationEditorType.NONE,
isEmpty: this.#isEmpty(), isEmpty: this.#isEmpty(),
@ -526,6 +580,7 @@ class AnnotationEditorUIManager {
hasEmptyClipboard: this.#clipboardManager.isEmpty(), hasEmptyClipboard: this.#clipboardManager.isEmpty(),
}); });
} else { } else {
this.#removeKeyboardManager();
this.#dispatchUpdateStates({ this.#dispatchUpdateStates({
isEditing: false, isEditing: false,
}); });

View File

@ -734,6 +734,7 @@ class BaseViewer {
}); });
this.#annotationEditorUIManager = new AnnotationEditorUIManager( this.#annotationEditorUIManager = new AnnotationEditorUIManager(
this.container,
this.eventBus this.eventBus
); );
if (mode !== AnnotationEditorType.NONE) { if (mode !== AnnotationEditorType.NONE) {