Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Editor] Always have an ink editor (when in ink mode) #15163

Merged
merged 1 commit into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/display/annotation_storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
93 changes: 51 additions & 42 deletions src/display/editor/annotation_editor_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ import { InkEditor } from "./ink.js";
class AnnotationEditorLayer {
#boundClick;

#boundMouseover;

#editors = new Map();

#isCleaningUp = false;

#uiManager;

static _initialized = false;
Expand Down Expand Up @@ -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);
Expand All @@ -114,26 +113,38 @@ 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;
}

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

/**
* Set the editing state.
* @param {boolean} isEditing
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -295,6 +285,10 @@ class AnnotationEditorLayer {
this.setActiveEditor(null);
this.#uiManager.allowClick = true;
}

if (!this.#isCleaningUp) {
this.addInkEditorIfNeeded(/* isCommitting = */ false);
}
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -505,6 +512,7 @@ class AnnotationEditorLayer {
bindEvents(this, this.div, ["dragover", "drop", "keydown"]);
this.div.addEventListener("click", this.#boundClick);
this.setDimensions();
this.updateMode();
}

/**
Expand All @@ -515,6 +523,7 @@ class AnnotationEditorLayer {
this.setActiveEditor(null);
this.viewport = parameters.viewport;
this.setDimensions();
this.updateMode();
}

/**
Expand Down
11 changes: 9 additions & 2 deletions src/display/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -109,7 +113,10 @@ class AnnotationEditor {
event.preventDefault();

this.commitOrRemove();
this.parent.setActiveEditor(null);

if (!target?.id?.startsWith(AnnotationEditorPrefix)) {
this.parent.setActiveEditor(null);
}
}

commitOrRemove() {
Expand Down
4 changes: 4 additions & 0 deletions src/display/editor/freetext.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
32 changes: 25 additions & 7 deletions src/display/editor/ink.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class InkEditor extends AnnotationEditor {

#disableEditing = false;

#isCanvasInitialized = false;

#observer = null;

#realWidth = 0;
Expand All @@ -53,11 +55,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 = [];
Expand Down Expand Up @@ -255,7 +254,6 @@ class InkEditor extends AnnotationEditor {
/** @inheritdoc */
onceAdded() {
this.div.draggable = !this.isEmpty();
this.div.focus();
}

/** @inheritdoc */
Expand Down Expand Up @@ -298,6 +296,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();
Expand Down Expand Up @@ -406,6 +411,8 @@ class InkEditor extends AnnotationEditor {
this.div.classList.add("disabled");

this.#fitToContent();

this.parent.addInkEditorIfNeeded(/* isCommitting = */ true);
}

/** @inheritdoc */
Expand Down Expand Up @@ -491,6 +498,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");
Expand Down Expand Up @@ -522,7 +530,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);
Expand All @@ -531,6 +538,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,
Expand All @@ -542,6 +550,9 @@ class InkEditor extends AnnotationEditor {
this.#setCanvasDims();
this.#redraw();
this.div.classList.add("disabled");
} else {
this.div.classList.add("editing");
this.enableEditMode();
}

this.#createObserver();
Expand All @@ -550,6 +561,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;
Expand Down Expand Up @@ -861,6 +875,10 @@ class InkEditor extends AnnotationEditor {

/** @inheritdoc */
serialize() {
if (this.isEmpty()) {
return null;
}

Comment on lines +878 to +881
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you perhaps want to make the same change for the FreeText-class as well?

const rect = this.getRect(0, 0);
const height =
this.rotation % 180 === 0 ? rect[3] - rect[1] : rect[2] - rect[0];
Expand Down
4 changes: 3 additions & 1 deletion src/display/editor/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
};

Expand Down