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();
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;
}

View File

@ -42,10 +42,10 @@ import { InkEditor } from "./ink.js";
class AnnotationEditorLayer {
#boundClick;
#boundMouseover;
#editors = new Map();
#isCleaningUp = false;
#uiManager;
static _initialized = false;
@ -92,7 +92,6 @@ class AnnotationEditorLayer {
this.pageIndex = options.pageIndex;
this.div = options.div;
this.#boundClick = this.click.bind(this);
this.#boundMouseover = this.mouseover.bind(this);
for (const editor of this.#uiManager.getEditors(options.pageIndex)) {
this.add(editor);
@ -114,24 +113,36 @@ class AnnotationEditorLayer {
* The mode has changed: it must be updated.
* @param {number} mode
*/
updateMode(mode) {
switch (mode) {
case AnnotationEditorType.INK:
// We want to have the ink editor covering all of the page without
// having to click to create it: it must be here when we start to draw.
this.div.addEventListener("mouseover", this.#boundMouseover);
this.div.removeEventListener("click", this.#boundClick);
break;
case AnnotationEditorType.FREETEXT:
this.div.removeEventListener("mouseover", this.#boundMouseover);
this.div.addEventListener("click", this.#boundClick);
break;
default:
this.div.removeEventListener("mouseover", this.#boundMouseover);
this.div.removeEventListener("click", this.#boundClick);
updateMode(mode = this.#uiManager.getMode()) {
this.#cleanup();
if (mode === AnnotationEditorType.INK) {
// We always want to an ink editor ready to draw in.
this.addInkEditorIfNeeded(false);
}
this.setActiveEditor(null);
}
addInkEditorIfNeeded(isCommitting) {
if (
!isCommitting &&
this.#uiManager.getMode() !== AnnotationEditorType.INK
) {
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);
}
/**
* 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).
* @param {Object} params
@ -258,14 +250,12 @@ class AnnotationEditorLayer {
currentActive.commitOrRemove();
}
this.#uiManager.allowClick =
this.#uiManager.getMode() === AnnotationEditorType.INK;
if (editor) {
this.unselectAll();
this.div.removeEventListener("click", this.#boundClick);
} 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);
}
}
@ -295,6 +285,10 @@ class AnnotationEditorLayer {
this.setActiveEditor(null);
this.#uiManager.allowClick = true;
}
if (!this.#isCleaningUp) {
this.addInkEditorIfNeeded(/* isCommitting = */ false);
}
}
/**
@ -496,6 +490,19 @@ class AnnotationEditorLayer {
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.
* @param {Object} parameters
@ -505,6 +512,7 @@ class AnnotationEditorLayer {
bindEvents(this, this.div, ["dragover", "drop", "keydown"]);
this.div.addEventListener("click", this.#boundClick);
this.setDimensions();
this.updateMode();
}
/**
@ -515,6 +523,7 @@ class AnnotationEditorLayer {
this.setActiveEditor(null);
this.viewport = parameters.viewport;
this.setDimensions();
this.updateMode();
}
/**

View File

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

View File

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

View File

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