Merge pull request #15163 from calixteman/prepare_touch
[Editor] Always have an ink editor (when in ink mode)
This commit is contained in:
commit
1301b71b7c
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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() {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user