Merge pull request #15163 from calixteman/prepare_touch

[Editor] Always have an ink editor (when in ink mode)
This commit is contained in:
calixteman 2022-07-12 19:35:32 +02:00 committed by GitHub
commit 1301b71b7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 97 additions and 53 deletions

View File

@ -146,7 +146,11 @@ class AnnotationStorage {
const clone = new Map(); const clone = new Map();
for (const [key, val] of this._storage) { for (const [key, val] of this._storage) {
clone.set(key, val instanceof AnnotationEditor ? val.serialize() : val); const serialized =
val instanceof AnnotationEditor ? val.serialize() : val;
if (serialized) {
clone.set(key, serialized);
}
} }
return clone; return clone;
} }

View File

@ -42,10 +42,10 @@ import { InkEditor } from "./ink.js";
class AnnotationEditorLayer { class AnnotationEditorLayer {
#boundClick; #boundClick;
#boundMouseover;
#editors = new Map(); #editors = new Map();
#isCleaningUp = false;
#uiManager; #uiManager;
static _initialized = false; static _initialized = false;
@ -92,7 +92,6 @@ class AnnotationEditorLayer {
this.pageIndex = options.pageIndex; this.pageIndex = options.pageIndex;
this.div = options.div; this.div = options.div;
this.#boundClick = this.click.bind(this); this.#boundClick = this.click.bind(this);
this.#boundMouseover = this.mouseover.bind(this);
for (const editor of this.#uiManager.getEditors(options.pageIndex)) { for (const editor of this.#uiManager.getEditors(options.pageIndex)) {
this.add(editor); this.add(editor);
@ -114,24 +113,36 @@ class AnnotationEditorLayer {
* The mode has changed: it must be updated. * The mode has changed: it must be updated.
* @param {number} mode * @param {number} mode
*/ */
updateMode(mode) { updateMode(mode = this.#uiManager.getMode()) {
switch (mode) { this.#cleanup();
case AnnotationEditorType.INK: if (mode === AnnotationEditorType.INK) {
// We want to have the ink editor covering all of the page without // We always want to an ink editor ready to draw in.
// having to click to create it: it must be here when we start to draw. this.addInkEditorIfNeeded(false);
this.div.addEventListener("mouseover", this.#boundMouseover); }
this.div.removeEventListener("click", this.#boundClick); this.setActiveEditor(null);
break; }
case AnnotationEditorType.FREETEXT:
this.div.removeEventListener("mouseover", this.#boundMouseover); addInkEditorIfNeeded(isCommitting) {
this.div.addEventListener("click", this.#boundClick); if (
break; !isCommitting &&
default: this.#uiManager.getMode() !== AnnotationEditorType.INK
this.div.removeEventListener("mouseover", this.#boundMouseover); ) {
this.div.removeEventListener("click", this.#boundClick); return;
} }
this.setActiveEditor(null); if (!isCommitting) {
// We're removing an editor but an empty one can already exist so in this
// case we don't need to create a new one.
for (const editor of this.#editors.values()) {
if (editor.isEmpty()) {
editor.setInBackground();
return;
}
}
}
const editor = this.#createAndAddNewEditor({ offsetX: 0, offsetY: 0 });
editor.setInBackground();
} }
/** /**
@ -142,25 +153,6 @@ class AnnotationEditorLayer {
this.#uiManager.setEditingState(isEditing); this.#uiManager.setEditingState(isEditing);
} }
/**
* Mouseover callback.
* @param {MouseEvent} event
*/
mouseover(event) {
if (
event.target === this.div &&
event.buttons === 0 &&
!this.#uiManager.hasActive()
) {
// The div is the target so there is no ink editor, hence we can
// create a new one.
// event.buttons === 0 is here to avoid adding a new ink editor
// when we drop an editor.
const editor = this.#createAndAddNewEditor(event);
editor.setInBackground();
}
}
/** /**
* Add some commands into the CommandManager (undo/redo stuff). * Add some commands into the CommandManager (undo/redo stuff).
* @param {Object} params * @param {Object} params
@ -258,14 +250,12 @@ class AnnotationEditorLayer {
currentActive.commitOrRemove(); currentActive.commitOrRemove();
} }
this.#uiManager.allowClick =
this.#uiManager.getMode() === AnnotationEditorType.INK;
if (editor) { if (editor) {
this.unselectAll(); this.unselectAll();
this.div.removeEventListener("click", this.#boundClick); this.div.removeEventListener("click", this.#boundClick);
} else { } else {
// When in Ink mode, setting the editor to null allows the
// user to have to make one click in order to start drawing.
this.#uiManager.allowClick =
this.#uiManager.getMode() === AnnotationEditorType.INK;
this.div.addEventListener("click", this.#boundClick); this.div.addEventListener("click", this.#boundClick);
} }
} }
@ -295,6 +285,10 @@ class AnnotationEditorLayer {
this.setActiveEditor(null); this.setActiveEditor(null);
this.#uiManager.allowClick = true; this.#uiManager.allowClick = true;
} }
if (!this.#isCleaningUp) {
this.addInkEditorIfNeeded(/* isCommitting = */ false);
}
} }
/** /**
@ -496,6 +490,19 @@ class AnnotationEditorLayer {
this.#uiManager.removeLayer(this); this.#uiManager.removeLayer(this);
} }
#cleanup() {
// When we're cleaning up, some editors are removed but we don't want
// to add a new one which will induce an addition in this.#editors, hence
// an infinite loop.
this.#isCleaningUp = true;
for (const editor of this.#editors.values()) {
if (editor.isEmpty()) {
editor.remove();
}
}
this.#isCleaningUp = false;
}
/** /**
* Render the main editor. * Render the main editor.
* @param {Object} parameters * @param {Object} parameters
@ -505,6 +512,7 @@ class AnnotationEditorLayer {
bindEvents(this, this.div, ["dragover", "drop", "keydown"]); bindEvents(this, this.div, ["dragover", "drop", "keydown"]);
this.div.addEventListener("click", this.#boundClick); this.div.addEventListener("click", this.#boundClick);
this.setDimensions(); this.setDimensions();
this.updateMode();
} }
/** /**
@ -515,6 +523,7 @@ class AnnotationEditorLayer {
this.setActiveEditor(null); this.setActiveEditor(null);
this.viewport = parameters.viewport; this.viewport = parameters.viewport;
this.setDimensions(); this.setDimensions();
this.updateMode();
} }
/** /**

View File

@ -16,8 +16,12 @@
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("./annotation_editor_layer.js").AnnotationEditorLayer} AnnotationEditorLayer */ /** @typedef {import("./annotation_editor_layer.js").AnnotationEditorLayer} AnnotationEditorLayer */
import {
AnnotationEditorPrefix,
shadow,
unreachable,
} from "../../shared/util.js";
import { bindEvents, ColorManager } from "./tools.js"; import { bindEvents, ColorManager } from "./tools.js";
import { shadow, unreachable } from "../../shared/util.js";
/** /**
* @typedef {Object} AnnotationEditorParameters * @typedef {Object} AnnotationEditorParameters
@ -109,7 +113,10 @@ class AnnotationEditor {
event.preventDefault(); event.preventDefault();
this.commitOrRemove(); this.commitOrRemove();
this.parent.setActiveEditor(null);
if (!target?.id?.startsWith(AnnotationEditorPrefix)) {
this.parent.setActiveEditor(null);
}
} }
commitOrRemove() { commitOrRemove() {

View File

@ -372,6 +372,10 @@ class FreeTextEditor extends AnnotationEditor {
/** @inheritdoc */ /** @inheritdoc */
serialize() { serialize() {
if (this.isEmpty()) {
return null;
}
const padding = FreeTextEditor._internalPadding * this.parent.scaleFactor; const padding = FreeTextEditor._internalPadding * this.parent.scaleFactor;
const rect = this.getRect(padding, padding); const rect = this.getRect(padding, padding);

View File

@ -46,6 +46,8 @@ class InkEditor extends AnnotationEditor {
#disableEditing = false; #disableEditing = false;
#isCanvasInitialized = false;
#observer = null; #observer = null;
#realWidth = 0; #realWidth = 0;
@ -58,11 +60,8 @@ class InkEditor extends AnnotationEditor {
constructor(params) { constructor(params) {
super({ ...params, name: "inkEditor" }); super({ ...params, name: "inkEditor" });
this.color = this.color = params.color || null;
params.color || this.thickness = params.thickness || null;
InkEditor._defaultColor ||
AnnotationEditor._defaultLineColor;
this.thickness = params.thickness || InkEditor._defaultThickness;
this.paths = []; this.paths = [];
this.bezierPath2D = []; this.bezierPath2D = [];
this.currentPath = []; this.currentPath = [];
@ -260,7 +259,6 @@ class InkEditor extends AnnotationEditor {
/** @inheritdoc */ /** @inheritdoc */
onceAdded() { onceAdded() {
this.div.draggable = !this.isEmpty(); this.div.draggable = !this.isEmpty();
this.div.focus();
} }
/** @inheritdoc */ /** @inheritdoc */
@ -303,6 +301,13 @@ class InkEditor extends AnnotationEditor {
* @param {number} y * @param {number} y
*/ */
#startDrawing(x, y) { #startDrawing(x, y) {
if (!this.#isCanvasInitialized) {
this.#isCanvasInitialized = true;
this.#setCanvasDims();
this.thickness ||= InkEditor._defaultThickness;
this.color ||=
InkEditor._defaultColor || AnnotationEditor._defaultLineColor;
}
this.currentPath.push([x, y]); this.currentPath.push([x, y]);
this.#setStroke(); this.#setStroke();
this.ctx.beginPath(); this.ctx.beginPath();
@ -411,6 +416,8 @@ class InkEditor extends AnnotationEditor {
this.div.classList.add("disabled"); this.div.classList.add("disabled");
this.#fitToContent(); this.#fitToContent();
this.parent.addInkEditorIfNeeded(/* isCommitting = */ true);
} }
/** @inheritdoc */ /** @inheritdoc */
@ -496,6 +503,7 @@ class InkEditor extends AnnotationEditor {
*/ */
#createCanvas() { #createCanvas() {
this.canvas = document.createElement("canvas"); this.canvas = document.createElement("canvas");
this.canvas.width = this.canvas.height = 0;
this.canvas.className = "inkEditorCanvas"; this.canvas.className = "inkEditorCanvas";
this.div.append(this.canvas); this.div.append(this.canvas);
this.ctx = this.canvas.getContext("2d"); this.ctx = this.canvas.getContext("2d");
@ -527,7 +535,6 @@ class InkEditor extends AnnotationEditor {
} }
super.render(); super.render();
this.div.classList.add("editing");
const [x, y, w, h] = this.#getInitialBBox(); const [x, y, w, h] = this.#getInitialBBox();
this.setAt(x, y, 0, 0); this.setAt(x, y, 0, 0);
this.setDims(w, h); this.setDims(w, h);
@ -536,6 +543,7 @@ class InkEditor extends AnnotationEditor {
if (this.width) { if (this.width) {
// This editor was created in using copy (ctrl+c). // This editor was created in using copy (ctrl+c).
this.#isCanvasInitialized = true;
const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions;
this.setAt( this.setAt(
baseX * parentWidth, baseX * parentWidth,
@ -547,6 +555,9 @@ class InkEditor extends AnnotationEditor {
this.#setCanvasDims(); this.#setCanvasDims();
this.#redraw(); this.#redraw();
this.div.classList.add("disabled"); this.div.classList.add("disabled");
} else {
this.div.classList.add("editing");
this.enableEditMode();
} }
this.#createObserver(); this.#createObserver();
@ -555,6 +566,9 @@ class InkEditor extends AnnotationEditor {
} }
#setCanvasDims() { #setCanvasDims() {
if (!this.#isCanvasInitialized) {
return;
}
const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions;
this.canvas.width = this.width * parentWidth; this.canvas.width = this.width * parentWidth;
this.canvas.height = this.height * parentHeight; this.canvas.height = this.height * parentHeight;
@ -874,6 +888,10 @@ class InkEditor extends AnnotationEditor {
/** @inheritdoc */ /** @inheritdoc */
serialize() { serialize() {
if (this.isEmpty()) {
return null;
}
const rect = this.getRect(0, 0); const rect = this.getRect(0, 0);
const height = const height =
this.rotation % 180 === 0 ? rect[3] - rect[1] : rect[2] - rect[0]; this.rotation % 180 === 0 ? rect[3] - rect[1] : rect[2] - rect[0];

View File

@ -779,7 +779,9 @@ class AnnotationEditorUIManager {
const editors = Array.from(this.#allEditors.values()); const editors = Array.from(this.#allEditors.values());
cmd = () => { cmd = () => {
for (const editor of editors) { for (const editor of editors) {
editor.remove(); if (!editor.isEmpty()) {
editor.remove();
}
} }
}; };