Merge pull request #16750 from calixteman/editor_keeboard_freetext

[Editor] Add the possibility to move an empty freetext editor with the keyboard (bug 1845088)
This commit is contained in:
calixteman 2023-07-27 11:38:17 +02:00 committed by GitHub
commit 82faae26c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 175 additions and 36 deletions

View File

@ -24,7 +24,11 @@ import {
shadow,
Util,
} from "../../shared/util.js";
import { bindEvents, KeyboardManager } from "./tools.js";
import {
AnnotationEditorUIManager,
bindEvents,
KeyboardManager,
} from "./tools.js";
import { AnnotationEditor } from "./editor.js";
import { FreeTextAnnotationElement } from "../annotation_layer.js";
@ -61,6 +65,9 @@ class FreeTextEditor extends AnnotationEditor {
static _defaultFontSize = 10;
static get _keyboardManager() {
const arrowChecker = self => self.isEmpty();
const small = AnnotationEditorUIManager.TRANSLATE_SMALL;
const big = AnnotationEditorUIManager.TRANSLATE_BIG;
return shadow(
this,
"_keyboardManager",
@ -77,6 +84,46 @@ class FreeTextEditor extends AnnotationEditor {
["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"],
FreeTextEditor.prototype.commitOrRemove,
],
[
["ArrowLeft", "mac+ArrowLeft"],
FreeTextEditor.prototype._translateEmpty,
{ args: [-small, 0], checker: arrowChecker },
],
[
["ctrl+ArrowLeft", "mac+shift+ArrowLeft"],
FreeTextEditor.prototype._translateEmpty,
{ args: [-big, 0], checker: arrowChecker },
],
[
["ArrowRight", "mac+ArrowRight"],
FreeTextEditor.prototype._translateEmpty,
{ args: [small, 0], checker: arrowChecker },
],
[
["ctrl+ArrowRight", "mac+shift+ArrowRight"],
FreeTextEditor.prototype._translateEmpty,
{ args: [big, 0], checker: arrowChecker },
],
[
["ArrowUp", "mac+ArrowUp"],
FreeTextEditor.prototype._translateEmpty,
{ args: [0, -small], checker: arrowChecker },
],
[
["ctrl+ArrowUp", "mac+shift+ArrowUp"],
FreeTextEditor.prototype._translateEmpty,
{ args: [0, -big], checker: arrowChecker },
],
[
["ArrowDown", "mac+ArrowDown"],
FreeTextEditor.prototype._translateEmpty,
{ args: [0, small], checker: arrowChecker },
],
[
["ctrl+ArrowDown", "mac+shift+ArrowDown"],
FreeTextEditor.prototype._translateEmpty,
{ args: [0, big], checker: arrowChecker },
],
])
);
}
@ -209,6 +256,15 @@ class FreeTextEditor extends AnnotationEditor {
});
}
/**
* Helper to translate the editor with the keyboard when it's empty.
* @param {number} x in page units.
* @param {number} y in page units.
*/
_translateEmpty(x, y) {
this._uiManager.translateSelectedEditors(x, y, /* noCommit = */ true);
}
/** @inheritdoc */
getInitialTranslation() {
// The start of the base line is where the user clicked.

View File

@ -561,9 +561,9 @@ class AnnotationEditorUIManager {
#container = null;
static #TRANSLATE_SMALL = 1; // page units.
static TRANSLATE_SMALL = 1; // page units.
static #TRANSLATE_BIG = 10; // page units.
static TRANSLATE_BIG = 10; // page units.
static get _keyboardManager() {
const arrowChecker = self => {
@ -576,8 +576,8 @@ class AnnotationEditorUIManager {
self.hasSomethingToControl()
);
};
const small = this.#TRANSLATE_SMALL;
const big = this.#TRANSLATE_BIG;
const small = this.TRANSLATE_SMALL;
const big = this.TRANSLATE_BIG;
return shadow(
this,
"_keyboardManager",
@ -1379,8 +1379,10 @@ class AnnotationEditorUIManager {
});
}
translateSelectedEditors(x, y) {
this.commitOrRemove();
translateSelectedEditors(x, y, noCommit = false) {
if (!noCommit) {
this.commitOrRemove();
}
if (!this.hasSelection) {
return;
}

View File

@ -44,6 +44,17 @@ const copyPaste = async page => {
await promise;
};
const clearAll = async page => {
await page.keyboard.down("Control");
await page.keyboard.press("a");
await page.keyboard.up("Control");
await page.waitForTimeout(10);
await page.keyboard.down("Control");
await page.keyboard.press("Backspace");
await page.keyboard.up("Control");
await page.waitForTimeout(10);
};
describe("FreeText Editor", () => {
describe("FreeText", () => {
let pages;
@ -140,17 +151,7 @@ describe("FreeText Editor", () => {
it("must clear all", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.keyboard.down("Control");
await page.keyboard.press("a");
await page.keyboard.up("Control");
await page.waitForTimeout(10);
await page.keyboard.down("Control");
await page.keyboard.press("Backspace");
await page.keyboard.up("Control");
await page.waitForTimeout(10);
await clearAll(page);
for (const n of [0, 1, 2]) {
const hasEditor = await page.evaluate(sel => {
@ -285,6 +286,7 @@ describe("FreeText Editor", () => {
// Commit.
await page.keyboard.press("Escape");
await page.waitForTimeout(10);
const ariaOwns = await page.$eval(".textLayer", el => {
const span = el.querySelector(`span[pdfjs="true"]`);
@ -306,17 +308,7 @@ describe("FreeText Editor", () => {
return { x, y };
});
await page.keyboard.down("Control");
await page.keyboard.press("a");
await page.keyboard.up("Control");
await page.waitForTimeout(10);
await page.keyboard.down("Control");
await page.keyboard.press("Backspace");
await page.keyboard.up("Control");
await page.waitForTimeout(10);
await clearAll(page);
const data = "Hello PDF.js World !!";
await page.mouse.click(rect.x + 100, rect.y + 100);
@ -377,13 +369,7 @@ describe("FreeText Editor", () => {
return { x, y };
});
await page.keyboard.down("Control");
await page.keyboard.press("a");
await page.keyboard.up("Control");
await page.keyboard.down("Control");
await page.keyboard.press("Backspace");
await page.keyboard.up("Control");
await clearAll(page);
await page.mouse.click(rect.x + 200, rect.y + 100);
@ -1918,5 +1904,100 @@ describe("FreeText Editor", () => {
})
);
});
it("must check the position of an empty freetext", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await clearAll(page);
const rect = await page.$eval(".annotationEditorLayer", el => {
const { x, y } = el.getBoundingClientRect();
return { x, y };
});
const data = "Hello PDF.js World !!";
await page.mouse.click(rect.x + 100, rect.y + 100);
await page.type(`${getEditorSelector(1)} .internal`, data);
const editorRect = await page.$eval(getEditorSelector(1), el => {
const { x, y, width, height } = el.getBoundingClientRect();
return {
x,
y,
width,
height,
};
});
// Commit.
await page.mouse.click(
editorRect.x,
editorRect.y + 2 * editorRect.height
);
await page.waitForTimeout(10);
const [pageX, pageY] = await getFirstSerialized(page, x => x.rect);
await clearAll(page);
await page.mouse.click(rect.x + 100, rect.y + 100);
for (let i = 0; i < 20; i++) {
await page.keyboard.press("ArrowRight");
await page.waitForTimeout(1);
}
await page.waitForTimeout(10);
for (let i = 0; i < 2; i++) {
await page.keyboard.down("Control");
await page.keyboard.press("ArrowDown");
await page.keyboard.up("Control");
await page.waitForTimeout(1);
}
await page.waitForTimeout(10);
for (let i = 0; i < 20; i++) {
await page.keyboard.press("ArrowLeft");
await page.waitForTimeout(1);
}
await page.waitForTimeout(10);
for (let i = 0; i < 2; i++) {
await page.keyboard.down("Control");
await page.keyboard.press("ArrowUp");
await page.keyboard.up("Control");
await page.waitForTimeout(1);
}
await page.waitForTimeout(10);
for (let i = 0; i < 2; i++) {
await page.keyboard.down("Control");
await page.keyboard.press("ArrowRight");
await page.keyboard.up("Control");
await page.waitForTimeout(1);
}
await page.waitForTimeout(10);
for (let i = 0; i < 2; i++) {
await page.keyboard.down("Control");
await page.keyboard.press("ArrowLeft");
await page.keyboard.up("Control");
await page.waitForTimeout(1);
}
await page.waitForTimeout(10);
await page.type(`${getEditorSelector(2)} .internal`, data);
await page.keyboard.press("Escape");
await page.waitForTimeout(10);
const [newX, newY] = await getFirstSerialized(page, x => x.rect);
expect(Math.round(newX))
.withContext(`In ${browserName}`)
.toEqual(Math.round(pageX));
expect(Math.round(newY))
.withContext(`In ${browserName}`)
.toEqual(Math.round(pageY));
})
);
});
});
});