Merge pull request #15179 from calixteman/editor_cp
[Editor] Use serialized data when copying/pasting
This commit is contained in:
commit
ad15532235
@ -408,6 +408,21 @@ class AnnotationEditorLayer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new editor
|
||||||
|
* @param {Object} data
|
||||||
|
* @returns {AnnotationEditor}
|
||||||
|
*/
|
||||||
|
deserialize(data) {
|
||||||
|
switch (data.annotationType) {
|
||||||
|
case AnnotationEditorType.FREETEXT:
|
||||||
|
return FreeTextEditor.deserialize(data, this);
|
||||||
|
case AnnotationEditorType.INK:
|
||||||
|
return InkEditor.deserialize(data, this);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and add a new editor.
|
* Create and add a new editor.
|
||||||
* @param {MouseEvent} event
|
* @param {MouseEvent} event
|
||||||
|
@ -290,6 +290,26 @@ class AnnotationEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRectInCurrentCoords(rect, pageHeight) {
|
||||||
|
const [x1, y1, x2, y2] = rect;
|
||||||
|
|
||||||
|
const width = x2 - x1;
|
||||||
|
const height = y2 - y1;
|
||||||
|
|
||||||
|
switch (this.rotation) {
|
||||||
|
case 0:
|
||||||
|
return [x1, pageHeight - y2, width, height];
|
||||||
|
case 90:
|
||||||
|
return [x1, pageHeight - y1, height, width];
|
||||||
|
case 180:
|
||||||
|
return [x2, pageHeight - y1, width, height];
|
||||||
|
case 270:
|
||||||
|
return [x2, pageHeight - y2, height, width];
|
||||||
|
default:
|
||||||
|
throw new Error("Invalid rotation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executed once this editor has been rendered.
|
* Executed once this editor has been rendered.
|
||||||
*/
|
*/
|
||||||
@ -336,18 +356,6 @@ class AnnotationEditor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy the elements of an editor in order to be able to build
|
|
||||||
* a new one from these data.
|
|
||||||
* It's used on ctrl+c action.
|
|
||||||
*
|
|
||||||
* To implement in subclasses.
|
|
||||||
* @returns {AnnotationEditor}
|
|
||||||
*/
|
|
||||||
copy() {
|
|
||||||
unreachable("An editor must be copyable");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this editor needs to be rebuilt or not.
|
* Check if this editor needs to be rebuilt or not.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
@ -378,6 +386,34 @@ class AnnotationEditor {
|
|||||||
unreachable("An editor must be serializable");
|
unreachable("An editor must be serializable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize the editor.
|
||||||
|
* The result of the deserialization is a new editor.
|
||||||
|
*
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {AnnotationEditorLayer} parent
|
||||||
|
* @returns {AnnotationEditor}
|
||||||
|
*/
|
||||||
|
static deserialize(data, parent) {
|
||||||
|
const editor = new this.prototype.constructor({
|
||||||
|
parent,
|
||||||
|
id: parent.getNextId(),
|
||||||
|
});
|
||||||
|
editor.rotation = data.rotation;
|
||||||
|
|
||||||
|
const [pageWidth, pageHeight] = parent.pageDimensions;
|
||||||
|
const [x, y, width, height] = editor.getRectInCurrentCoords(
|
||||||
|
data.rect,
|
||||||
|
pageHeight
|
||||||
|
);
|
||||||
|
editor.x = x / pageWidth;
|
||||||
|
editor.y = y / pageHeight;
|
||||||
|
editor.width = width / pageWidth;
|
||||||
|
editor.height = height / pageHeight;
|
||||||
|
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove this editor.
|
* Remove this editor.
|
||||||
* It's used on ctrl+backspace action.
|
* It's used on ctrl+backspace action.
|
||||||
|
@ -13,11 +13,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
/** @typedef {import("./annotation_editor_layer.js").AnnotationEditorLayer} AnnotationEditorLayer */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AnnotationEditorParamsType,
|
AnnotationEditorParamsType,
|
||||||
AnnotationEditorType,
|
AnnotationEditorType,
|
||||||
assert,
|
assert,
|
||||||
LINE_FACTOR,
|
LINE_FACTOR,
|
||||||
|
Util,
|
||||||
} from "../../shared/util.js";
|
} from "../../shared/util.js";
|
||||||
import { AnnotationEditor } from "./editor.js";
|
import { AnnotationEditor } from "./editor.js";
|
||||||
import { bindEvents } from "./tools.js";
|
import { bindEvents } from "./tools.js";
|
||||||
@ -77,26 +81,6 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
copy() {
|
|
||||||
const [width, height] = this.parent.viewportBaseDimensions;
|
|
||||||
const editor = new FreeTextEditor({
|
|
||||||
parent: this.parent,
|
|
||||||
id: this.parent.getNextId(),
|
|
||||||
x: this.x * width,
|
|
||||||
y: this.y * height,
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.width = this.width;
|
|
||||||
editor.height = this.height;
|
|
||||||
editor.#color = this.#color;
|
|
||||||
editor.#fontSize = this.#fontSize;
|
|
||||||
editor.#content = this.#content;
|
|
||||||
editor.#contentHTML = this.#contentHTML;
|
|
||||||
|
|
||||||
return editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
static updateDefaultParams(type, value) {
|
static updateDefaultParams(type, value) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnnotationEditorParamsType.FREETEXT_SIZE:
|
case AnnotationEditorParamsType.FREETEXT_SIZE:
|
||||||
@ -370,6 +354,21 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
return this.div;
|
return this.div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
static deserialize(data, parent) {
|
||||||
|
const editor = super.deserialize(data, parent);
|
||||||
|
|
||||||
|
editor.#fontSize = data.fontSize;
|
||||||
|
editor.#color = Util.makeHexColor(...data.color);
|
||||||
|
editor.#content = data.value;
|
||||||
|
editor.#contentHTML = data.value
|
||||||
|
.split("\n")
|
||||||
|
.map(line => `<div>${line}</div>`)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
serialize() {
|
serialize() {
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
|
@ -76,34 +76,6 @@ class InkEditor extends AnnotationEditor {
|
|||||||
this.#boundCanvasMousedown = this.canvasMousedown.bind(this);
|
this.#boundCanvasMousedown = this.canvasMousedown.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
copy() {
|
|
||||||
const editor = new InkEditor({
|
|
||||||
parent: this.parent,
|
|
||||||
id: this.parent.getNextId(),
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.x = this.x;
|
|
||||||
editor.y = this.y;
|
|
||||||
editor.width = this.width;
|
|
||||||
editor.height = this.height;
|
|
||||||
editor.color = this.color;
|
|
||||||
editor.thickness = this.thickness;
|
|
||||||
editor.paths = this.paths.slice();
|
|
||||||
editor.bezierPath2D = this.bezierPath2D.slice();
|
|
||||||
editor.scaleFactor = this.scaleFactor;
|
|
||||||
editor.translationX = this.translationX;
|
|
||||||
editor.translationY = this.translationY;
|
|
||||||
editor.#aspectRatio = this.#aspectRatio;
|
|
||||||
editor.#baseWidth = this.#baseWidth;
|
|
||||||
editor.#baseHeight = this.#baseHeight;
|
|
||||||
editor.#disableEditing = this.#disableEditing;
|
|
||||||
editor.#realWidth = this.#realWidth;
|
|
||||||
editor.#realHeight = this.#realHeight;
|
|
||||||
|
|
||||||
return editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
static updateDefaultParams(type, value) {
|
static updateDefaultParams(type, value) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnnotationEditorParamsType.INK_THICKNESS:
|
case AnnotationEditorParamsType.INK_THICKNESS:
|
||||||
@ -351,7 +323,7 @@ class InkEditor extends AnnotationEditor {
|
|||||||
const xy = [x, y];
|
const xy = [x, y];
|
||||||
bezier = [[xy, xy.slice(), xy.slice(), xy]];
|
bezier = [[xy, xy.slice(), xy.slice(), xy]];
|
||||||
}
|
}
|
||||||
const path2D = this.#buildPath2D(bezier);
|
const path2D = InkEditor.#buildPath2D(bezier);
|
||||||
this.currentPath.length = 0;
|
this.currentPath.length = 0;
|
||||||
|
|
||||||
const cmd = () => {
|
const cmd = () => {
|
||||||
@ -543,7 +515,6 @@ 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,
|
||||||
@ -551,9 +522,11 @@ class InkEditor extends AnnotationEditor {
|
|||||||
this.width * parentWidth,
|
this.width * parentWidth,
|
||||||
this.height * parentHeight
|
this.height * parentHeight
|
||||||
);
|
);
|
||||||
this.setDims(this.width * parentWidth, this.height * parentHeight);
|
this.#isCanvasInitialized = true;
|
||||||
this.#setCanvasDims();
|
this.#setCanvasDims();
|
||||||
|
this.setDims(this.width * parentWidth, this.height * parentHeight);
|
||||||
this.#redraw();
|
this.#redraw();
|
||||||
|
this.#setMinDims();
|
||||||
this.div.classList.add("disabled");
|
this.div.classList.add("disabled");
|
||||||
} else {
|
} else {
|
||||||
this.div.classList.add("editing");
|
this.div.classList.add("editing");
|
||||||
@ -570,8 +543,8 @@ class InkEditor extends AnnotationEditor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions;
|
const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions;
|
||||||
this.canvas.width = this.width * parentWidth;
|
this.canvas.width = Math.ceil(this.width * parentWidth);
|
||||||
this.canvas.height = this.height * parentHeight;
|
this.canvas.height = Math.ceil(this.height * parentHeight);
|
||||||
this.#updateTransform();
|
this.#updateTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,10 +583,7 @@ class InkEditor extends AnnotationEditor {
|
|||||||
this.height = height / parentHeight;
|
this.height = height / parentHeight;
|
||||||
|
|
||||||
if (this.#disableEditing) {
|
if (this.#disableEditing) {
|
||||||
const padding = this.#getPadding();
|
this.#setScaleFactor(width, height);
|
||||||
const scaleFactorW = (width - padding) / this.#baseWidth;
|
|
||||||
const scaleFactorH = (height - padding) / this.#baseHeight;
|
|
||||||
this.scaleFactor = Math.min(scaleFactorW, scaleFactorH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#setCanvasDims();
|
this.#setCanvasDims();
|
||||||
@ -622,6 +592,13 @@ class InkEditor extends AnnotationEditor {
|
|||||||
this.canvas.style.visibility = "visible";
|
this.canvas.style.visibility = "visible";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setScaleFactor(width, height) {
|
||||||
|
const padding = this.#getPadding();
|
||||||
|
const scaleFactorW = (width - padding) / this.#baseWidth;
|
||||||
|
const scaleFactorH = (height - padding) / this.#baseHeight;
|
||||||
|
this.scaleFactor = Math.min(scaleFactorW, scaleFactorH);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the canvas transform.
|
* Update the canvas transform.
|
||||||
*/
|
*/
|
||||||
@ -642,7 +619,7 @@ class InkEditor extends AnnotationEditor {
|
|||||||
* @param {Arra<Array<number>} bezier
|
* @param {Arra<Array<number>} bezier
|
||||||
* @returns {Path2D}
|
* @returns {Path2D}
|
||||||
*/
|
*/
|
||||||
#buildPath2D(bezier) {
|
static #buildPath2D(bezier) {
|
||||||
const path2D = new Path2D();
|
const path2D = new Path2D();
|
||||||
for (let i = 0, ii = bezier.length; i < ii; i++) {
|
for (let i = 0, ii = bezier.length; i < ii; i++) {
|
||||||
const [first, control1, control2, second] = bezier[i];
|
const [first, control1, control2, second] = bezier[i];
|
||||||
@ -859,14 +836,7 @@ class InkEditor extends AnnotationEditor {
|
|||||||
this.height = height / parentHeight;
|
this.height = height / parentHeight;
|
||||||
|
|
||||||
this.#aspectRatio = width / height;
|
this.#aspectRatio = width / height;
|
||||||
const { style } = this.div;
|
this.#setMinDims();
|
||||||
if (this.#aspectRatio >= 1) {
|
|
||||||
style.minHeight = `${RESIZER_SIZE}px`;
|
|
||||||
style.minWidth = `${Math.round(this.#aspectRatio * RESIZER_SIZE)}px`;
|
|
||||||
} else {
|
|
||||||
style.minWidth = `${RESIZER_SIZE}px`;
|
|
||||||
style.minHeight = `${Math.round(RESIZER_SIZE / this.#aspectRatio)}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevTranslationX = this.translationX;
|
const prevTranslationX = this.translationX;
|
||||||
const prevTranslationY = this.translationY;
|
const prevTranslationY = this.translationY;
|
||||||
@ -886,6 +856,68 @@ class InkEditor extends AnnotationEditor {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setMinDims() {
|
||||||
|
const { style } = this.div;
|
||||||
|
if (this.#aspectRatio >= 1) {
|
||||||
|
style.minHeight = `${RESIZER_SIZE}px`;
|
||||||
|
style.minWidth = `${Math.round(this.#aspectRatio * RESIZER_SIZE)}px`;
|
||||||
|
} else {
|
||||||
|
style.minWidth = `${RESIZER_SIZE}px`;
|
||||||
|
style.minHeight = `${Math.round(RESIZER_SIZE / this.#aspectRatio)}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
static deserialize(data, parent) {
|
||||||
|
const editor = super.deserialize(data, parent);
|
||||||
|
|
||||||
|
editor.thickness = data.thickness;
|
||||||
|
editor.color = Util.makeHexColor(...data.color);
|
||||||
|
|
||||||
|
const [pageWidth, pageHeight] = parent.pageDimensions;
|
||||||
|
const width = editor.width * pageWidth;
|
||||||
|
const height = editor.height * pageHeight;
|
||||||
|
const scaleFactor = parent.scaleFactor;
|
||||||
|
const padding = data.thickness / 2;
|
||||||
|
|
||||||
|
editor.#aspectRatio = width / height;
|
||||||
|
editor.#disableEditing = true;
|
||||||
|
editor.#realWidth = Math.round(width);
|
||||||
|
editor.#realHeight = Math.round(height);
|
||||||
|
|
||||||
|
for (const { bezier } of data.paths) {
|
||||||
|
const path = [];
|
||||||
|
editor.paths.push(path);
|
||||||
|
let p0 = scaleFactor * (bezier[0] - padding);
|
||||||
|
let p1 = scaleFactor * (height - bezier[1] - padding);
|
||||||
|
for (let i = 2, ii = bezier.length; i < ii; i += 6) {
|
||||||
|
const p10 = scaleFactor * (bezier[i] - padding);
|
||||||
|
const p11 = scaleFactor * (height - bezier[i + 1] - padding);
|
||||||
|
const p20 = scaleFactor * (bezier[i + 2] - padding);
|
||||||
|
const p21 = scaleFactor * (height - bezier[i + 3] - padding);
|
||||||
|
const p30 = scaleFactor * (bezier[i + 4] - padding);
|
||||||
|
const p31 = scaleFactor * (height - bezier[i + 5] - padding);
|
||||||
|
path.push([
|
||||||
|
[p0, p1],
|
||||||
|
[p10, p11],
|
||||||
|
[p20, p21],
|
||||||
|
[p30, p31],
|
||||||
|
]);
|
||||||
|
p0 = p30;
|
||||||
|
p1 = p31;
|
||||||
|
}
|
||||||
|
const path2D = this.#buildPath2D(path);
|
||||||
|
editor.bezierPath2D.push(path2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bbox = editor.#getBbox();
|
||||||
|
editor.#baseWidth = bbox[2] - bbox[0];
|
||||||
|
editor.#baseHeight = bbox[3] - bbox[1];
|
||||||
|
editor.#setScaleFactor(width, height);
|
||||||
|
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
serialize() {
|
serialize() {
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
|
@ -284,16 +284,25 @@ class KeyboardManager {
|
|||||||
* It has to be used as a singleton.
|
* It has to be used as a singleton.
|
||||||
*/
|
*/
|
||||||
class ClipboardManager {
|
class ClipboardManager {
|
||||||
constructor() {
|
#elements = null;
|
||||||
this.element = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy an element.
|
* Copy an element.
|
||||||
* @param {AnnotationEditor} element
|
* @param {AnnotationEditor|Array<AnnotationEditor>} element
|
||||||
*/
|
*/
|
||||||
copy(element) {
|
copy(element) {
|
||||||
this.element = element.copy();
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(element)) {
|
||||||
|
this.#elements = element.map(el => el.serialize());
|
||||||
|
} else {
|
||||||
|
this.#elements = [element.serialize()];
|
||||||
|
}
|
||||||
|
this.#elements = this.#elements.filter(el => !!el);
|
||||||
|
if (this.#elements.length === 0) {
|
||||||
|
this.#elements = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,7 +310,7 @@ class ClipboardManager {
|
|||||||
* @returns {AnnotationEditor|null}
|
* @returns {AnnotationEditor|null}
|
||||||
*/
|
*/
|
||||||
paste() {
|
paste() {
|
||||||
return this.element?.copy() || null;
|
return this.#elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,11 +318,11 @@ class ClipboardManager {
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isEmpty() {
|
isEmpty() {
|
||||||
return this.element === null;
|
return this.#elements === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.element = null;
|
this.#elements = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,6 +406,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#commandManager = new CommandManager();
|
#commandManager = new CommandManager();
|
||||||
|
|
||||||
|
#currentPageIndex = 0;
|
||||||
|
|
||||||
#editorTypes = null;
|
#editorTypes = null;
|
||||||
|
|
||||||
#eventBus = null;
|
#eventBus = null;
|
||||||
@ -413,6 +424,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#boundOnEditingAction = this.onEditingAction.bind(this);
|
#boundOnEditingAction = this.onEditingAction.bind(this);
|
||||||
|
|
||||||
|
#boundOnPageChanging = this.onPageChanging.bind(this);
|
||||||
|
|
||||||
#previousStates = {
|
#previousStates = {
|
||||||
isEditing: false,
|
isEditing: false,
|
||||||
isEmpty: true,
|
isEmpty: true,
|
||||||
@ -425,10 +438,12 @@ class AnnotationEditorUIManager {
|
|||||||
constructor(eventBus) {
|
constructor(eventBus) {
|
||||||
this.#eventBus = eventBus;
|
this.#eventBus = eventBus;
|
||||||
this.#eventBus._on("editingaction", this.#boundOnEditingAction);
|
this.#eventBus._on("editingaction", this.#boundOnEditingAction);
|
||||||
|
this.#eventBus._on("pagechanging", this.#boundOnPageChanging);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.#eventBus._off("editingaction", this.#boundOnEditingAction);
|
this.#eventBus._off("editingaction", this.#boundOnEditingAction);
|
||||||
|
this.#eventBus._off("pagechanging", this.#boundOnPageChanging);
|
||||||
for (const layer of this.#allLayers.values()) {
|
for (const layer of this.#allLayers.values()) {
|
||||||
layer.destroy();
|
layer.destroy();
|
||||||
}
|
}
|
||||||
@ -439,6 +454,10 @@ class AnnotationEditorUIManager {
|
|||||||
this.#commandManager.destroy();
|
this.#commandManager.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageChanging({ pageNumber }) {
|
||||||
|
this.#currentPageIndex = pageNumber - 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute an action for a given name.
|
* Execute an action for a given name.
|
||||||
* For example, the user can click on the "Undo" entry in the context menu
|
* For example, the user can click on the "Undo" entry in the context menu
|
||||||
@ -822,18 +841,21 @@ class AnnotationEditorUIManager {
|
|||||||
* @returns {undefined}
|
* @returns {undefined}
|
||||||
*/
|
*/
|
||||||
paste() {
|
paste() {
|
||||||
const editor = this.#clipboardManager.paste();
|
if (this.#clipboardManager.isEmpty()) {
|
||||||
if (!editor) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: paste in the current visible layer.
|
|
||||||
|
const layer = this.#allLayers.get(this.#currentPageIndex);
|
||||||
|
const newEditors = this.#clipboardManager
|
||||||
|
.paste()
|
||||||
|
.map(data => layer.deserialize(data));
|
||||||
|
|
||||||
const cmd = () => {
|
const cmd = () => {
|
||||||
this.#addEditorToLayer(editor);
|
newEditors.map(editor => this.#addEditorToLayer(editor));
|
||||||
};
|
};
|
||||||
const undo = () => {
|
const undo = () => {
|
||||||
editor.remove();
|
newEditors.map(editor => editor.remove());
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addCommands({ cmd, undo, mustExec: true });
|
this.addCommands({ cmd, undo, mustExec: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ describe("Editor", () => {
|
|||||||
el.innerText.trimEnd()
|
el.innerText.trimEnd()
|
||||||
);
|
);
|
||||||
|
|
||||||
let pastedContent = await page.$eval(`${editorPrefix}2`, el =>
|
let pastedContent = await page.$eval(`${editorPrefix}1`, el =>
|
||||||
el.innerText.trimEnd()
|
el.innerText.trimEnd()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ describe("Editor", () => {
|
|||||||
await page.keyboard.press("v");
|
await page.keyboard.press("v");
|
||||||
await page.keyboard.up("Control");
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
pastedContent = await page.$eval(`${editorPrefix}4`, el =>
|
pastedContent = await page.$eval(`${editorPrefix}2`, el =>
|
||||||
el.innerText.trimEnd()
|
el.innerText.trimEnd()
|
||||||
);
|
);
|
||||||
expect(pastedContent)
|
expect(pastedContent)
|
||||||
@ -132,7 +132,7 @@ describe("Editor", () => {
|
|||||||
await page.keyboard.press("Backspace");
|
await page.keyboard.press("Backspace");
|
||||||
await page.keyboard.up("Control");
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
for (const n of [0, 2, 4]) {
|
for (const n of [0, 1, 2]) {
|
||||||
const hasEditor = await page.evaluate(sel => {
|
const hasEditor = await page.evaluate(sel => {
|
||||||
return !!document.querySelector(sel);
|
return !!document.querySelector(sel);
|
||||||
}, `${editorPrefix}${n}`);
|
}, `${editorPrefix}${n}`);
|
||||||
@ -153,9 +153,9 @@ describe("Editor", () => {
|
|||||||
|
|
||||||
const data = "Hello PDF.js World !!";
|
const data = "Hello PDF.js World !!";
|
||||||
await page.mouse.click(rect.x + 100, rect.y + 100);
|
await page.mouse.click(rect.x + 100, rect.y + 100);
|
||||||
await page.type(`${editorPrefix}5 .internal`, data);
|
await page.type(`${editorPrefix}3 .internal`, data);
|
||||||
|
|
||||||
const editorRect = await page.$eval(`${editorPrefix}5`, el => {
|
const editorRect = await page.$eval(`${editorPrefix}3`, el => {
|
||||||
const { x, y, width, height } = el.getBoundingClientRect();
|
const { x, y, width, height } = el.getBoundingClientRect();
|
||||||
return { x, y, width, height };
|
return { x, y, width, height };
|
||||||
});
|
});
|
||||||
@ -181,7 +181,7 @@ describe("Editor", () => {
|
|||||||
|
|
||||||
let hasEditor = await page.evaluate(sel => {
|
let hasEditor = await page.evaluate(sel => {
|
||||||
return !!document.querySelector(sel);
|
return !!document.querySelector(sel);
|
||||||
}, `${editorPrefix}7`);
|
}, `${editorPrefix}4`);
|
||||||
|
|
||||||
expect(hasEditor).withContext(`In ${browserName}`).toEqual(true);
|
expect(hasEditor).withContext(`In ${browserName}`).toEqual(true);
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ describe("Editor", () => {
|
|||||||
|
|
||||||
hasEditor = await page.evaluate(sel => {
|
hasEditor = await page.evaluate(sel => {
|
||||||
return !!document.querySelector(sel);
|
return !!document.querySelector(sel);
|
||||||
}, `${editorPrefix}7`);
|
}, `${editorPrefix}4`);
|
||||||
|
|
||||||
expect(hasEditor).withContext(`In ${browserName}`).toEqual(false);
|
expect(hasEditor).withContext(`In ${browserName}`).toEqual(false);
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user