Skip to content

Commit b868812

Browse files
authored
Merge pull request #15050 from calixteman/make_ink_better
[Editor] - Add the ability to directly draw after selecting ink tool
2 parents c3d0858 + e7dc1ef commit b868812

File tree

6 files changed

+161
-38
lines changed

6 files changed

+161
-38
lines changed

src/display/editor/annotation_editor_layer.js

+84-14
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import { PixelsPerInch } from "../display_utils.js";
4343
class AnnotationEditorLayer {
4444
#boundClick;
4545

46+
#boundMouseover;
47+
4648
#editors = new Map();
4749

4850
#uiManager;
@@ -83,6 +85,7 @@ class AnnotationEditorLayer {
8385
this.pageIndex = options.pageIndex;
8486
this.div = options.div;
8587
this.#boundClick = this.click.bind(this);
88+
this.#boundMouseover = this.mouseover.bind(this);
8689

8790
for (const editor of this.#uiManager.getEditors(options.pageIndex)) {
8891
this.add(editor);
@@ -91,13 +94,45 @@ class AnnotationEditorLayer {
9194
this.#uiManager.addLayer(this);
9295
}
9396

97+
/**
98+
* The mode has changed: it must be updated.
99+
* @param {number} mode
100+
*/
101+
updateMode(mode) {
102+
if (mode === AnnotationEditorType.INK) {
103+
// We want to have the ink editor covering all of the page without having
104+
// to click to create it: it must be here when we start to draw.
105+
this.div.addEventListener("mouseover", this.#boundMouseover);
106+
this.div.removeEventListener("click", this.#boundClick);
107+
} else {
108+
this.div.removeEventListener("mouseover", this.#boundMouseover);
109+
}
110+
}
111+
112+
/**
113+
* Mouseover callback.
114+
* @param {MouseEvent} event
115+
*/
116+
mouseover(event) {
117+
if (event.target === this.div && event.buttons === 0) {
118+
// The div is the target so there is no ink editor, hence we can
119+
// create a new one.
120+
// event.buttons === 0 is here to avoid adding a new ink editor
121+
// when we drop an editor.
122+
const editor = this.#createAndAddNewEditor(event);
123+
editor.setInBackground();
124+
}
125+
}
126+
94127
/**
95128
* Add some commands into the CommandManager (undo/redo stuff).
96129
* @param {function} cmd
97130
* @param {function} undo
131+
* @param {boolean} mustExec - If true the command is executed after having
132+
* been added.
98133
*/
99-
addCommands(cmd, undo) {
100-
this.#uiManager.addCommands(cmd, undo);
134+
addCommands(cmd, undo, mustExec) {
135+
this.#uiManager.addCommands(cmd, undo, mustExec);
101136
}
102137

103138
/**
@@ -178,14 +213,24 @@ class AnnotationEditorLayer {
178213
* @param {AnnotationEditor} editor
179214
*/
180215
setActiveEditor(editor) {
216+
const currentActive = this.#uiManager.getActive();
217+
if (currentActive === editor) {
218+
return;
219+
}
220+
221+
this.#uiManager.setActiveEditor(editor);
222+
223+
if (currentActive && currentActive !== editor) {
224+
currentActive.commitOrRemove();
225+
}
226+
181227
if (editor) {
182228
this.unselectAll();
183229
this.div.removeEventListener("click", this.#boundClick);
184230
} else {
185231
this.#uiManager.allowClick = false;
186232
this.div.addEventListener("click", this.#boundClick);
187233
}
188-
this.#uiManager.setActiveEditor(editor);
189234
}
190235

191236
attach(editor) {
@@ -212,7 +257,6 @@ class AnnotationEditorLayer {
212257
if (this.#uiManager.isActive(editor) || this.#editors.size === 0) {
213258
this.setActiveEditor(null);
214259
this.#uiManager.allowClick = true;
215-
this.div.focus();
216260
}
217261
}
218262

@@ -279,7 +323,22 @@ class AnnotationEditorLayer {
279323
editor.remove();
280324
};
281325

282-
this.addCommands(cmd, undo);
326+
this.addCommands(cmd, undo, true);
327+
}
328+
329+
/**
330+
* Add a new editor and make this addition undoable.
331+
* @param {AnnotationEditor} editor
332+
*/
333+
addUndoableEditor(editor) {
334+
const cmd = () => {
335+
this.addOrRebuild(editor);
336+
};
337+
const undo = () => {
338+
editor.remove();
339+
};
340+
341+
this.addCommands(cmd, undo, false);
283342
}
284343

285344
/**
@@ -306,16 +365,11 @@ class AnnotationEditorLayer {
306365
}
307366

308367
/**
309-
* Mouseclick callback.
368+
* Create and add a new editor.
310369
* @param {MouseEvent} event
311-
* @returns {undefined}
370+
* @returns {AnnotationEditor}
312371
*/
313-
click(event) {
314-
if (!this.#uiManager.allowClick) {
315-
this.#uiManager.allowClick = true;
316-
return;
317-
}
318-
372+
#createAndAddNewEditor(event) {
319373
const id = this.getNextId();
320374
const editor = this.#createNewEditor({
321375
parent: this,
@@ -324,8 +378,24 @@ class AnnotationEditorLayer {
324378
y: event.offsetY,
325379
});
326380
if (editor) {
327-
this.addANewEditor(editor);
381+
this.add(editor);
328382
}
383+
384+
return editor;
385+
}
386+
387+
/**
388+
* Mouseclick callback.
389+
* @param {MouseEvent} event
390+
* @returns {undefined}
391+
*/
392+
click(event) {
393+
if (!this.#uiManager.allowClick) {
394+
this.#uiManager.allowClick = true;
395+
return;
396+
}
397+
398+
this.#createAndAddNewEditor(event);
329399
}
330400

331401
/**

src/display/editor/editor.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ class AnnotationEditor {
5353
this.isAttachedToDOM = false;
5454
}
5555

56+
/**
57+
* This editor will be behind the others.
58+
*/
59+
setInBackground() {
60+
this.div.classList.add("background");
61+
}
62+
63+
/**
64+
* This editor will be in the foreground.
65+
*/
66+
setInForeground() {
67+
this.div.classList.remove("background");
68+
}
69+
5670
/**
5771
* onfocus callback.
5872
*/
@@ -81,12 +95,16 @@ class AnnotationEditor {
8195

8296
event.preventDefault();
8397

98+
this.commitOrRemove();
99+
this.parent.setActiveEditor(null);
100+
}
101+
102+
commitOrRemove() {
84103
if (this.isEmpty()) {
85104
this.remove();
86105
} else {
87106
this.commit();
88107
}
89-
this.parent.setActiveEditor(null);
90108
}
91109

92110
/**
@@ -156,7 +174,6 @@ class AnnotationEditor {
156174
this.div = document.createElement("div");
157175
this.div.className = this.name;
158176
this.div.setAttribute("id", this.id);
159-
this.div.draggable = true;
160177
this.div.tabIndex = 100;
161178

162179
const [tx, ty] = this.getInitialTranslation();

src/display/editor/freetext.js

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class FreeTextEditor extends AnnotationEditor {
3333

3434
#contentHTML = "";
3535

36+
#hasAlreadyBeenCommitted = false;
37+
3638
#fontSize;
3739

3840
static _freeTextDefaultContent = "";
@@ -168,6 +170,13 @@ class FreeTextEditor extends AnnotationEditor {
168170
* @returns {undefined}
169171
*/
170172
commit() {
173+
if (!this.#hasAlreadyBeenCommitted) {
174+
// This editor has something and it's the first time
175+
// it's commited so we can it in the undo/redo stack.
176+
this.#hasAlreadyBeenCommitted = true;
177+
this.parent.addUndoableEditor(this);
178+
}
179+
171180
this.disableEditMode();
172181
this.#contentHTML = this.editorDiv.innerHTML;
173182
this.#content = this.#extractText().trimEnd();

src/display/editor/ink.js

+28-14
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import { fitCurve } from "./fit_curve/fit_curve.js";
2121
* Basic draw editor in order to generate an Ink annotation.
2222
*/
2323
class InkEditor extends AnnotationEditor {
24-
#aspectRatio;
24+
#aspectRatio = 0;
2525

26-
#baseHeight;
26+
#baseHeight = 0;
2727

28-
#baseWidth;
28+
#baseWidth = 0;
2929

3030
#boundCanvasMousemove;
3131

@@ -35,9 +35,9 @@ class InkEditor extends AnnotationEditor {
3535

3636
#boundCanvasMousedown;
3737

38-
#disableEditing;
38+
#disableEditing = false;
3939

40-
#observer;
40+
#observer = null;
4141

4242
constructor(params) {
4343
super({ ...params, name: "inkEditor" });
@@ -48,10 +48,6 @@ class InkEditor extends AnnotationEditor {
4848
this.currentPath = [];
4949
this.scaleFactor = 1;
5050
this.translationX = this.translationY = 0;
51-
this.#baseWidth = this.#baseHeight = 0;
52-
this.#aspectRatio = 0;
53-
this.#disableEditing = false;
54-
this.#observer = null;
5551
this.x = 0;
5652
this.y = 0;
5753

@@ -113,20 +109,20 @@ class InkEditor extends AnnotationEditor {
113109
return;
114110
}
115111

116-
super.remove();
117-
118112
// Destroy the canvas.
119113
this.canvas.width = this.canvas.heigth = 0;
120114
this.canvas.remove();
121115
this.canvas = null;
122116

123117
this.#observer.disconnect();
124118
this.#observer = null;
119+
120+
super.remove();
125121
}
126122

127123
/** @inheritdoc */
128124
enableEditMode() {
129-
if (this.#disableEditing) {
125+
if (this.#disableEditing || this.canvas === null) {
130126
return;
131127
}
132128

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

146142
super.disableEditMode();
147143
this.canvas.style.cursor = "auto";
148-
this.div.draggable = true;
144+
this.div.draggable = !this.isEmpty();
149145
this.div.classList.remove("editing");
150146

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

155151
/** @inheritdoc */
156152
onceAdded() {
153+
this.div.draggable = !this.isEmpty();
157154
this.div.focus();
158155
}
159156

@@ -238,11 +235,15 @@ class InkEditor extends AnnotationEditor {
238235
if (this.paths.length === 0) {
239236
this.remove();
240237
} else {
238+
if (!this.canvas) {
239+
this.#createCanvas();
240+
this.#createObserver();
241+
}
241242
this.#fitToContent();
242243
}
243244
};
244245

245-
this.parent.addCommands(cmd, undo);
246+
this.parent.addCommands(cmd, undo, true);
246247
}
247248

248249
/**
@@ -273,8 +274,12 @@ class InkEditor extends AnnotationEditor {
273274
if (this.#disableEditing) {
274275
return;
275276
}
277+
276278
this.disableEditMode();
277279

280+
// This editor must be on top of the main ink editor.
281+
this.setInForeground();
282+
278283
this.#disableEditing = true;
279284
this.div.classList.add("disabled");
280285

@@ -297,6 +302,10 @@ class InkEditor extends AnnotationEditor {
297302
return;
298303
}
299304

305+
// We want to draw on top of any other editors.
306+
// Since it's the last child, there's no need to give it a higher z-index.
307+
this.setInForeground();
308+
300309
event.stopPropagation();
301310

302311
this.canvas.addEventListener("mouseleave", this.#boundCanvasMouseleave);
@@ -324,6 +333,10 @@ class InkEditor extends AnnotationEditor {
324333
if (this.isInEditMode() && this.currentPath.length !== 0) {
325334
event.stopPropagation();
326335
this.#endDrawing(event);
336+
337+
// Since the ink editor covers all of the page and we want to be able
338+
// to select another editor, we just put this one in the background.
339+
this.setInBackground();
327340
}
328341
}
329342

@@ -334,6 +347,7 @@ class InkEditor extends AnnotationEditor {
334347
*/
335348
canvasMouseleave(event) {
336349
this.#endDrawing(event);
350+
this.setInBackground();
337351
}
338352

339353
/**

0 commit comments

Comments
 (0)