Skip to content

Commit

Permalink
[Editor] - Add the ability to directly draw after selecting ink tool
Browse files Browse the repository at this point in the history
- Right now, we must select the tool, then click to select a page and
  click to start drawing and it's a bit painful;
- so just create a new ink editor when we're hovering a page without one.
  • Loading branch information
calixteman committed Jun 16, 2022
1 parent 89cebcb commit e7dc1ef
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 38 deletions.
98 changes: 84 additions & 14 deletions src/display/editor/annotation_editor_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import { PixelsPerInch } from "../display_utils.js";
class AnnotationEditorLayer {
#boundClick;

#boundMouseover;

#editors = new Map();

#uiManager;
Expand Down Expand Up @@ -83,6 +85,7 @@ 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 @@ -91,13 +94,45 @@ class AnnotationEditorLayer {
this.#uiManager.addLayer(this);
}

/**
* The mode has changed: it must be updated.
* @param {number} mode
*/
updateMode(mode) {
if (mode === 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);
} else {
this.div.removeEventListener("mouseover", this.#boundMouseover);
}
}

/**
* Mouseover callback.
* @param {MouseEvent} event
*/
mouseover(event) {
if (event.target === this.div && event.buttons === 0) {
// 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 {function} cmd
* @param {function} undo
* @param {boolean} mustExec - If true the command is executed after having
* been added.
*/
addCommands(cmd, undo) {
this.#uiManager.addCommands(cmd, undo);
addCommands(cmd, undo, mustExec) {
this.#uiManager.addCommands(cmd, undo, mustExec);
}

/**
Expand Down Expand Up @@ -178,14 +213,24 @@ class AnnotationEditorLayer {
* @param {AnnotationEditor} editor
*/
setActiveEditor(editor) {
const currentActive = this.#uiManager.getActive();
if (currentActive === editor) {
return;
}

this.#uiManager.setActiveEditor(editor);

if (currentActive && currentActive !== editor) {
currentActive.commitOrRemove();
}

if (editor) {
this.unselectAll();
this.div.removeEventListener("click", this.#boundClick);
} else {
this.#uiManager.allowClick = false;
this.div.addEventListener("click", this.#boundClick);
}
this.#uiManager.setActiveEditor(editor);
}

attach(editor) {
Expand All @@ -212,7 +257,6 @@ class AnnotationEditorLayer {
if (this.#uiManager.isActive(editor) || this.#editors.size === 0) {
this.setActiveEditor(null);
this.#uiManager.allowClick = true;
this.div.focus();
}
}

Expand Down Expand Up @@ -279,7 +323,22 @@ class AnnotationEditorLayer {
editor.remove();
};

this.addCommands(cmd, undo);
this.addCommands(cmd, undo, true);
}

/**
* Add a new editor and make this addition undoable.
* @param {AnnotationEditor} editor
*/
addUndoableEditor(editor) {
const cmd = () => {
this.addOrRebuild(editor);
};
const undo = () => {
editor.remove();
};

this.addCommands(cmd, undo, false);
}

/**
Expand All @@ -306,16 +365,11 @@ class AnnotationEditorLayer {
}

/**
* Mouseclick callback.
* Create and add a new editor.
* @param {MouseEvent} event
* @returns {undefined}
* @returns {AnnotationEditor}
*/
click(event) {
if (!this.#uiManager.allowClick) {
this.#uiManager.allowClick = true;
return;
}

#createAndAddNewEditor(event) {
const id = this.getNextId();
const editor = this.#createNewEditor({
parent: this,
Expand All @@ -324,8 +378,24 @@ class AnnotationEditorLayer {
y: event.offsetY,
});
if (editor) {
this.addANewEditor(editor);
this.add(editor);
}

return editor;
}

/**
* Mouseclick callback.
* @param {MouseEvent} event
* @returns {undefined}
*/
click(event) {
if (!this.#uiManager.allowClick) {
this.#uiManager.allowClick = true;
return;
}

this.#createAndAddNewEditor(event);
}

/**
Expand Down
21 changes: 19 additions & 2 deletions src/display/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ class AnnotationEditor {
this.isAttachedToDOM = false;
}

/**
* This editor will be behind the others.
*/
setInBackground() {
this.div.classList.add("background");
}

/**
* This editor will be in the foreground.
*/
setInForeground() {
this.div.classList.remove("background");
}

/**
* onfocus callback.
*/
Expand Down Expand Up @@ -81,12 +95,16 @@ class AnnotationEditor {

event.preventDefault();

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

commitOrRemove() {
if (this.isEmpty()) {
this.remove();
} else {
this.commit();
}
this.parent.setActiveEditor(null);
}

/**
Expand Down Expand Up @@ -156,7 +174,6 @@ class AnnotationEditor {
this.div = document.createElement("div");
this.div.className = this.name;
this.div.setAttribute("id", this.id);
this.div.draggable = true;
this.div.tabIndex = 100;

const [tx, ty] = this.getInitialTranslation();
Expand Down
9 changes: 9 additions & 0 deletions src/display/editor/freetext.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class FreeTextEditor extends AnnotationEditor {

#contentHTML = "";

#hasAlreadyBeenCommitted = false;

#fontSize;

static _freeTextDefaultContent = "";
Expand Down Expand Up @@ -168,6 +170,13 @@ class FreeTextEditor extends AnnotationEditor {
* @returns {undefined}
*/
commit() {
if (!this.#hasAlreadyBeenCommitted) {
// This editor has something and it's the first time
// it's commited so we can it in the undo/redo stack.
this.#hasAlreadyBeenCommitted = true;
this.parent.addUndoableEditor(this);
}

this.disableEditMode();
this.#contentHTML = this.editorDiv.innerHTML;
this.#content = this.#extractText().trimEnd();
Expand Down
42 changes: 28 additions & 14 deletions src/display/editor/ink.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import { fitCurve } from "./fit_curve/fit_curve.js";
* Basic draw editor in order to generate an Ink annotation.
*/
class InkEditor extends AnnotationEditor {
#aspectRatio;
#aspectRatio = 0;

#baseHeight;
#baseHeight = 0;

#baseWidth;
#baseWidth = 0;

#boundCanvasMousemove;

Expand All @@ -35,9 +35,9 @@ class InkEditor extends AnnotationEditor {

#boundCanvasMousedown;

#disableEditing;
#disableEditing = false;

#observer;
#observer = null;

constructor(params) {
super({ ...params, name: "inkEditor" });
Expand All @@ -48,10 +48,6 @@ class InkEditor extends AnnotationEditor {
this.currentPath = [];
this.scaleFactor = 1;
this.translationX = this.translationY = 0;
this.#baseWidth = this.#baseHeight = 0;
this.#aspectRatio = 0;
this.#disableEditing = false;
this.#observer = null;
this.x = 0;
this.y = 0;

Expand Down Expand Up @@ -113,20 +109,20 @@ class InkEditor extends AnnotationEditor {
return;
}

super.remove();

// Destroy the canvas.
this.canvas.width = this.canvas.heigth = 0;
this.canvas.remove();
this.canvas = null;

this.#observer.disconnect();
this.#observer = null;

super.remove();
}

/** @inheritdoc */
enableEditMode() {
if (this.#disableEditing) {
if (this.#disableEditing || this.canvas === null) {
return;
}

Expand All @@ -145,7 +141,7 @@ class InkEditor extends AnnotationEditor {

super.disableEditMode();
this.canvas.style.cursor = "auto";
this.div.draggable = true;
this.div.draggable = !this.isEmpty();
this.div.classList.remove("editing");

this.canvas.removeEventListener("mousedown", this.#boundCanvasMousedown);
Expand All @@ -154,6 +150,7 @@ class InkEditor extends AnnotationEditor {

/** @inheritdoc */
onceAdded() {
this.div.draggable = !this.isEmpty();
this.div.focus();
}

Expand Down Expand Up @@ -238,11 +235,15 @@ class InkEditor extends AnnotationEditor {
if (this.paths.length === 0) {
this.remove();
} else {
if (!this.canvas) {
this.#createCanvas();
this.#createObserver();
}
this.#fitToContent();
}
};

this.parent.addCommands(cmd, undo);
this.parent.addCommands(cmd, undo, true);
}

/**
Expand Down Expand Up @@ -273,8 +274,12 @@ class InkEditor extends AnnotationEditor {
if (this.#disableEditing) {
return;
}

this.disableEditMode();

// This editor must be on top of the main ink editor.
this.setInForeground();

this.#disableEditing = true;
this.div.classList.add("disabled");

Expand All @@ -297,6 +302,10 @@ class InkEditor extends AnnotationEditor {
return;
}

// We want to draw on top of any other editors.
// Since it's the last child, there's no need to give it a higher z-index.
this.setInForeground();

event.stopPropagation();

this.canvas.addEventListener("mouseleave", this.#boundCanvasMouseleave);
Expand Down Expand Up @@ -324,6 +333,10 @@ class InkEditor extends AnnotationEditor {
if (this.isInEditMode() && this.currentPath.length !== 0) {
event.stopPropagation();
this.#endDrawing(event);

// Since the ink editor covers all of the page and we want to be able
// to select another editor, we just put this one in the background.
this.setInBackground();
}
}

Expand All @@ -334,6 +347,7 @@ class InkEditor extends AnnotationEditor {
*/
canvasMouseleave(event) {
this.#endDrawing(event);
this.setInBackground();
}

/**
Expand Down
Loading

0 comments on commit e7dc1ef

Please sign in to comment.