Merge pull request #15200 from calixteman/multiple_selection
[Editor] Add the ability to make multiple selections (bug 1779582)
This commit is contained in:
commit
6138e16ce2
@ -136,7 +136,7 @@ class AnnotationEditorLayer {
|
|||||||
} else {
|
} else {
|
||||||
this.enableClick();
|
this.enableClick();
|
||||||
}
|
}
|
||||||
this.setActiveEditor(null);
|
this.#uiManager.unselectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
addInkEditorIfNeeded(isCommitting) {
|
addInkEditorIfNeeded(isCommitting) {
|
||||||
@ -210,14 +210,6 @@ class AnnotationEditorLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.#uiManager.setActiveEditor(editor);
|
this.#uiManager.setActiveEditor(editor);
|
||||||
|
|
||||||
if (currentActive && currentActive !== editor) {
|
|
||||||
currentActive.commitOrRemove();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editor) {
|
|
||||||
this.#uiManager.unselectAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enableClick() {
|
enableClick() {
|
||||||
@ -250,11 +242,19 @@ class AnnotationEditorLayer {
|
|||||||
this.#uiManager.removeEditor(editor);
|
this.#uiManager.removeEditor(editor);
|
||||||
this.detach(editor);
|
this.detach(editor);
|
||||||
this.annotationStorage.removeKey(editor.id);
|
this.annotationStorage.removeKey(editor.id);
|
||||||
editor.div.remove();
|
editor.div.style.display = "none";
|
||||||
editor.isAttachedToDOM = false;
|
setTimeout(() => {
|
||||||
if (this.#uiManager.isActive(editor) || this.#editors.size === 0) {
|
// When the div is removed from DOM the focus can move on the
|
||||||
this.setActiveEditor(null);
|
// document.body, so we just slightly postpone the removal in
|
||||||
}
|
// order to let an element potentially grab the focus before
|
||||||
|
// the body.
|
||||||
|
editor.div.style.display = "";
|
||||||
|
editor.div.remove();
|
||||||
|
editor.isAttachedToDOM = false;
|
||||||
|
if (document.activeElement === document.body) {
|
||||||
|
this.#uiManager.focusMainContainer();
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
if (!this.#isCleaningUp) {
|
if (!this.#isCleaningUp) {
|
||||||
this.addInkEditorIfNeeded(/* isCommitting = */ false);
|
this.addInkEditorIfNeeded(/* isCommitting = */ false);
|
||||||
@ -271,10 +271,6 @@ class AnnotationEditorLayer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#uiManager.isActive(editor)) {
|
|
||||||
editor.parent?.setActiveEditor(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.attach(editor);
|
this.attach(editor);
|
||||||
editor.pageIndex = this.pageIndex;
|
editor.pageIndex = this.pageIndex;
|
||||||
editor.parent?.detach(editor);
|
editor.parent?.detach(editor);
|
||||||
@ -546,6 +542,42 @@ class AnnotationEditorLayer {
|
|||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the last selected editor.
|
||||||
|
* @param {AnnotationEditor} editor
|
||||||
|
*/
|
||||||
|
setSelected(editor) {
|
||||||
|
this.#uiManager.setSelected(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the editor is selected.
|
||||||
|
* @param {AnnotationEditor} editor
|
||||||
|
*/
|
||||||
|
isSelected(editor) {
|
||||||
|
return this.#uiManager.isSelected(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unselect an editor.
|
||||||
|
* @param {AnnotationEditor} editor
|
||||||
|
*/
|
||||||
|
unselect(editor) {
|
||||||
|
this.#uiManager.unselect(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isMultipleSelection() {
|
||||||
|
return this.#uiManager.isMultipleSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An editor just got a mousedown with ctrl key pressed.
|
||||||
|
* @param {boolean}} isMultiple
|
||||||
|
*/
|
||||||
|
set isMultipleSelection(isMultiple) {
|
||||||
|
this.#uiManager.isMultipleSelection = isMultiple;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mouseclick callback.
|
* Mouseclick callback.
|
||||||
* @param {MouseEvent} event
|
* @param {MouseEvent} event
|
||||||
@ -662,7 +694,6 @@ class AnnotationEditorLayer {
|
|||||||
* @param {Object} parameters
|
* @param {Object} parameters
|
||||||
*/
|
*/
|
||||||
update(parameters) {
|
update(parameters) {
|
||||||
this.setActiveEditor(null);
|
|
||||||
this.viewport = parameters.viewport;
|
this.viewport = parameters.viewport;
|
||||||
this.setDimensions();
|
this.setDimensions();
|
||||||
this.updateMode();
|
this.updateMode();
|
||||||
|
@ -16,12 +16,8 @@
|
|||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("./annotation_editor_layer.js").AnnotationEditorLayer} AnnotationEditorLayer */
|
/** @typedef {import("./annotation_editor_layer.js").AnnotationEditorLayer} AnnotationEditorLayer */
|
||||||
|
|
||||||
import {
|
|
||||||
AnnotationEditorPrefix,
|
|
||||||
shadow,
|
|
||||||
unreachable,
|
|
||||||
} from "../../shared/util.js";
|
|
||||||
import { bindEvents, ColorManager } from "./tools.js";
|
import { bindEvents, ColorManager } from "./tools.js";
|
||||||
|
import { shadow, unreachable } from "../../shared/util.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} AnnotationEditorParameters
|
* @typedef {Object} AnnotationEditorParameters
|
||||||
@ -35,8 +31,20 @@ import { bindEvents, ColorManager } from "./tools.js";
|
|||||||
* Base class for editors.
|
* Base class for editors.
|
||||||
*/
|
*/
|
||||||
class AnnotationEditor {
|
class AnnotationEditor {
|
||||||
|
#boundFocusin = this.focusin.bind(this);
|
||||||
|
|
||||||
|
#boundFocusout = this.focusout.bind(this);
|
||||||
|
|
||||||
|
#isEditing = false;
|
||||||
|
|
||||||
|
#isFocused = false;
|
||||||
|
|
||||||
#isInEditMode = false;
|
#isInEditMode = false;
|
||||||
|
|
||||||
|
#wasSelected = false;
|
||||||
|
|
||||||
|
#wasFocused = false;
|
||||||
|
|
||||||
#zIndex = AnnotationEditor._zIndex++;
|
#zIndex = AnnotationEditor._zIndex++;
|
||||||
|
|
||||||
static _colorManager = new ColorManager();
|
static _colorManager = new ColorManager();
|
||||||
@ -88,17 +96,32 @@ class AnnotationEditor {
|
|||||||
this.div.style.zIndex = this.#zIndex;
|
this.div.style.zIndex = this.#zIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#select() {
|
||||||
|
if (this.#wasSelected) {
|
||||||
|
this.parent.unselect(this);
|
||||||
|
this.unselect();
|
||||||
|
this.#wasSelected = true;
|
||||||
|
} else {
|
||||||
|
this.parent.setSelected(this);
|
||||||
|
this.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onfocus callback.
|
* onfocus callback.
|
||||||
*/
|
*/
|
||||||
focusin(/* event */) {
|
focusin(event) {
|
||||||
this.parent.setActiveEditor(this);
|
this.#isFocused =
|
||||||
|
event.target === this.div ||
|
||||||
|
!!event.relatedTarget?.closest(`#${this.id}`);
|
||||||
|
if (event.target === this.div) {
|
||||||
|
this.#select();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onblur callback.
|
* onblur callback.
|
||||||
* @param {FocusEvent} event
|
* @param {FocusEvent} event
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
focusout(event) {
|
focusout(event) {
|
||||||
if (!this.isAttachedToDOM) {
|
if (!this.isAttachedToDOM) {
|
||||||
@ -116,10 +139,14 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
this.commitOrRemove();
|
this.#isFocused = false;
|
||||||
|
if (!this.parent.isMultipleSelection) {
|
||||||
if (!target?.id?.startsWith(AnnotationEditorPrefix)) {
|
this.commitOrRemove();
|
||||||
this.parent.setActiveEditor(null);
|
if (target?.closest(".annotationEditorLayer")) {
|
||||||
|
// We only unselect the element when another editor (or its parent)
|
||||||
|
// is grabbing the focus.
|
||||||
|
this.parent.unselect(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,15 +255,13 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
this.setInForeground();
|
this.setInForeground();
|
||||||
|
|
||||||
|
this.div.addEventListener("focusin", this.#boundFocusin);
|
||||||
|
this.div.addEventListener("focusout", this.#boundFocusout);
|
||||||
|
|
||||||
const [tx, ty] = this.getInitialTranslation();
|
const [tx, ty] = this.getInitialTranslation();
|
||||||
this.translate(tx, ty);
|
this.translate(tx, ty);
|
||||||
|
|
||||||
bindEvents(this, this.div, [
|
bindEvents(this, this.div, ["dragstart", "mousedown", "mouseup"]);
|
||||||
"dragstart",
|
|
||||||
"focusin",
|
|
||||||
"focusout",
|
|
||||||
"mousedown",
|
|
||||||
]);
|
|
||||||
|
|
||||||
return this.div;
|
return this.div;
|
||||||
}
|
}
|
||||||
@ -250,6 +275,23 @@ class AnnotationEditor {
|
|||||||
// Avoid to focus this editor because of a non-left click.
|
// Avoid to focus this editor because of a non-left click.
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMultipleSelection = (this.parent.isMultipleSelection =
|
||||||
|
event.ctrlKey || event.shiftKey);
|
||||||
|
this.#wasSelected = isMultipleSelection && this.parent.isSelected(this);
|
||||||
|
this.#wasFocused = this.#isFocused;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Onmouseup callback.
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
*/
|
||||||
|
mouseup(event) {
|
||||||
|
if (this.#wasFocused) {
|
||||||
|
this.#select();
|
||||||
|
}
|
||||||
|
this.parent.isMultipleSelection = false;
|
||||||
|
this.#wasFocused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRect(tx, ty) {
|
getRect(tx, ty) {
|
||||||
@ -331,7 +373,6 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable edit mode.
|
* Enable edit mode.
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
enableEditMode() {
|
enableEditMode() {
|
||||||
this.#isInEditMode = true;
|
this.#isInEditMode = true;
|
||||||
@ -339,7 +380,6 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable edit mode.
|
* Disable edit mode.
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
disableEditMode() {
|
disableEditMode() {
|
||||||
this.#isInEditMode = false;
|
this.#isInEditMode = false;
|
||||||
@ -374,10 +414,9 @@ class AnnotationEditor {
|
|||||||
* Rebuild the editor in case it has been removed on undo.
|
* Rebuild the editor in case it has been removed on undo.
|
||||||
*
|
*
|
||||||
* To implement in subclasses.
|
* To implement in subclasses.
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
rebuild() {
|
rebuild() {
|
||||||
unreachable("An editor must be rebuildable");
|
this.div?.addEventListener("focusin", this.#boundFocusin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -386,7 +425,6 @@ class AnnotationEditor {
|
|||||||
* new annotation to add to the pdf document.
|
* new annotation to add to the pdf document.
|
||||||
*
|
*
|
||||||
* To implement in subclasses.
|
* To implement in subclasses.
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
serialize() {
|
serialize() {
|
||||||
unreachable("An editor must be serializable");
|
unreachable("An editor must be serializable");
|
||||||
@ -423,10 +461,11 @@ class AnnotationEditor {
|
|||||||
/**
|
/**
|
||||||
* Remove this editor.
|
* Remove this editor.
|
||||||
* It's used on ctrl+backspace action.
|
* It's used on ctrl+backspace action.
|
||||||
*
|
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
*/
|
||||||
remove() {
|
remove() {
|
||||||
|
this.div.removeEventListener("focusin", this.#boundFocusin);
|
||||||
|
this.div.removeEventListener("focusout", this.#boundFocusout);
|
||||||
|
|
||||||
if (!this.isEmpty()) {
|
if (!this.isEmpty()) {
|
||||||
// The editor is removed but it can be back at some point thanks to
|
// The editor is removed but it can be back at some point thanks to
|
||||||
// undo/redo so we must commit it before.
|
// undo/redo so we must commit it before.
|
||||||
@ -439,18 +478,14 @@ class AnnotationEditor {
|
|||||||
* Select this editor.
|
* Select this editor.
|
||||||
*/
|
*/
|
||||||
select() {
|
select() {
|
||||||
if (this.div) {
|
this.div?.classList.add("selectedEditor");
|
||||||
this.div.classList.add("selectedEditor");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unselect this editor.
|
* Unselect this editor.
|
||||||
*/
|
*/
|
||||||
unselect() {
|
unselect() {
|
||||||
if (this.div) {
|
this.div?.classList.remove("selectedEditor");
|
||||||
this.div.classList.remove("selectedEditor");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -494,6 +529,29 @@ class AnnotationEditor {
|
|||||||
get contentDiv() {
|
get contentDiv() {
|
||||||
return this.div;
|
return this.div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true then the editor is currently edited.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isEditing() {
|
||||||
|
return this.#isEditing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to true, it means that this editor is currently edited.
|
||||||
|
* @param {boolean} value
|
||||||
|
*/
|
||||||
|
set isEditing(value) {
|
||||||
|
this.#isEditing = value;
|
||||||
|
if (value) {
|
||||||
|
this.select();
|
||||||
|
this.parent.setSelected(this);
|
||||||
|
this.parent.setActiveEditor(this);
|
||||||
|
} else {
|
||||||
|
this.parent.setActiveEditor(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AnnotationEditor };
|
export { AnnotationEditor };
|
||||||
|
@ -30,6 +30,10 @@ import { AnnotationEditor } from "./editor.js";
|
|||||||
* Basic text editor in order to create a FreeTex annotation.
|
* Basic text editor in order to create a FreeTex annotation.
|
||||||
*/
|
*/
|
||||||
class FreeTextEditor extends AnnotationEditor {
|
class FreeTextEditor extends AnnotationEditor {
|
||||||
|
#boundEditorDivBlur = this.editorDivBlur.bind(this);
|
||||||
|
|
||||||
|
#boundEditorDivFocus = this.editorDivFocus.bind(this);
|
||||||
|
|
||||||
#boundEditorDivKeydown = this.editorDivKeydown.bind(this);
|
#boundEditorDivKeydown = this.editorDivKeydown.bind(this);
|
||||||
|
|
||||||
#color;
|
#color;
|
||||||
@ -199,6 +203,7 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
rebuild() {
|
rebuild() {
|
||||||
|
super.rebuild();
|
||||||
if (this.div === null) {
|
if (this.div === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -220,6 +225,8 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
this.div.draggable = false;
|
this.div.draggable = false;
|
||||||
this.div.removeAttribute("tabIndex");
|
this.div.removeAttribute("tabIndex");
|
||||||
this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown);
|
this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown);
|
||||||
|
this.editorDiv.addEventListener("focus", this.#boundEditorDivFocus);
|
||||||
|
this.editorDiv.addEventListener("blur", this.#boundEditorDivBlur);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@ -231,6 +238,8 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
this.div.draggable = true;
|
this.div.draggable = true;
|
||||||
this.div.tabIndex = 0;
|
this.div.tabIndex = 0;
|
||||||
this.editorDiv.removeEventListener("keydown", this.#boundEditorDivKeydown);
|
this.editorDiv.removeEventListener("keydown", this.#boundEditorDivKeydown);
|
||||||
|
this.editorDiv.removeEventListener("focus", this.#boundEditorDivFocus);
|
||||||
|
this.editorDiv.removeEventListener("blur", this.#boundEditorDivBlur);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@ -251,6 +260,7 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
remove() {
|
remove() {
|
||||||
|
this.isEditing = false;
|
||||||
this.parent.setEditingState(true);
|
this.parent.setEditingState(true);
|
||||||
super.remove();
|
super.remove();
|
||||||
}
|
}
|
||||||
@ -333,6 +343,14 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
FreeTextEditor._keyboardManager.exec(this, event);
|
FreeTextEditor._keyboardManager.exec(this, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editorDivFocus(event) {
|
||||||
|
this.isEditing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
editorDivBlur(event) {
|
||||||
|
this.isEditing = false;
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
disableEditing() {
|
disableEditing() {
|
||||||
this.editorDiv.setAttribute("role", "comment");
|
this.editorDiv.setAttribute("role", "comment");
|
||||||
|
@ -123,8 +123,16 @@ class InkEditor extends AnnotationEditor {
|
|||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
get propertiesToUpdate() {
|
get propertiesToUpdate() {
|
||||||
return [
|
return [
|
||||||
[AnnotationEditorParamsType.INK_THICKNESS, this.thickness],
|
[
|
||||||
[AnnotationEditorParamsType.INK_COLOR, this.color],
|
AnnotationEditorParamsType.INK_THICKNESS,
|
||||||
|
this.thickness || InkEditor._defaultThickness,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AnnotationEditorParamsType.INK_COLOR,
|
||||||
|
this.color ||
|
||||||
|
InkEditor._defaultColor ||
|
||||||
|
AnnotationEditor._defaultLineColor,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +182,7 @@ class InkEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
rebuild() {
|
rebuild() {
|
||||||
|
super.rebuild();
|
||||||
if (this.div === null) {
|
if (this.div === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -284,6 +293,7 @@ class InkEditor extends AnnotationEditor {
|
|||||||
* @param {number} y
|
* @param {number} y
|
||||||
*/
|
*/
|
||||||
#startDrawing(x, y) {
|
#startDrawing(x, y) {
|
||||||
|
this.isEditing = true;
|
||||||
if (!this.#isCanvasInitialized) {
|
if (!this.#isCanvasInitialized) {
|
||||||
this.#isCanvasInitialized = true;
|
this.#isCanvasInitialized = true;
|
||||||
this.#setCanvasDims();
|
this.#setCanvasDims();
|
||||||
@ -390,6 +400,7 @@ class InkEditor extends AnnotationEditor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isEditing = false;
|
||||||
this.disableEditMode();
|
this.disableEditMode();
|
||||||
|
|
||||||
// This editor must be on top of the main ink editor.
|
// This editor must be on top of the main ink editor.
|
||||||
@ -408,8 +419,8 @@ class InkEditor extends AnnotationEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
focusin(/* event */) {
|
focusin(event) {
|
||||||
super.focusin();
|
super.focusin(event);
|
||||||
this.enableEditMode();
|
this.enableEditMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,19 +377,19 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#currentPageIndex = 0;
|
#currentPageIndex = 0;
|
||||||
|
|
||||||
|
#isMultipleSelection = false;
|
||||||
|
|
||||||
#editorTypes = null;
|
#editorTypes = null;
|
||||||
|
|
||||||
#eventBus = null;
|
#eventBus = null;
|
||||||
|
|
||||||
#idManager = new IdManager();
|
#idManager = new IdManager();
|
||||||
|
|
||||||
#isAllSelected = false;
|
|
||||||
|
|
||||||
#isEnabled = false;
|
#isEnabled = false;
|
||||||
|
|
||||||
#mode = AnnotationEditorType.NONE;
|
#mode = AnnotationEditorType.NONE;
|
||||||
|
|
||||||
#previousActiveEditor = null;
|
#selectedEditors = new Set();
|
||||||
|
|
||||||
#boundKeydown = this.keydown.bind(this);
|
#boundKeydown = this.keydown.bind(this);
|
||||||
|
|
||||||
@ -435,6 +435,7 @@ class AnnotationEditorUIManager {
|
|||||||
],
|
],
|
||||||
AnnotationEditorUIManager.prototype.delete,
|
AnnotationEditorUIManager.prototype.delete,
|
||||||
],
|
],
|
||||||
|
[["Escape"], AnnotationEditorUIManager.prototype.unselectAll],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
constructor(container, eventBus) {
|
constructor(container, eventBus) {
|
||||||
@ -456,6 +457,7 @@ class AnnotationEditorUIManager {
|
|||||||
this.#allLayers.clear();
|
this.#allLayers.clear();
|
||||||
this.#allEditors.clear();
|
this.#allEditors.clear();
|
||||||
this.#activeEditor = null;
|
this.#activeEditor = null;
|
||||||
|
this.#selectedEditors.clear();
|
||||||
this.#clipboardManager.destroy();
|
this.#clipboardManager.destroy();
|
||||||
this.#commandManager.destroy();
|
this.#commandManager.destroy();
|
||||||
}
|
}
|
||||||
@ -470,6 +472,10 @@ class AnnotationEditorUIManager {
|
|||||||
layer?.onTextLayerRendered();
|
layer?.onTextLayerRendered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focusMainContainer() {
|
||||||
|
this.#container.focus();
|
||||||
|
}
|
||||||
|
|
||||||
#addKeyboardManager() {
|
#addKeyboardManager() {
|
||||||
// The keyboard events are caught at the container level in order to be able
|
// The keyboard events are caught at the container level in order to be able
|
||||||
// to execute some callbacks even if the current page doesn't have focus.
|
// to execute some callbacks even if the current page doesn't have focus.
|
||||||
@ -631,10 +637,10 @@ class AnnotationEditorUIManager {
|
|||||||
* @param {*} value
|
* @param {*} value
|
||||||
*/
|
*/
|
||||||
updateParams(type, value) {
|
updateParams(type, value) {
|
||||||
(this.#activeEditor || this.#previousActiveEditor)?.updateParams(
|
for (const editor of this.#selectedEditors) {
|
||||||
type,
|
editor.updateParams(type, value);
|
||||||
value
|
}
|
||||||
);
|
|
||||||
for (const editorType of this.#editorTypes) {
|
for (const editorType of this.#editorTypes) {
|
||||||
editorType.updateDefaultParams(type, value);
|
editorType.updateDefaultParams(type, value);
|
||||||
}
|
}
|
||||||
@ -656,6 +662,7 @@ class AnnotationEditorUIManager {
|
|||||||
* Disable all the layers.
|
* Disable all the layers.
|
||||||
*/
|
*/
|
||||||
#disableAll() {
|
#disableAll() {
|
||||||
|
this.unselectAll();
|
||||||
if (this.#isEnabled) {
|
if (this.#isEnabled) {
|
||||||
this.#isEnabled = false;
|
this.#isEnabled = false;
|
||||||
for (const layer of this.#allLayers.values()) {
|
for (const layer of this.#allLayers.values()) {
|
||||||
@ -702,6 +709,9 @@ class AnnotationEditorUIManager {
|
|||||||
*/
|
*/
|
||||||
removeEditor(editor) {
|
removeEditor(editor) {
|
||||||
this.#allEditors.delete(editor.id);
|
this.#allEditors.delete(editor.id);
|
||||||
|
if (this.hasSelection) {
|
||||||
|
this.#selectedEditors.delete(editor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -726,24 +736,80 @@ class AnnotationEditorUIManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#previousActiveEditor = this.#activeEditor;
|
|
||||||
|
|
||||||
this.#activeEditor = editor;
|
this.#activeEditor = editor;
|
||||||
if (editor) {
|
if (editor) {
|
||||||
this.#dispatchUpdateUI(editor.propertiesToUpdate);
|
this.#dispatchUpdateUI(editor.propertiesToUpdate);
|
||||||
this.#dispatchUpdateStates({ hasSelectedEditor: true });
|
|
||||||
} else {
|
|
||||||
this.#dispatchUpdateStates({ hasSelectedEditor: false });
|
|
||||||
if (this.#previousActiveEditor) {
|
|
||||||
this.#dispatchUpdateUI(this.#previousActiveEditor.propertiesToUpdate);
|
|
||||||
} else {
|
|
||||||
for (const editorType of this.#editorTypes) {
|
|
||||||
this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the last selected editor.
|
||||||
|
* @param {AnnotationEditor} editor
|
||||||
|
*/
|
||||||
|
setSelected(editor) {
|
||||||
|
if (!this.#isMultipleSelection) {
|
||||||
|
if (this.#selectedEditors.has(editor)) {
|
||||||
|
if (this.#selectedEditors.size > 1) {
|
||||||
|
for (const ed of this.#selectedEditors) {
|
||||||
|
if (ed !== editor) {
|
||||||
|
ed.unselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.#selectedEditors.clear();
|
||||||
|
this.#selectedEditors.add(editor);
|
||||||
|
this.#dispatchUpdateUI(editor.propertiesToUpdate);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ed of this.#selectedEditors) {
|
||||||
|
ed.unselect();
|
||||||
|
}
|
||||||
|
this.#selectedEditors.clear();
|
||||||
|
}
|
||||||
|
this.#selectedEditors.add(editor);
|
||||||
|
this.#dispatchUpdateUI(editor.propertiesToUpdate);
|
||||||
|
this.#dispatchUpdateStates({
|
||||||
|
hasSelectedEditor: this.hasSelection,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the editor is selected.
|
||||||
|
* @param {AnnotationEditor} editor
|
||||||
|
*/
|
||||||
|
isSelected(editor) {
|
||||||
|
return this.#selectedEditors.has(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unselect an editor.
|
||||||
|
* @param {AnnotationEditor} editor
|
||||||
|
*/
|
||||||
|
unselect(editor) {
|
||||||
|
editor.unselect();
|
||||||
|
this.#selectedEditors.delete(editor);
|
||||||
|
this.#dispatchUpdateStates({
|
||||||
|
hasSelectedEditor: this.hasSelection,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasSelection() {
|
||||||
|
return this.#selectedEditors.size !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isMultipleSelection() {
|
||||||
|
return this.#isMultipleSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An editor just got a mousedown with ctrl key pressed.
|
||||||
|
* @param {boolean} isMultiple
|
||||||
|
*/
|
||||||
|
set isMultipleSelection(isMultiple) {
|
||||||
|
this.#isMultipleSelection = isMultiple;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo the last command.
|
* Undo the last command.
|
||||||
*/
|
*/
|
||||||
@ -795,52 +861,26 @@ class AnnotationEditorUIManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unselect the current editor.
|
|
||||||
*/
|
|
||||||
unselect() {
|
|
||||||
if (this.#activeEditor) {
|
|
||||||
this.#activeEditor.parent.setActiveEditor(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the current editor or all.
|
* Delete the current editor or all.
|
||||||
*/
|
*/
|
||||||
delete() {
|
delete() {
|
||||||
let cmd, undo;
|
if (!this.hasSelection) {
|
||||||
if (this.#isAllSelected) {
|
return;
|
||||||
this.#previousActiveEditor = this.#activeEditor = null;
|
|
||||||
const editors = Array.from(this.#allEditors.values());
|
|
||||||
cmd = () => {
|
|
||||||
for (const editor of editors) {
|
|
||||||
if (!editor.isEmpty()) {
|
|
||||||
editor.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
undo = () => {
|
|
||||||
for (const editor of editors) {
|
|
||||||
this.#addEditorToLayer(editor);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addCommands({ cmd, undo, mustExec: true });
|
|
||||||
} else {
|
|
||||||
if (!this.#activeEditor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const editor = this.#activeEditor;
|
|
||||||
this.#previousActiveEditor = this.#activeEditor = null;
|
|
||||||
cmd = () => {
|
|
||||||
editor.remove();
|
|
||||||
};
|
|
||||||
undo = () => {
|
|
||||||
this.#addEditorToLayer(editor);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editors = [...this.#selectedEditors];
|
||||||
|
const cmd = () => {
|
||||||
|
for (const editor of editors) {
|
||||||
|
editor.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const undo = () => {
|
||||||
|
for (const editor of editors) {
|
||||||
|
this.#addEditorToLayer(editor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.addCommands({ cmd, undo, mustExec: true });
|
this.addCommands({ cmd, undo, mustExec: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,8 +888,8 @@ class AnnotationEditorUIManager {
|
|||||||
* Copy the selected editor.
|
* Copy the selected editor.
|
||||||
*/
|
*/
|
||||||
copy() {
|
copy() {
|
||||||
if (this.#activeEditor) {
|
if (this.hasSelection) {
|
||||||
this.#clipboardManager.copy(this.#activeEditor);
|
this.#clipboardManager.copy([...this.#selectedEditors]);
|
||||||
this.#dispatchUpdateStates({ hasEmptyClipboard: false });
|
this.#dispatchUpdateStates({ hasEmptyClipboard: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -858,10 +898,8 @@ class AnnotationEditorUIManager {
|
|||||||
* Cut the selected editor.
|
* Cut the selected editor.
|
||||||
*/
|
*/
|
||||||
cut() {
|
cut() {
|
||||||
if (this.#activeEditor) {
|
this.copy();
|
||||||
this.#clipboardManager.copy(this.#activeEditor);
|
this.delete();
|
||||||
this.delete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -873,42 +911,63 @@ class AnnotationEditorUIManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.unselectAll();
|
||||||
|
|
||||||
const layer = this.#allLayers.get(this.#currentPageIndex);
|
const layer = this.#allLayers.get(this.#currentPageIndex);
|
||||||
const newEditors = this.#clipboardManager
|
const newEditors = this.#clipboardManager
|
||||||
.paste()
|
.paste()
|
||||||
.map(data => layer.deserialize(data));
|
.map(data => layer.deserialize(data));
|
||||||
|
|
||||||
const cmd = () => {
|
const cmd = () => {
|
||||||
newEditors.map(editor => this.#addEditorToLayer(editor));
|
for (const editor of newEditors) {
|
||||||
|
this.#addEditorToLayer(editor);
|
||||||
|
}
|
||||||
|
this.#selectEditors(newEditors);
|
||||||
};
|
};
|
||||||
const undo = () => {
|
const undo = () => {
|
||||||
newEditors.map(editor => editor.remove());
|
for (const editor of newEditors) {
|
||||||
|
editor.remove();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
this.addCommands({ cmd, undo, mustExec: true });
|
this.addCommands({ cmd, undo, mustExec: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select all the editors.
|
* Select the editors.
|
||||||
|
* @param {Array<AnnotationEditor>} editors
|
||||||
*/
|
*/
|
||||||
selectAll() {
|
#selectEditors(editors) {
|
||||||
this.#isAllSelected = true;
|
this.#selectedEditors.clear();
|
||||||
for (const editor of this.#allEditors.values()) {
|
for (const editor of editors) {
|
||||||
|
if (editor.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.#selectedEditors.add(editor);
|
||||||
editor.select();
|
editor.select();
|
||||||
}
|
}
|
||||||
this.#dispatchUpdateStates({ hasSelectedEditor: true });
|
this.#dispatchUpdateStates({ hasSelectedEditor: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unselect all the editors.
|
* Select all the editors.
|
||||||
|
*/
|
||||||
|
selectAll() {
|
||||||
|
for (const editor of this.#selectedEditors) {
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
this.#selectEditors(this.#allEditors.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unselect all the selected editors.
|
||||||
*/
|
*/
|
||||||
unselectAll() {
|
unselectAll() {
|
||||||
this.#isAllSelected = false;
|
for (const editor of this.#selectedEditors) {
|
||||||
|
|
||||||
for (const editor of this.#allEditors.values()) {
|
|
||||||
editor.unselect();
|
editor.unselect();
|
||||||
}
|
}
|
||||||
|
this.#selectedEditors.clear();
|
||||||
this.#dispatchUpdateStates({
|
this.#dispatchUpdateStates({
|
||||||
hasSelectedEditor: this.#activeEditor !== null,
|
hasSelectedEditor: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,4 +252,176 @@ describe("Editor", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("FreeText (multiselection)", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getSelected(page) {
|
||||||
|
return page.evaluate(prefix => {
|
||||||
|
const elements = document.querySelectorAll(".selectedEditor");
|
||||||
|
const results = [];
|
||||||
|
for (const element of elements) {
|
||||||
|
results.push(parseInt(element.id.slice(prefix.length)));
|
||||||
|
}
|
||||||
|
results.sort();
|
||||||
|
return results;
|
||||||
|
}, editorPrefix.slice(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
it("must select/unselect several editors and check copy, paste and delete operations", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.click("#editorFreeText");
|
||||||
|
|
||||||
|
const rect = await page.$eval(".annotationEditorLayer", el => {
|
||||||
|
// With Chrome something is wrong when serializing a DomRect,
|
||||||
|
// hence we extract the values and just return them.
|
||||||
|
const { x, y } = el.getBoundingClientRect();
|
||||||
|
return { x, y };
|
||||||
|
});
|
||||||
|
|
||||||
|
const editorCenters = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const data = `FreeText ${i}`;
|
||||||
|
await page.mouse.click(
|
||||||
|
rect.x + (i + 1) * 100,
|
||||||
|
rect.y + (i + 1) * 100
|
||||||
|
);
|
||||||
|
await page.type(`${editorPrefix}${i} .internal`, data);
|
||||||
|
|
||||||
|
const editorRect = await page.$eval(`${editorPrefix}${i}`, el => {
|
||||||
|
const { x, y, width, height } = el.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
editorCenters.push({
|
||||||
|
x: editorRect.x + editorRect.width / 2,
|
||||||
|
y: editorRect.y + editorRect.height / 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Commit.
|
||||||
|
await page.mouse.click(
|
||||||
|
editorRect.x,
|
||||||
|
editorRect.y + 2 * editorRect.height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.keyboard.press("a");
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([0, 1, 2, 3]);
|
||||||
|
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.mouse.click(editorCenters[1].x, editorCenters[1].y);
|
||||||
|
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([0, 2, 3]);
|
||||||
|
|
||||||
|
await page.mouse.click(editorCenters[2].x, editorCenters[2].y);
|
||||||
|
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([0, 3]);
|
||||||
|
|
||||||
|
await page.mouse.click(editorCenters[1].x, editorCenters[1].y);
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([0, 1, 3]);
|
||||||
|
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.keyboard.press("c");
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.keyboard.press("v");
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
|
// 0,1,3 are unselected and new pasted editors are selected.
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([4, 5, 6]);
|
||||||
|
|
||||||
|
// No ctrl here, hence all are unselected and 2 is selected.
|
||||||
|
await page.mouse.click(editorCenters[2].x, editorCenters[2].y);
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([2]);
|
||||||
|
|
||||||
|
await page.mouse.click(editorCenters[1].x, editorCenters[1].y);
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([1]);
|
||||||
|
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
|
||||||
|
await page.mouse.click(editorCenters[3].x, editorCenters[3].y);
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([1, 3]);
|
||||||
|
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
|
// Delete 1 and 3.
|
||||||
|
await page.keyboard.press("Backspace");
|
||||||
|
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.keyboard.press("a");
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([0, 2, 4, 5, 6]);
|
||||||
|
|
||||||
|
// Create an empty editor.
|
||||||
|
await page.mouse.click(rect.x + 700, rect.y + 100);
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([7]);
|
||||||
|
|
||||||
|
// Set the focus to 2 and check that only 2 is selected.
|
||||||
|
await page.mouse.click(editorCenters[2].x, editorCenters[2].y);
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([2]);
|
||||||
|
|
||||||
|
// Create an empty editor.
|
||||||
|
await page.mouse.click(rect.x + 700, rect.y + 100);
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([8]);
|
||||||
|
// Dismiss it.
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
|
||||||
|
// Select all.
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.keyboard.press("a");
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
|
||||||
|
// Check that all the editors are correctly selected (and the focus
|
||||||
|
// didn't move to the body when the empty editor was removed).
|
||||||
|
expect(await getSelected(page))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([0, 2, 4, 5, 6]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -47,6 +47,11 @@
|
|||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.annotationEditorLayer .selectedEditor {
|
||||||
|
outline: var(--focus-outline);
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .freeTextEditor {
|
.annotationEditorLayer .freeTextEditor {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@ -94,21 +99,17 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .freeTextEditor:focus-within {
|
.annotationEditorLayer .inkEditor.disabled {
|
||||||
outline: var(--focus-outline);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .inkEditor:not(:focus) {
|
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .freeTextEditor:hover:not(:focus-within),
|
.annotationEditorLayer .inkEditor.disabled.selectedEditor {
|
||||||
.annotationEditorLayer .inkEditor:hover:not(:focus) {
|
resize: horizontal;
|
||||||
outline: var(--hover-outline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .inkEditor.disabled:focus {
|
.annotationEditorLayer .freeTextEditor:hover:not(.selectedEditor),
|
||||||
resize: horizontal;
|
.annotationEditorLayer .inkEditor:hover:not(.selectedEditor) {
|
||||||
|
outline: var(--hover-outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .inkEditor {
|
.annotationEditorLayer .inkEditor {
|
||||||
@ -123,11 +124,6 @@
|
|||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .inkEditor:focus {
|
|
||||||
outline: var(--focus-outline);
|
|
||||||
resize: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationEditorLayer .inkEditor.editing {
|
.annotationEditorLayer .inkEditor.editing {
|
||||||
resize: none;
|
resize: none;
|
||||||
cursor: var(--editorInk-editing-cursor), pointer;
|
cursor: var(--editorInk-editing-cursor), pointer;
|
||||||
@ -140,8 +136,3 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer .selectedEditor {
|
|
||||||
outline: var(--focus-outline);
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user