Merge pull request #15186 from calixteman/freetext_commit
[Editor] Move the keyboard manager at the container level
This commit is contained in:
commit
f18a27bee9
@ -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);
|
||||||
|
@ -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).
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user