Merge pull request #17755 from calixteman/bug1881684
[Editor] Add the possibility to move the caret with the keyboard once an highlight has been made (bug 1881684)
This commit is contained in:
commit
7cc34cf4a7
@ -16,11 +16,12 @@
|
|||||||
import {
|
import {
|
||||||
AnnotationEditorParamsType,
|
AnnotationEditorParamsType,
|
||||||
AnnotationEditorType,
|
AnnotationEditorType,
|
||||||
|
shadow,
|
||||||
Util,
|
Util,
|
||||||
} from "../../shared/util.js";
|
} from "../../shared/util.js";
|
||||||
|
import { bindEvents, KeyboardManager } from "./tools.js";
|
||||||
import { FreeOutliner, Outliner } from "./outliner.js";
|
import { FreeOutliner, Outliner } from "./outliner.js";
|
||||||
import { AnnotationEditor } from "./editor.js";
|
import { AnnotationEditor } from "./editor.js";
|
||||||
import { bindEvents } from "./tools.js";
|
|
||||||
import { ColorPicker } from "./color_picker.js";
|
import { ColorPicker } from "./color_picker.js";
|
||||||
import { noContextMenu } from "../display_utils.js";
|
import { noContextMenu } from "../display_utils.js";
|
||||||
|
|
||||||
@ -28,6 +29,10 @@ import { noContextMenu } from "../display_utils.js";
|
|||||||
* Basic draw editor in order to generate an Highlight annotation.
|
* Basic draw editor in order to generate an Highlight annotation.
|
||||||
*/
|
*/
|
||||||
class HighlightEditor extends AnnotationEditor {
|
class HighlightEditor extends AnnotationEditor {
|
||||||
|
#anchorNode = null;
|
||||||
|
|
||||||
|
#anchorOffset = 0;
|
||||||
|
|
||||||
#boxes;
|
#boxes;
|
||||||
|
|
||||||
#clipPathId = null;
|
#clipPathId = null;
|
||||||
@ -36,6 +41,10 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
#focusOutlines = null;
|
#focusOutlines = null;
|
||||||
|
|
||||||
|
#focusNode = null;
|
||||||
|
|
||||||
|
#focusOffset = 0;
|
||||||
|
|
||||||
#highlightDiv = null;
|
#highlightDiv = null;
|
||||||
|
|
||||||
#highlightOutlines = null;
|
#highlightOutlines = null;
|
||||||
@ -44,6 +53,8 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
#isFreeHighlight = false;
|
#isFreeHighlight = false;
|
||||||
|
|
||||||
|
#boundKeydown = this.#keydown.bind(this);
|
||||||
|
|
||||||
#lastPoint = null;
|
#lastPoint = null;
|
||||||
|
|
||||||
#opacity;
|
#opacity;
|
||||||
@ -72,6 +83,20 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
static _freeHighlightClipId = "";
|
static _freeHighlightClipId = "";
|
||||||
|
|
||||||
|
static get _keyboardManager() {
|
||||||
|
const proto = HighlightEditor.prototype;
|
||||||
|
return shadow(
|
||||||
|
this,
|
||||||
|
"_keyboardManager",
|
||||||
|
new KeyboardManager([
|
||||||
|
[["ArrowLeft", "mac+ArrowLeft"], proto._moveCaret, { args: [0] }],
|
||||||
|
[["ArrowRight", "mac+ArrowRight"], proto._moveCaret, { args: [1] }],
|
||||||
|
[["ArrowUp", "mac+ArrowUp"], proto._moveCaret, { args: [2] }],
|
||||||
|
[["ArrowDown", "mac+ArrowDown"], proto._moveCaret, { args: [3] }],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
super({ ...params, name: "highlightEditor" });
|
super({ ...params, name: "highlightEditor" });
|
||||||
this.color = params.color || HighlightEditor._defaultColor;
|
this.color = params.color || HighlightEditor._defaultColor;
|
||||||
@ -86,6 +111,10 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
this.#createFreeOutlines(params);
|
this.#createFreeOutlines(params);
|
||||||
this.#addToDrawLayer();
|
this.#addToDrawLayer();
|
||||||
} else {
|
} else {
|
||||||
|
this.#anchorNode = params.anchorNode;
|
||||||
|
this.#anchorOffset = params.anchorOffset;
|
||||||
|
this.#focusNode = params.focusNode;
|
||||||
|
this.#focusOffset = params.focusOffset;
|
||||||
this.#createOutlines();
|
this.#createOutlines();
|
||||||
this.#addToDrawLayer();
|
this.#addToDrawLayer();
|
||||||
this.rotate(this.rotation);
|
this.rotate(this.rotation);
|
||||||
@ -530,6 +559,8 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
const div = super.render();
|
const div = super.render();
|
||||||
if (this.#isFreeHighlight) {
|
if (this.#isFreeHighlight) {
|
||||||
div.classList.add("free");
|
div.classList.add("free");
|
||||||
|
} else {
|
||||||
|
this.div.addEventListener("keydown", this.#boundKeydown);
|
||||||
}
|
}
|
||||||
const highlightDiv = (this.#highlightDiv = document.createElement("div"));
|
const highlightDiv = (this.#highlightDiv = document.createElement("div"));
|
||||||
div.append(highlightDiv);
|
div.append(highlightDiv);
|
||||||
@ -552,6 +583,36 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
this.parent.drawLayer.removeClass(this.#outlineId, "hovered");
|
this.parent.drawLayer.removeClass(this.#outlineId, "hovered");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#keydown(event) {
|
||||||
|
HighlightEditor._keyboardManager.exec(this, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
_moveCaret(direction) {
|
||||||
|
this.parent.unselect(this);
|
||||||
|
switch (direction) {
|
||||||
|
case 0 /* left */:
|
||||||
|
case 2 /* up */:
|
||||||
|
this.#setCaret(/* start = */ true);
|
||||||
|
break;
|
||||||
|
case 1 /* right */:
|
||||||
|
case 3 /* down */:
|
||||||
|
this.#setCaret(/* start = */ false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#setCaret(start) {
|
||||||
|
if (!this.#anchorNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (start) {
|
||||||
|
selection.setPosition(this.#anchorNode, this.#anchorOffset);
|
||||||
|
} else {
|
||||||
|
selection.setPosition(this.#focusNode, this.#focusOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
select() {
|
select() {
|
||||||
super.select();
|
super.select();
|
||||||
@ -563,6 +624,9 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
unselect() {
|
unselect() {
|
||||||
super.unselect();
|
super.unselect();
|
||||||
this.parent?.drawLayer.removeClass(this.#outlineId, "selected");
|
this.parent?.drawLayer.removeClass(this.#outlineId, "selected");
|
||||||
|
if (!this.#isFreeHighlight) {
|
||||||
|
this.#setCaret(/* start = */ false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#getRotation() {
|
#getRotation() {
|
||||||
|
@ -947,7 +947,7 @@ class AnnotationEditorUIManager {
|
|||||||
if (!selection || selection.isCollapsed) {
|
if (!selection || selection.isCollapsed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { anchorNode } = selection;
|
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
|
||||||
const anchorElement =
|
const anchorElement =
|
||||||
anchorNode.nodeType === Node.TEXT_NODE
|
anchorNode.nodeType === Node.TEXT_NODE
|
||||||
? anchorNode.parentElement
|
? anchorNode.parentElement
|
||||||
@ -966,6 +966,10 @@ class AnnotationEditorUIManager {
|
|||||||
layer.createAndAddNewEditor({ x: 0, y: 0 }, false, {
|
layer.createAndAddNewEditor({ x: 0, y: 0 }, false, {
|
||||||
methodOfCreation,
|
methodOfCreation,
|
||||||
boxes,
|
boxes,
|
||||||
|
anchorNode,
|
||||||
|
anchorOffset,
|
||||||
|
focusNode,
|
||||||
|
focusOffset,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1154,5 +1154,81 @@ describe("Highlight Editor", () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("must check that an highlight can be left with the keyboard", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.click("#editorHighlight");
|
||||||
|
await page.waitForSelector(".annotationEditorLayer.highlightEditing");
|
||||||
|
|
||||||
|
if (browserName === "chrome") {
|
||||||
|
// Unfortunately, we can't test this on Chrome because we can't set
|
||||||
|
// the caret browsing mode to true.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rect = await getSpanRectFromText(
|
||||||
|
page,
|
||||||
|
1,
|
||||||
|
"Dynamic languages such as JavaScript are more difficult to com-"
|
||||||
|
);
|
||||||
|
await page.mouse.click(rect.x + 5, rect.y + rect.height / 2);
|
||||||
|
await page.keyboard.down("Shift");
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
await page.keyboard.press("ArrowRight");
|
||||||
|
}
|
||||||
|
const focusOffset = await page.evaluate(
|
||||||
|
() => window.getSelection().focusOffset
|
||||||
|
);
|
||||||
|
await page.keyboard.up("Shift");
|
||||||
|
|
||||||
|
await page.waitForSelector(getEditorSelector(0));
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
await page.keyboard.press("ArrowRight");
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = await page.evaluate(
|
||||||
|
() => window.getSelection().anchorOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(offset)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(focusOffset + 4);
|
||||||
|
|
||||||
|
rect = await getSpanRectFromText(
|
||||||
|
page,
|
||||||
|
1,
|
||||||
|
"experience and enable a new generation of applications, virtual ma-"
|
||||||
|
);
|
||||||
|
await page.mouse.click(
|
||||||
|
rect.x + rect.width / 2,
|
||||||
|
rect.y + rect.height / 2
|
||||||
|
);
|
||||||
|
await page.keyboard.down("Shift");
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
await page.keyboard.press("ArrowRight");
|
||||||
|
}
|
||||||
|
const anchorOffset = await page.evaluate(
|
||||||
|
() => window.getSelection().anchorOffset
|
||||||
|
);
|
||||||
|
await page.keyboard.up("Shift");
|
||||||
|
|
||||||
|
await page.waitForSelector(getEditorSelector(1));
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
await page.keyboard.press("ArrowLeft");
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = await page.evaluate(
|
||||||
|
() => window.getSelection().anchorOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(offset)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(anchorOffset - 4);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user