Comparing version 1.10.0 to 1.11.0
@@ -14,2 +14,7 @@ "use strict"; | ||
return new class extends SerializableCommand_1.default { | ||
constructor() { | ||
super(...arguments); | ||
// For debugging | ||
this._command = command; | ||
} | ||
serializeToJSON() { | ||
@@ -16,0 +21,0 @@ return command.serialize(); |
@@ -119,2 +119,10 @@ import SerializableCommand from '../commands/SerializableCommand'; | ||
setZIndex(newZIndex: number): SerializableCommand; | ||
/** | ||
* Combines {@link transformBy} and {@link setZIndex} into a single command. | ||
* | ||
* @param newZIndex - The z-index this component should have after applying this command. | ||
* @param originalZIndex - @internal The z-index the component should revert to after unapplying | ||
* this command. | ||
*/ | ||
setZIndexAndTransformBy(affineTransfm: Mat33, newZIndex: number, originalZIndex?: number): SerializableCommand; | ||
isSelectable(): boolean; | ||
@@ -121,0 +129,0 @@ isBackground(): boolean; |
@@ -147,3 +147,5 @@ "use strict"; | ||
// updates the editor. | ||
// This also increases the element's z-index so that it is on top. | ||
// | ||
// The transformed component is also moved to the top (use {@link setZIndexAndTransformBy} to | ||
// avoid this behavior). | ||
transformBy(affineTransfm) { | ||
@@ -154,4 +156,14 @@ return new AbstractComponent.TransformElementCommand(affineTransfm, this.getId(), this); | ||
setZIndex(newZIndex) { | ||
return new AbstractComponent.TransformElementCommand(math_1.Mat33.identity, this.getId(), this, newZIndex, this.getZIndex()); | ||
return new AbstractComponent.TransformElementCommand(math_1.Mat33.identity, this.getId(), this, newZIndex); | ||
} | ||
/** | ||
* Combines {@link transformBy} and {@link setZIndex} into a single command. | ||
* | ||
* @param newZIndex - The z-index this component should have after applying this command. | ||
* @param originalZIndex - @internal The z-index the component should revert to after unapplying | ||
* this command. | ||
*/ | ||
setZIndexAndTransformBy(affineTransfm, newZIndex, originalZIndex) { | ||
return new AbstractComponent.TransformElementCommand(affineTransfm, this.getId(), this, newZIndex, originalZIndex); | ||
} | ||
// @returns true iff this component can be selected (e.g. by the selection tool.) | ||
@@ -226,4 +238,8 @@ isSelectable() { | ||
const instance = this.deserializationCallbacks[json.name](json.data); | ||
instance.zIndex = json.zIndex; | ||
instance.id = json.id; | ||
if (isFinite(json.zIndex)) { | ||
instance.zIndex = json.zIndex; | ||
// Ensure that new components will be added on top. | ||
AbstractComponent.zIndexCounter = Math.max(AbstractComponent.zIndexCounter, instance.zIndex + 1); | ||
} | ||
// TODO: What should we do with json.loadSaveData? | ||
@@ -237,2 +253,3 @@ // If we attach it to [instance], we create a potential security risk — loadSaveData | ||
// Topmost z-index | ||
// TODO: Should be a property of the EditorImage. | ||
AbstractComponent.zIndexCounter = 0; | ||
@@ -265,3 +282,3 @@ AbstractComponent.deserializationCallbacks = {}; | ||
} | ||
updateTransform(editor, newTransfm) { | ||
updateTransform(editor, newTransfm, targetZIndex) { | ||
if (!this.component) { | ||
@@ -278,3 +295,8 @@ throw new Error('this.component is undefined or null!'); | ||
this.component.applyTransformation(newTransfm); | ||
this.component.zIndex = targetZIndex; | ||
this.component.lastChangedTime = (new Date()).getTime(); | ||
// Ensure that new components are automatically drawn above the current component. | ||
if (targetZIndex >= AbstractComponent.zIndexCounter) { | ||
AbstractComponent.zIndexCounter = targetZIndex + 1; | ||
} | ||
// Add the element back to the document. | ||
@@ -287,4 +309,3 @@ if (hadParent) { | ||
this.resolveComponent(editor.image); | ||
this.component.zIndex = this.targetZIndex; | ||
this.updateTransform(editor, this.affineTransfm); | ||
this.updateTransform(editor, this.affineTransfm, this.targetZIndex); | ||
editor.queueRerender(); | ||
@@ -294,4 +315,3 @@ } | ||
this.resolveComponent(editor.image); | ||
this.component.zIndex = this.origZIndex; | ||
this.updateTransform(editor, this.affineTransfm.inverse()); | ||
this.updateTransform(editor, this.affineTransfm.inverse(), this.origZIndex); | ||
editor.queueRerender(); | ||
@@ -298,0 +318,0 @@ } |
@@ -10,2 +10,13 @@ import { Rect2 } from '@js-draw/math'; | ||
preview(renderer: AbstractRenderer): void; | ||
/** | ||
* Called when the pen is stationary (or the user otherwise | ||
* activates autocomplete). This might attempt to fit the user's | ||
* drawing to a particular shape. | ||
* | ||
* The shape returned by this function may be ignored if it has | ||
* an empty bounding box. | ||
* | ||
* Although this returns a Promise, it should return *as fast as | ||
* possible*. | ||
*/ | ||
autocorrectShape?: () => Promise<AbstractComponent | null>; | ||
@@ -12,0 +23,0 @@ addPoint(point: StrokeDataPoint): void; |
@@ -6,2 +6,3 @@ import Editor from '../Editor'; | ||
import { DispatcherEventListener } from '../EventDispatcher'; | ||
import { BaseTool } from '../lib'; | ||
export interface SpacerOptions { | ||
@@ -135,3 +136,3 @@ grow: number; | ||
* | ||
* **Note**: This is roughly equivalent to | ||
* **Note**: This is *roughly* equivalent to | ||
* ```ts | ||
@@ -159,5 +160,20 @@ * toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], { | ||
/** | ||
* Adds toolbar widgets based on the enabled tools. | ||
* Adds widgets for pen/eraser/selection/text/pan-zoom primary tools. | ||
* | ||
* If `filter` returns `false` for a tool, no widget is added for that tool. | ||
* See {@link addDefaultToolWidgets} | ||
*/ | ||
addWidgetsForPrimaryTools(filter?: (tool: BaseTool) => boolean): void; | ||
/** | ||
* Adds toolbar widgets based on the enabled tools, and additional tool-like | ||
* buttons (e.g. {@link DocumentPropertiesWidget} and {@link InsertImageWidget}). | ||
*/ | ||
addDefaultToolWidgets(): void; | ||
/** | ||
* Adds widgets that don't correspond to tools, but do allow the user to control | ||
* the editor in some way. | ||
* | ||
* By default, this includes {@link DocumentPropertiesWidget} and {@link InsertImageWidget}. | ||
*/ | ||
addDefaultEditorControlWidgets(): void; | ||
addDefaultActionButtons(): void; | ||
@@ -164,0 +180,0 @@ /** |
@@ -38,2 +38,3 @@ "use strict"; | ||
const SaveActionWidget_1 = __importDefault(require("./widgets/SaveActionWidget")); | ||
const ExitActionWidget_1 = __importDefault(require("./widgets/ExitActionWidget")); | ||
class AbstractToolbar { | ||
@@ -301,3 +302,2 @@ /** @internal */ | ||
const widget = new SaveActionWidget_1.default(this.editor, this.localizationTable, saveCallback, labelOverride); | ||
widget.setTags([BaseWidget_1.ToolbarWidgetTag.Save]); | ||
this.addWidget(widget); | ||
@@ -309,3 +309,3 @@ return widget; | ||
* | ||
* **Note**: This is roughly equivalent to | ||
* **Note**: This is *roughly* equivalent to | ||
* ```ts | ||
@@ -327,11 +327,5 @@ * toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], { | ||
addExitButton(exitCallback, labelOverride = {}) { | ||
return this.addTaggedActionButton([BaseWidget_1.ToolbarWidgetTag.Exit], { | ||
label: this.editor.localization.exit, | ||
icon: this.editor.icons.makeCloseIcon(), | ||
...labelOverride, | ||
}, () => { | ||
exitCallback(); | ||
}, { | ||
autoDisableInReadOnlyEditors: false, | ||
}); | ||
const widget = new ExitActionWidget_1.default(this.editor, this.localizationTable, exitCallback, labelOverride); | ||
this.addWidget(widget); | ||
return widget; | ||
} | ||
@@ -384,23 +378,45 @@ /** | ||
/** | ||
* Adds toolbar widgets based on the enabled tools. | ||
* Adds widgets for pen/eraser/selection/text/pan-zoom primary tools. | ||
* | ||
* If `filter` returns `false` for a tool, no widget is added for that tool. | ||
* See {@link addDefaultToolWidgets} | ||
*/ | ||
addWidgetsForPrimaryTools(filter) { | ||
for (const tool of this.editor.toolController.getPrimaryTools()) { | ||
if (filter && !filter?.(tool)) { | ||
continue; | ||
} | ||
if (tool instanceof Pen_1.default) { | ||
const widget = new PenToolWidget_1.default(this.editor, tool, this.localizationTable); | ||
this.addWidget(widget); | ||
} | ||
else if (tool instanceof Eraser_1.default) { | ||
this.addWidget(new EraserToolWidget_1.default(this.editor, tool, this.localizationTable)); | ||
} | ||
else if (tool instanceof SelectionTool_1.default) { | ||
this.addWidget(new SelectionToolWidget_1.default(this.editor, tool, this.localizationTable)); | ||
} | ||
else if (tool instanceof TextTool_1.default) { | ||
this.addWidget(new TextToolWidget_1.default(this.editor, tool, this.localizationTable)); | ||
} | ||
else if (tool instanceof PanZoom_1.default) { | ||
this.addWidget(new HandToolWidget_1.default(this.editor, tool, this.localizationTable)); | ||
} | ||
} | ||
} | ||
/** | ||
* Adds toolbar widgets based on the enabled tools, and additional tool-like | ||
* buttons (e.g. {@link DocumentPropertiesWidget} and {@link InsertImageWidget}). | ||
*/ | ||
addDefaultToolWidgets() { | ||
const toolController = this.editor.toolController; | ||
for (const tool of toolController.getMatchingTools(Pen_1.default)) { | ||
const widget = new PenToolWidget_1.default(this.editor, tool, this.localizationTable); | ||
this.addWidget(widget); | ||
} | ||
for (const tool of toolController.getMatchingTools(Eraser_1.default)) { | ||
this.addWidget(new EraserToolWidget_1.default(this.editor, tool, this.localizationTable)); | ||
} | ||
for (const tool of toolController.getMatchingTools(SelectionTool_1.default)) { | ||
this.addWidget(new SelectionToolWidget_1.default(this.editor, tool, this.localizationTable)); | ||
} | ||
for (const tool of toolController.getMatchingTools(TextTool_1.default)) { | ||
this.addWidget(new TextToolWidget_1.default(this.editor, tool, this.localizationTable)); | ||
} | ||
const panZoomTool = toolController.getMatchingTools(PanZoom_1.default)[0]; | ||
if (panZoomTool) { | ||
this.addWidget(new HandToolWidget_1.default(this.editor, panZoomTool, this.localizationTable)); | ||
} | ||
this.addWidgetsForPrimaryTools(); | ||
this.addDefaultEditorControlWidgets(); | ||
} | ||
/** | ||
* Adds widgets that don't correspond to tools, but do allow the user to control | ||
* the editor in some way. | ||
* | ||
* By default, this includes {@link DocumentPropertiesWidget} and {@link InsertImageWidget}. | ||
*/ | ||
addDefaultEditorControlWidgets() { | ||
this.addWidget(new DocumentPropertiesWidget_1.default(this.editor, this.localizationTable)); | ||
@@ -407,0 +423,0 @@ this.addWidget(new InsertImageWidget_1.default(this.editor, this.localizationTable)); |
@@ -56,3 +56,3 @@ "use strict"; | ||
this.container = document.createElement('div'); | ||
this.container.classList.add(`${constants_1.toolbarCSSPrefix}toolContainer`, `${constants_1.toolbarCSSPrefix}toolButtonContainer`); | ||
this.container.classList.add(`${constants_1.toolbarCSSPrefix}toolContainer`, `${constants_1.toolbarCSSPrefix}toolButtonContainer`, `${constants_1.toolbarCSSPrefix}internalWidgetId--${id.replace(/[^a-zA-Z0-9_]/g, '-')}`); | ||
this.dropdownContent = document.createElement('div'); | ||
@@ -59,0 +59,0 @@ __classPrivateFieldSet(this, _BaseWidget_hasDropdown, false, "f"); |
@@ -7,7 +7,8 @@ import Editor from '../../Editor'; | ||
export default class HandToolWidget extends BaseToolWidget { | ||
private allowTogglingBaseTool; | ||
protected overridePanZoomTool: PanZoom; | ||
private allowTogglingBaseTool; | ||
constructor(editor: Editor, overridePanZoomTool: PanZoom, localizationTable: ToolbarLocalization); | ||
constructor(editor: Editor, tool: PanZoom, localizationTable: ToolbarLocalization); | ||
private static getPrimaryHandTool; | ||
private static getOverrideHandTool; | ||
protected shouldAutoDisableInReadOnlyEditor(): boolean; | ||
private static getPrimaryHandTool; | ||
protected getTitle(): string; | ||
@@ -14,0 +15,0 @@ protected createIcon(): Element; |
@@ -127,12 +127,18 @@ "use strict"; | ||
constructor(editor, | ||
// Pan zoom tool that overrides all other tools (enabling this tool for a device | ||
// causes that device to pan/zoom instead of interact with the primary tools) | ||
overridePanZoomTool, localizationTable) { | ||
const primaryHandTool = HandToolWidget.getPrimaryHandTool(editor.toolController); | ||
const tool = primaryHandTool ?? overridePanZoomTool; | ||
super(editor, tool, 'hand-tool-widget', localizationTable); | ||
this.overridePanZoomTool = overridePanZoomTool; | ||
// Can either be the primary pan/zoom tool (in the primary tools list) or | ||
// the override pan/zoom tool. | ||
// If the override pan/zoom tool, the primary will be gotten from the editor's | ||
// tool controller. | ||
// If the primary, the override will be gotten from the editor's tool controller. | ||
tool, localizationTable) { | ||
const isGivenToolPrimary = editor.toolController.getPrimaryTools().includes(tool); | ||
const primaryTool = (isGivenToolPrimary ? tool : HandToolWidget.getPrimaryHandTool(editor.toolController)) | ||
?? tool; | ||
super(editor, primaryTool, 'hand-tool-widget', localizationTable); | ||
this.overridePanZoomTool = | ||
(isGivenToolPrimary ? HandToolWidget.getOverrideHandTool(editor.toolController) : tool) | ||
?? tool; | ||
// Only allow toggling a hand tool if we're using the primary hand tool and not the override | ||
// hand tool for this button. | ||
this.allowTogglingBaseTool = primaryHandTool !== null; | ||
this.allowTogglingBaseTool = primaryTool !== null; | ||
// Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled. | ||
@@ -143,10 +149,7 @@ if (!this.allowTogglingBaseTool) { | ||
// Controls for the overriding hand tool. | ||
const touchPanningWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoom_1.PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable); | ||
const rotationLockWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoom_1.PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable); | ||
const touchPanningWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoom_1.PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable); | ||
const rotationLockWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoom_1.PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable); | ||
this.addSubWidget(touchPanningWidget); | ||
this.addSubWidget(rotationLockWidget); | ||
} | ||
shouldAutoDisableInReadOnlyEditor() { | ||
return false; | ||
} | ||
static getPrimaryHandTool(toolController) { | ||
@@ -157,2 +160,10 @@ const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom_1.default); | ||
} | ||
static getOverrideHandTool(toolController) { | ||
const panZoomToolList = toolController.getMatchingTools(PanZoom_1.default); | ||
const panZoomTool = panZoomToolList[0]; | ||
return panZoomTool; | ||
} | ||
shouldAutoDisableInReadOnlyEditor() { | ||
return false; | ||
} | ||
getTitle() { | ||
@@ -159,0 +170,0 @@ return this.localizationTable.handTool; |
@@ -177,3 +177,3 @@ "use strict"; | ||
this.statusView.replaceChildren(sizeText); | ||
const largeImageThreshold = 0.25; // MiB | ||
const largeImageThreshold = 0.12; // MiB | ||
if (sizeInMiB > largeImageThreshold) { | ||
@@ -180,0 +180,0 @@ this.statusView.appendChild(decreaseSizeButton); |
export declare const resizeImageToSelectionKeyboardShortcut = "jsdraw.toolbar.SelectionTool.resizeImageToSelection"; | ||
export declare const selectStrokeTypeKeyboardShortcutIds: string[]; | ||
export declare const saveKeyboardShortcut = "jsdraw.toolbar.SaveActionWidget.save"; | ||
export declare const exitKeyboardShortcut = "jsdraw.toolbar.ExitActionWidget.exit"; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.saveKeyboardShortcut = exports.selectStrokeTypeKeyboardShortcutIds = exports.resizeImageToSelectionKeyboardShortcut = void 0; | ||
exports.exitKeyboardShortcut = exports.saveKeyboardShortcut = exports.selectStrokeTypeKeyboardShortcutIds = exports.resizeImageToSelectionKeyboardShortcut = void 0; | ||
const KeyboardShortcutManager_1 = __importDefault(require("../../shortcuts/KeyboardShortcutManager")); | ||
@@ -21,1 +21,4 @@ // Selection | ||
KeyboardShortcutManager_1.default.registerDefaultKeyboardShortcut(exports.saveKeyboardShortcut, ['ctrlOrMeta+KeyS'], 'Save'); | ||
// Exit | ||
exports.exitKeyboardShortcut = 'jsdraw.toolbar.ExitActionWidget.exit'; | ||
KeyboardShortcutManager_1.default.registerDefaultKeyboardShortcut(exports.exitKeyboardShortcut, ['Alt+KeyQ'], 'Exit'); |
@@ -1,2 +0,2 @@ | ||
import ReactiveValue from 'js-draw/src/util/ReactiveValue'; | ||
import ReactiveValue from '../../../util/ReactiveValue'; | ||
/** | ||
@@ -3,0 +3,0 @@ * A class that manages whether/what content is shown for a widget. |
@@ -18,1 +18,2 @@ export declare const undoKeyboardShortcutId = "jsdraw.tools.undo"; | ||
export declare const duplicateSelectionShortcut = "jsdraw.tools.SelectionTool.duplicateSelection"; | ||
export declare const sendToBackSelectionShortcut = "jsdraw.tools.SelectionTool.sendToBack"; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.duplicateSelectionShortcut = exports.selectAllKeyboardShortcut = exports.zoomOutKeyboardShortcutId = exports.zoomInKeyboardShortcutId = exports.rotateCounterClockwiseKeyboardShortcutId = exports.rotateClockwiseKeyboardShortcutId = exports.moveDownKeyboardShortcutId = exports.moveUpKeyboardShortcutId = exports.moveRightKeyboardShortcutId = exports.moveLeftKeyboardShortcutId = exports.toggleFindVisibleShortcutId = exports.lineLockKeyboardShortcutId = exports.snapToGridKeyboardShortcutId = exports.decreaseSizeKeyboardShortcutId = exports.increaseSizeKeyboardShortcutId = exports.redoKeyboardShortcutId = exports.undoKeyboardShortcutId = void 0; | ||
exports.sendToBackSelectionShortcut = exports.duplicateSelectionShortcut = exports.selectAllKeyboardShortcut = exports.zoomOutKeyboardShortcutId = exports.zoomInKeyboardShortcutId = exports.rotateCounterClockwiseKeyboardShortcutId = exports.rotateClockwiseKeyboardShortcutId = exports.moveDownKeyboardShortcutId = exports.moveUpKeyboardShortcutId = exports.moveRightKeyboardShortcutId = exports.moveLeftKeyboardShortcutId = exports.toggleFindVisibleShortcutId = exports.lineLockKeyboardShortcutId = exports.snapToGridKeyboardShortcutId = exports.decreaseSizeKeyboardShortcutId = exports.increaseSizeKeyboardShortcutId = exports.redoKeyboardShortcutId = exports.undoKeyboardShortcutId = void 0; | ||
const KeyboardShortcutManager_1 = __importDefault(require("../shortcuts/KeyboardShortcutManager")); | ||
@@ -49,1 +49,3 @@ // This file contains user-overridable tool-realted keybindings. | ||
KeyboardShortcutManager_1.default.registerDefaultKeyboardShortcut(exports.duplicateSelectionShortcut, ['CtrlOrMeta+KeyD'], 'Duplicate selection'); | ||
exports.sendToBackSelectionShortcut = 'jsdraw.tools.SelectionTool.sendToBack'; | ||
KeyboardShortcutManager_1.default.registerDefaultKeyboardShortcut(exports.sendToBackSelectionShortcut, ['End'], 'Send to back'); |
@@ -195,2 +195,7 @@ "use strict"; | ||
} | ||
// Don't complete to empty shapes. | ||
const bboxArea = correctedShape.getBBox().area; | ||
if (bboxArea === 0 || !isFinite(bboxArea)) { | ||
return; | ||
} | ||
this.autocorrectedShape = correctedShape; | ||
@@ -197,0 +202,0 @@ this.lastAutocorrectedShape = correctedShape; |
@@ -5,2 +5,3 @@ /** | ||
*/ | ||
import SerializableCommand from '../../commands/SerializableCommand'; | ||
import Editor from '../../Editor'; | ||
@@ -40,3 +41,6 @@ import { Mat33, Rect2, Point2 } from '@js-draw/math'; | ||
setTransform(transform: Mat33, preview?: boolean): void; | ||
private getDeltaZIndexToMoveSelectionToTop; | ||
finalizeTransform(): void | Promise<void>; | ||
/** Sends all selected elements to the bottom of the visible image. */ | ||
sendToBack(): SerializableCommand | null; | ||
private static ApplyTransformationCommand; | ||
@@ -43,0 +47,0 @@ private previewTransformCmds; |
@@ -44,2 +44,3 @@ "use strict"; | ||
const EditorImage_1 = __importDefault(require("../../image/EditorImage")); | ||
const uniteCommands_1 = __importDefault(require("../../commands/uniteCommands")); | ||
const updateChunkSize = 100; | ||
@@ -55,2 +56,3 @@ const maxPreviewElemCount = 500; | ||
this.transform = math_1.Mat33.identity; | ||
// invariant: sorted by increasing z-index | ||
this.selectedElems = []; | ||
@@ -166,2 +168,12 @@ this.hasParent = true; | ||
} | ||
getDeltaZIndexToMoveSelectionToTop() { | ||
if (this.selectedElems.length === 0) { | ||
return 0; | ||
} | ||
const selectedBottommostZIndex = this.selectedElems[0].getZIndex(); | ||
const visibleObjects = this.editor.image.getElementsIntersectingRegion(this.region); | ||
const topMostVisibleZIndex = visibleObjects[visibleObjects.length - 1]?.getZIndex() ?? selectedBottommostZIndex; | ||
const deltaZIndex = (topMostVisibleZIndex + 1) - selectedBottommostZIndex; | ||
return deltaZIndex; | ||
} | ||
// Applies the current transformation to the selection | ||
@@ -175,8 +187,10 @@ finalizeTransform() { | ||
this.scrollTo(); | ||
let transformPromise = undefined; | ||
// Make the commands undo-able. | ||
// Don't check for non-empty transforms because this breaks changing the | ||
// z-index of the just-transformed commands. | ||
// | ||
// TODO: Check whether the selectedElems are already all toplevel. | ||
const transformPromise = this.editor.dispatch(new _a.ApplyTransformationCommand(this, selectedElems, fullTransform)); | ||
if (this.selectedElems.length > 0) { | ||
const deltaZIndex = this.getDeltaZIndexToMoveSelectionToTop(); | ||
transformPromise = this.editor.dispatch(new _a.ApplyTransformationCommand(this, selectedElems, fullTransform, deltaZIndex)); | ||
} | ||
// Clear renderings of any in-progress transformations | ||
@@ -187,2 +201,18 @@ const wetInkRenderer = this.editor.display.getWetInkRenderer(); | ||
} | ||
/** Sends all selected elements to the bottom of the visible image. */ | ||
sendToBack() { | ||
const visibleObjects = this.editor.image.getElementsIntersectingRegion(this.editor.viewport.visibleRect); | ||
// VisibleObjects and selectedElems should both be sorted by z-index | ||
const lowestVisibleZIndex = visibleObjects[0]?.getZIndex() ?? 0; | ||
const highestSelectedZIndex = this.selectedElems[this.selectedElems.length - 1]?.getZIndex() ?? 0; | ||
const targetHighestZIndex = lowestVisibleZIndex - 1; | ||
const deltaZIndex = targetHighestZIndex - highestSelectedZIndex; | ||
if (deltaZIndex !== 0) { | ||
const commands = this.selectedElems.map(elem => { | ||
return elem.setZIndex(elem.getZIndex() + deltaZIndex); | ||
}); | ||
return (0, uniteCommands_1.default)(commands, updateChunkSize); | ||
} | ||
return null; | ||
} | ||
// Preview the effects of the current transformation on the selection | ||
@@ -201,3 +231,3 @@ previewTransformCmds() { | ||
wetInkRenderer.pushTransform(this.transform); | ||
const viewportVisibleRect = this.editor.viewport.visibleRect; | ||
const viewportVisibleRect = this.editor.viewport.visibleRect.union(this.region); | ||
const visibleRect = viewportVisibleRect.transformedBoundingBox(this.transform.inverse()); | ||
@@ -448,3 +478,4 @@ for (const elem of this.selectedElems) { | ||
const selectionToUpdate = null; | ||
tmpApplyCommand = new _a.ApplyTransformationCommand(selectionToUpdate, this.selectedElems, this.transform); | ||
const deltaZIndex = this.getDeltaZIndexToMoveSelectionToTop(); | ||
tmpApplyCommand = new _a.ApplyTransformationCommand(selectionToUpdate, this.selectedElems, this.transform, deltaZIndex); | ||
// Transform to ensure that the duplicates are in the correct location | ||
@@ -490,2 +521,4 @@ await tmpApplyCommand.apply(this.editor); | ||
this.selectedElems = objects.filter(object => object.isSelectable()); | ||
// Enforce increasing z-index invariant | ||
this.selectedElems.sort((a, b) => a.getZIndex() - b.getZIndex()); | ||
this.padRegion(); | ||
@@ -504,3 +537,4 @@ this.updateUI(); | ||
const elemIds = (json.elems ?? []); | ||
return new _a.ApplyTransformationCommand(null, elemIds, fullTransform); | ||
const deltaZIndex = parseInt(json.deltaZIndex ?? 0); | ||
return new _a.ApplyTransformationCommand(null, elemIds, fullTransform, deltaZIndex); | ||
}); | ||
@@ -513,6 +547,7 @@ })(); | ||
// Full transformation used to transform elements. | ||
fullTransform) { | ||
fullTransform, deltaZIndex) { | ||
super('selection-tool-transform'); | ||
this.selection = selection; | ||
this.fullTransform = fullTransform; | ||
this.deltaZIndex = deltaZIndex; | ||
const isIDList = (arr) => { | ||
@@ -528,7 +563,7 @@ return typeof arr[0] === 'string'; | ||
this.transformCommands = selectedElems.map(elem => { | ||
return elem.transformBy(this.fullTransform); | ||
return elem.setZIndexAndTransformBy(this.fullTransform, elem.getZIndex() + deltaZIndex); | ||
}); | ||
} | ||
} | ||
resolveToElems(editor) { | ||
resolveToElems(editor, isUndoing) { | ||
if (this.transformCommands) { | ||
@@ -542,7 +577,15 @@ return; | ||
} | ||
return elem.transformBy(this.fullTransform); | ||
let originalZIndex = elem.getZIndex(); | ||
let targetZIndex = elem.getZIndex() + this.deltaZIndex; | ||
// If the command has already been applied, the element should currently | ||
// have the target z-index. | ||
if (isUndoing) { | ||
targetZIndex = elem.getZIndex(); | ||
originalZIndex = elem.getZIndex() - this.deltaZIndex; | ||
} | ||
return elem.setZIndexAndTransformBy(this.fullTransform, targetZIndex, originalZIndex); | ||
}); | ||
} | ||
async apply(editor) { | ||
this.resolveToElems(editor); | ||
this.resolveToElems(editor, false); | ||
this.selection?.setTransform(this.fullTransform, false); | ||
@@ -556,3 +599,3 @@ this.selection?.updateUI(); | ||
async unapply(editor) { | ||
this.resolveToElems(editor); | ||
this.resolveToElems(editor, true); | ||
this.selection?.setTransform(this.fullTransform.inverse(), false); | ||
@@ -569,2 +612,3 @@ this.selection?.updateUI(); | ||
transform: this.fullTransform.toArray(), | ||
deltaZIndex: this.deltaZIndex, | ||
}; | ||
@@ -571,0 +615,0 @@ } |
@@ -31,2 +31,3 @@ import AbstractComponent from '../../components/AbstractComponent'; | ||
private static handleableKeys; | ||
private hasUnfinalizedTransformFromKeyPress; | ||
onKeyPress(event: KeyPressEvent): boolean; | ||
@@ -33,0 +34,0 @@ onKeyUp(evt: KeyUpEvent): boolean; |
@@ -30,2 +30,5 @@ "use strict"; | ||
this.lastSelectedObjects = []; | ||
// Whether the last keypress corresponded to an action that didn't transform the | ||
// selection (and thus does not need to be finalized on onKeyUp). | ||
this.hasUnfinalizedTransformFromKeyPress = false; | ||
this.autoscroller = new ToPointerAutoscroller_1.default(editor.viewport, (scrollBy) => { | ||
@@ -223,3 +226,4 @@ editor.dispatch(Viewport_1.default.transformBy(math_1.Mat33.translation(scrollBy)), false); | ||
} | ||
if (this.selectionBox && shortcucts.matchesShortcut(keybindings_1.duplicateSelectionShortcut, event)) { | ||
if (this.selectionBox && (shortcucts.matchesShortcut(keybindings_1.duplicateSelectionShortcut, event) | ||
|| shortcucts.matchesShortcut(keybindings_1.sendToBackSelectionShortcut, event))) { | ||
// Handle duplication on key up — we don't want to accidentally duplicate | ||
@@ -308,2 +312,4 @@ // many times. | ||
this.selectionBox.scrollTo(); | ||
// The transformation needs to be finalized at some point (on key up) | ||
this.hasUnfinalizedTransformFromKeyPress = true; | ||
} | ||
@@ -334,2 +340,9 @@ if (this.selectionBox && !handled && (event.key === 'Delete' || event.key === 'Backspace')) { | ||
} | ||
if (this.selectionBox && shortcucts.matchesShortcut(keybindings_1.sendToBackSelectionShortcut, evt)) { | ||
const sendToBackCommand = this.selectionBox.sendToBack(); | ||
if (sendToBackCommand) { | ||
this.editor.dispatch(sendToBackCommand); | ||
} | ||
return true; | ||
} | ||
if (evt.key === 'Shift') { | ||
@@ -339,4 +352,9 @@ this.shiftKeyPressed = false; | ||
} | ||
// If we don't need to finalize the transform | ||
if (!this.hasUnfinalizedTransformFromKeyPress) { | ||
return true; | ||
} | ||
if (this.selectionBox && SelectionTool.handleableKeys.some(key => key === evt.key)) { | ||
this.selectionBox.finalizeTransform(); | ||
this.hasUnfinalizedTransformFromKeyPress = false; | ||
return true; | ||
@@ -343,0 +361,0 @@ } |
@@ -10,3 +10,2 @@ import Editor from '../Editor'; | ||
* | ||
* @deprecated This may be replaced in the future. | ||
*/ | ||
@@ -13,0 +12,0 @@ export default class ToolSwitcherShortcut extends BaseTool { |
@@ -13,3 +13,2 @@ "use strict"; | ||
* | ||
* @deprecated This may be replaced in the future. | ||
*/ | ||
@@ -16,0 +15,0 @@ class ToolSwitcherShortcut extends BaseTool_1.default { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = { | ||
number: '1.10.0', | ||
number: '1.11.0', | ||
}; |
@@ -119,2 +119,10 @@ import SerializableCommand from '../commands/SerializableCommand'; | ||
setZIndex(newZIndex: number): SerializableCommand; | ||
/** | ||
* Combines {@link transformBy} and {@link setZIndex} into a single command. | ||
* | ||
* @param newZIndex - The z-index this component should have after applying this command. | ||
* @param originalZIndex - @internal The z-index the component should revert to after unapplying | ||
* this command. | ||
*/ | ||
setZIndexAndTransformBy(affineTransfm: Mat33, newZIndex: number, originalZIndex?: number): SerializableCommand; | ||
isSelectable(): boolean; | ||
@@ -121,0 +129,0 @@ isBackground(): boolean; |
@@ -10,2 +10,13 @@ import { Rect2 } from '@js-draw/math'; | ||
preview(renderer: AbstractRenderer): void; | ||
/** | ||
* Called when the pen is stationary (or the user otherwise | ||
* activates autocomplete). This might attempt to fit the user's | ||
* drawing to a particular shape. | ||
* | ||
* The shape returned by this function may be ignored if it has | ||
* an empty bounding box. | ||
* | ||
* Although this returns a Promise, it should return *as fast as | ||
* possible*. | ||
*/ | ||
autocorrectShape?: () => Promise<AbstractComponent | null>; | ||
@@ -12,0 +23,0 @@ addPoint(point: StrokeDataPoint): void; |
@@ -6,2 +6,3 @@ import Editor from '../Editor'; | ||
import { DispatcherEventListener } from '../EventDispatcher'; | ||
import { BaseTool } from '../lib'; | ||
export interface SpacerOptions { | ||
@@ -135,3 +136,3 @@ grow: number; | ||
* | ||
* **Note**: This is roughly equivalent to | ||
* **Note**: This is *roughly* equivalent to | ||
* ```ts | ||
@@ -159,5 +160,20 @@ * toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], { | ||
/** | ||
* Adds toolbar widgets based on the enabled tools. | ||
* Adds widgets for pen/eraser/selection/text/pan-zoom primary tools. | ||
* | ||
* If `filter` returns `false` for a tool, no widget is added for that tool. | ||
* See {@link addDefaultToolWidgets} | ||
*/ | ||
addWidgetsForPrimaryTools(filter?: (tool: BaseTool) => boolean): void; | ||
/** | ||
* Adds toolbar widgets based on the enabled tools, and additional tool-like | ||
* buttons (e.g. {@link DocumentPropertiesWidget} and {@link InsertImageWidget}). | ||
*/ | ||
addDefaultToolWidgets(): void; | ||
/** | ||
* Adds widgets that don't correspond to tools, but do allow the user to control | ||
* the editor in some way. | ||
* | ||
* By default, this includes {@link DocumentPropertiesWidget} and {@link InsertImageWidget}. | ||
*/ | ||
addDefaultEditorControlWidgets(): void; | ||
addDefaultActionButtons(): void; | ||
@@ -164,0 +180,0 @@ /** |
@@ -7,7 +7,8 @@ import Editor from '../../Editor'; | ||
export default class HandToolWidget extends BaseToolWidget { | ||
private allowTogglingBaseTool; | ||
protected overridePanZoomTool: PanZoom; | ||
private allowTogglingBaseTool; | ||
constructor(editor: Editor, overridePanZoomTool: PanZoom, localizationTable: ToolbarLocalization); | ||
constructor(editor: Editor, tool: PanZoom, localizationTable: ToolbarLocalization); | ||
private static getPrimaryHandTool; | ||
private static getOverrideHandTool; | ||
protected shouldAutoDisableInReadOnlyEditor(): boolean; | ||
private static getPrimaryHandTool; | ||
protected getTitle(): string; | ||
@@ -14,0 +15,0 @@ protected createIcon(): Element; |
export declare const resizeImageToSelectionKeyboardShortcut = "jsdraw.toolbar.SelectionTool.resizeImageToSelection"; | ||
export declare const selectStrokeTypeKeyboardShortcutIds: string[]; | ||
export declare const saveKeyboardShortcut = "jsdraw.toolbar.SaveActionWidget.save"; | ||
export declare const exitKeyboardShortcut = "jsdraw.toolbar.ExitActionWidget.exit"; |
@@ -1,2 +0,2 @@ | ||
import ReactiveValue from 'js-draw/src/util/ReactiveValue'; | ||
import ReactiveValue from '../../../util/ReactiveValue'; | ||
/** | ||
@@ -3,0 +3,0 @@ * A class that manages whether/what content is shown for a widget. |
@@ -18,1 +18,2 @@ export declare const undoKeyboardShortcutId = "jsdraw.tools.undo"; | ||
export declare const duplicateSelectionShortcut = "jsdraw.tools.SelectionTool.duplicateSelection"; | ||
export declare const sendToBackSelectionShortcut = "jsdraw.tools.SelectionTool.sendToBack"; |
@@ -5,2 +5,3 @@ /** | ||
*/ | ||
import SerializableCommand from '../../commands/SerializableCommand'; | ||
import Editor from '../../Editor'; | ||
@@ -40,3 +41,6 @@ import { Mat33, Rect2, Point2 } from '@js-draw/math'; | ||
setTransform(transform: Mat33, preview?: boolean): void; | ||
private getDeltaZIndexToMoveSelectionToTop; | ||
finalizeTransform(): void | Promise<void>; | ||
/** Sends all selected elements to the bottom of the visible image. */ | ||
sendToBack(): SerializableCommand | null; | ||
private static ApplyTransformationCommand; | ||
@@ -43,0 +47,0 @@ private previewTransformCmds; |
@@ -31,2 +31,3 @@ import AbstractComponent from '../../components/AbstractComponent'; | ||
private static handleableKeys; | ||
private hasUnfinalizedTransformFromKeyPress; | ||
onKeyPress(event: KeyPressEvent): boolean; | ||
@@ -33,0 +34,0 @@ onKeyUp(evt: KeyUpEvent): boolean; |
@@ -10,3 +10,2 @@ import Editor from '../Editor'; | ||
* | ||
* @deprecated This may be replaced in the future. | ||
*/ | ||
@@ -13,0 +12,0 @@ export default class ToolSwitcherShortcut extends BaseTool { |
{ | ||
"name": "js-draw", | ||
"version": "1.10.0", | ||
"version": "1.11.0", | ||
"description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ", | ||
@@ -89,3 +89,3 @@ "types": "./dist/mjs/lib.d.ts", | ||
], | ||
"gitHead": "ccf1d0634e902c731fcd794df11cd001c3a30585" | ||
"gitHead": "01fc3dc7bdbc9f456705bf08d9c30b4549122d97" | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3861683
731
52749