Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(): Draggable Text Migration Regression #8534

Merged
merged 63 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f750f9d
Update itext_behavior.mixin.ts
ShaMan123 Dec 20, 2022
617bc87
Update itext_behavior.mixin.ts
ShaMan123 Dec 22, 2022
db8739a
fix(): bad conflict resolution
ShaMan123 Dec 22, 2022
5555c5c
fix no dpr on drag image offset
ShaMan123 Dec 22, 2022
df95065
fix(): drag image + vpt
ShaMan123 Dec 22, 2022
9ec6440
test `isIdentityMatrix`
ShaMan123 Dec 22, 2022
c385df1
Update CHANGELOG.md
ShaMan123 Dec 22, 2022
057af08
cleanup
ShaMan123 Dec 22, 2022
7fb8378
fix(): acount for stroke style
ShaMan123 Dec 22, 2022
7899a25
drag image visual tests
ShaMan123 Dec 22, 2022
92cfc43
Update itext_behavior.mixin.ts
ShaMan123 Dec 22, 2022
328a016
better goldens
ShaMan123 Dec 22, 2022
2bdf139
Merge branch 'master' into fix-text-dnd
ShaMan123 Dec 29, 2022
9ca3c4f
Update CHANGELOG.md
ShaMan123 Jan 3, 2023
ecb8073
Merge branch 'master' into fix-text-dnd
ShaMan123 Jan 4, 2023
ee30fec
export isIdentityMatrix
ShaMan123 Jan 4, 2023
86242d6
Merge branch 'master' into fix-text-dnd
ShaMan123 Jan 6, 2023
e26c326
Merge branch 'master' into fix-text-dnd
asturur Jan 9, 2023
1ab5bff
fix(): offset is always 0
ShaMan123 Jan 9, 2023
3732bff
code styling
ShaMan123 Jan 9, 2023
74c34c9
fix(): transform event `isClick`
ShaMan123 Jan 9, 2023
f0e3d96
Merge branch 'master' into fix-text-dnd
ShaMan123 Jan 9, 2023
30c3924
fix(): click & drag interactions
ShaMan123 Jan 9, 2023
3af1301
fix(): false positive drag event
ShaMan123 Jan 9, 2023
7dc9815
fix(): render selection only if blurred and editing
ShaMan123 Jan 9, 2023
1f6d096
fix(): drag start flicker
ShaMan123 Jan 9, 2023
7205724
fix(): export itext while editing
ShaMan123 Jan 9, 2023
acc5b5e
fix(): clear cursor on stale drop target
ShaMan123 Jan 9, 2023
26aa014
fix(): _renderDragEffects when overlapping
ShaMan123 Jan 9, 2023
7e5de48
Merge branch 'master' into fix-text-dnd
asturur Jan 9, 2023
3a68c7c
fix(): find drag targets
ShaMan123 Jan 9, 2023
d713f76
Merge branch 'fix-text-dnd' of https://github.com/fabricjs/fabric.js …
ShaMan123 Jan 9, 2023
8b7971f
cleanup
ShaMan123 Jan 9, 2023
921b5f2
Update EventTypeDefs.ts
ShaMan123 Jan 9, 2023
b2b90ec
Merge branch 'master' into fix-text-dnd
asturur Jan 10, 2023
f5b628d
Merge branch 'master' into fix-text-dnd
ShaMan123 Jan 12, 2023
0c0e853
Update .gitignore
ShaMan123 Jan 12, 2023
37f7258
fix imports
ShaMan123 Jan 12, 2023
f4b03a9
fix merge
ShaMan123 Jan 12, 2023
ced0b67
revert
ShaMan123 Jan 12, 2023
9c41e18
types
ShaMan123 Jan 12, 2023
4d84e35
Update canvas_events.ts
ShaMan123 Jan 12, 2023
8a88bef
Update text.js
ShaMan123 Jan 12, 2023
542f8c6
Update itext_click_behaviour.js
ShaMan123 Jan 12, 2023
e6dd51a
init
ShaMan123 Jan 12, 2023
da6fc7d
init tests
ShaMan123 Jan 12, 2023
360ad4d
Update itext_behavior.mixin.ts
ShaMan123 Jan 12, 2023
915230f
Update draggable_text.js
ShaMan123 Jan 13, 2023
4fdbd68
Update draggable_text.js
ShaMan123 Jan 13, 2023
c4886b0
Update draggable_text.js
ShaMan123 Jan 13, 2023
cf24a6f
Merge branch 'master' into fix-text-dnd
ShaMan123 Jan 13, 2023
8eaae61
test(): Overlapping draggable text effects
ShaMan123 Jan 13, 2023
e6bd3c8
Update text.js
ShaMan123 Jan 13, 2023
68b036c
Update CHANGELOG.md
ShaMan123 Jan 13, 2023
fd48378
cleanup
ShaMan123 Jan 13, 2023
4fcb2ea
Update text.js
ShaMan123 Jan 13, 2023
565a9a8
renderEffects
ShaMan123 Jan 13, 2023
fa0a91d
Update canvas_events.ts
ShaMan123 Jan 14, 2023
f4fee32
fix(): focus hiddentextarea in edge cases
ShaMan123 Jan 14, 2023
de198d4
correct comment
ShaMan123 Jan 14, 2023
c0fa8fb
Merge branch 'master' into fix-text-dnd
asturur Jan 15, 2023
3dd25c2
added coment and extract dropTarget variable
asturur Jan 15, 2023
7ecb5ea
prettier
asturur Jan 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ change-output.md
before_commit
/coverage/
.idea/
/dist/fabric.require.js
/dist/fabric.min.js.gz
/dist
/cli_output/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- refactor(IText): Fixes Draggable Text for retina and viewport transform #8534
- chore(TS): refactor canvas init, fix `_initRetinaScaling` regression #8520
- chore(TS): remove all remaining empty declarations [#8593](https://github.com/fabricjs/fabric.js/pull/8593)
- refactor(IText): modernize IText cursor animation based on animation API changes (and fix minor regression) plus leftovers from #8547 [#8583](https://github.com/fabricjs/fabric.js/pull/8583)
Expand Down
2 changes: 2 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import {
calcDimensionsMatrix,
calcRotateMatrix,
multiplyTransformMatrices,
isIdentityMatrix,
} from './src/util/misc/matrix';
import {
stylesFromArray,
Expand Down Expand Up @@ -190,6 +191,7 @@ const util = {
calcDimensionsMatrix,
calcRotateMatrix,
multiplyTransformMatrices,
isIdentityMatrix,
stylesFromArray,
stylesToArray,
hasStyleChanged,
Expand Down
10 changes: 5 additions & 5 deletions src/EventTypeDefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ export type TPointerEventInfo<E extends TPointerEvent = TPointerEvent> =
currentTarget?: FabricObject | null;
};

type SimpleEventHandler<T extends Event = TPointerEvent> =
TEventWithTarget<T> & {
subTargets: FabricObject[];
};
type SimpleEventHandler<T extends Event = TPointerEvent> = TEvent<T> & {
target?: FabricObject;
subTargets: FabricObject[];
};

type InEvent = {
previousTarget?: FabricObject;
Expand All @@ -156,7 +156,7 @@ export type DragEventData = TEvent<DragEvent> & {
dropTarget?: FabricObject;
};

type DropEventData = DragEventData & { pointer: Point };
export type DropEventData = DragEventData & { pointer: Point };

type DnDEvents = {
dragstart: TEventWithTarget<DragEvent>;
Expand Down
4 changes: 4 additions & 0 deletions src/canvas/canvas.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,9 @@ export class SelectableCanvas<
this.renderCanvas(this.contextContainer, this._objectsToRender);
}

/**
* text selection is rendered by the active text instance during the rendering cycle
*/
renderTopLayer(ctx: CanvasRenderingContext2D): void {
ctx.save();
if (this.isDrawingMode && this._isCurrentlyDrawing) {
Expand All @@ -619,6 +622,7 @@ export class SelectableCanvas<
/**
* Method to render only the top canvas.
* Also used to render the group selection box.
* Does not render text selection.
*/
renderTop() {
const ctx = this.contextTop;
Expand Down
158 changes: 96 additions & 62 deletions src/canvas/canvas_events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Point } from '../point.class';
import { ActiveSelection } from '../shapes/active_selection.class';
import { Group } from '../shapes/group.class';
import type { FabricObject } from '../shapes/Object/FabricObject';
import { AssertKeys } from '../typedefs';
import { isTouchEvent, stopEvent } from '../util/dom_event';
import { createCanvasElement } from '../util/misc/dom';
import { sendPointToPlane } from '../util/misc/planeChange';
Expand Down Expand Up @@ -105,6 +106,15 @@ export class Canvas extends SelectableCanvas {
*/
private declare _dragSource?: FabricObject;

/**
* Holds a reference to an object on the canvas that is the current drop target
* May differ from {@link _draggedoverTarget}
* @todo inspect whether {@link _draggedoverTarget} and {@link _dropTarget} should be merged somehow
* @type FabricObject
* @private
*/
private declare _dropTarget: FabricObject<ObjectEvents> | undefined;

declare currentTarget?: FabricObject;

declare currentSubTargets?: FabricObject[];
Expand All @@ -117,6 +127,8 @@ export class Canvas extends SelectableCanvas {
*/
declare _previousPointer: Point;

private _isClick: boolean;

textEditingManager = new TextEditingManager();

constructor(el: string | HTMLCanvasElement, options = {}) {
Expand Down Expand Up @@ -292,6 +304,7 @@ export class Canvas extends SelectableCanvas {
* @param {DragEvent} e
*/
private _onDragStart(e: DragEvent) {
this._isClick = false;
const activeObject = this.getActiveObject();
if (
isFabricObjectWithDragSupport(activeObject) &&
Expand All @@ -312,6 +325,9 @@ export class Canvas extends SelectableCanvas {
}

/**
* First we clear top context where the effects are being rendered.
* Then we render the effects.
* Doing so will render the correct effect for all cases including an overlap between `source` and `target`.
* @private
*/
private _renderDragEffects(
Expand All @@ -320,19 +336,30 @@ export class Canvas extends SelectableCanvas {
target?: FabricObject
) {
let dirty = false;
// clear top context
const dropTarget = this._dropTarget;
if (dropTarget && dropTarget !== source && dropTarget !== target) {
dropTarget.clearContextTop();
dirty = true;
}
source?.clearContextTop();
target !== source && target?.clearContextTop();
// render effects
const ctx = this.contextTop;
ctx.save();
ctx.transform(...this.viewportTransform);
if (source) {
source.clearContextTop(true);
source.renderDragSourceEffect(e);
ctx.save();
source.transform(ctx);
(source as AssertKeys<FabricObject, 'canvas'>).renderDragSourceEffect(e);
ctx.restore();
dirty = true;
}
if (target) {
if (target !== source) {
ctx.restore();
ctx.save();
target.clearContextTop(true);
}
target.renderDropTargetEffect(e);
ctx.save();
target.transform(ctx);
(target as AssertKeys<FabricObject, 'canvas'>).renderDropTargetEffect(e);
ctx.restore();
dirty = true;
}
ctx.restore();
Expand Down Expand Up @@ -384,32 +411,47 @@ export class Canvas extends SelectableCanvas {
this._dragSource && this._dragSource.fire('drag', options);
}

/**
* As opposed to {@link findTarget} we want the top most object to be returned w/o the active object cutting in line.
* Override at will
*/
protected findDragTargets(e: DragEvent) {
this.targets = [];
const target = this._searchPossibleTargets(
this._objects,
this.getPointer(e, true)
);
return {
target,
targets: [...this.targets],
};
}

/**
* prevent default to allow drop event to be fired
* https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#specifying_drop_targets
* @private
* @param {DragEvent} [e] Event object fired on Event.js shake
*/
private _onDragOver(e: DragEvent) {
const eventType = 'dragover',
target = this.findTarget(e),
targets = this.targets,
options = {
e: e,
target,
subTargets: targets,
dragSource: this._dragSource as FabricObject,
canDrop: false,
dropTarget: undefined,
};
const eventType = 'dragover';
const { target, targets } = this.findDragTargets(e);
const dragSource = this._dragSource as FabricObject;
const options = {
e,
target,
subTargets: targets,
dragSource,
canDrop: false,
dropTarget: undefined,
};
let dropTarget;
// fire on canvas
this.fire(eventType, options);
// make sure we fire dragenter events before dragover
// if dragleave is needed, object will not fire dragover so we don't need to trouble ourselves with it
this._fireEnterLeaveEvents(target, options);
if (target) {
// render drag selection before rendering target cursor for correct visuals
if (target.canDrop(e)) {
dropTarget = target;
}
Expand All @@ -427,7 +469,8 @@ export class Canvas extends SelectableCanvas {
subTarget.fire(eventType, options);
}
// render drag effects now that relations between source and target is clear
this._renderDragEffects(e, this._dragSource, dropTarget);
this._renderDragEffects(e, dragSource, dropTarget);
this._dropTarget = dropTarget;
}

/**
Expand All @@ -436,11 +479,11 @@ export class Canvas extends SelectableCanvas {
* @param {Event} [e] Event object fired on Event.js shake
*/
private _onDragEnter(e: DragEvent) {
const target = this.findTarget(e);
const { target, targets } = this.findDragTargets(e);
const options = {
e,
target: target as FabricObject,
subTargets: this.targets,
target,
subTargets: targets,
dragSource: this._dragSource,
};
this.fire('dragenter', options);
Expand All @@ -461,8 +504,11 @@ export class Canvas extends SelectableCanvas {
dragSource: this._dragSource,
};
this.fire('dragleave', options);

// fire dragleave on targets
this._fireEnterLeaveEvents(undefined, options);
this._renderDragEffects(e, this._dragSource);
this._dropTarget = undefined;
// clear targets
this.targets = [];
this._hoveredTargets = [];
Expand All @@ -477,8 +523,11 @@ export class Canvas extends SelectableCanvas {
* @param {Event} e
*/
private _onDrop(e: DragEvent) {
const options = this._simpleEventHandler('drop:before', {
const { target, targets } = this.findDragTargets(e);
const options = this._basicEventHandler('drop:before', {
e,
target,
subTargets: targets,
dragSource: this._dragSource,
pointer: this.getPointer(e),
});
Expand All @@ -499,7 +548,13 @@ export class Canvas extends SelectableCanvas {
* @param {Event} e Event object fired on mousedown
*/
private _onContextMenu(e: TPointerEvent): false {
const options = this._simpleEventHandler('contextmenu:before', { e });
const target = this.findTarget(e),
subTargets = this.targets || [];
const options = this._basicEventHandler('contextmenu:before', {
e,
target,
subTargets,
});
// TODO: this line is silly because the dev can subscribe to the event and prevent it themselves
this.stopContextMenu && stopEvent(e);
this._basicEventHandler('contextmenu', options);
Expand Down Expand Up @@ -697,7 +752,10 @@ export class Canvas extends SelectableCanvas {
_onMouseMove(e: TPointerEvent) {
const activeObject = this.getActiveObject();
!this.allowTouchScrolling &&
(!activeObject || !activeObject.__isDragging) &&
(!activeObject ||
// a drag event sequence is started by the active object flagging itself on mousedown / mousedown:before
// we must not prevent the event's default behavior in order for the window to start the drag event sequence
!activeObject.__isDragging) &&
e.preventDefault &&
e.preventDefault();
this.__onMouseMove(e);
Expand Down Expand Up @@ -743,12 +801,10 @@ export class Canvas extends SelectableCanvas {
* @param {Event} e Event object fired on mouseup
*/
__onMouseUp(e: TPointerEvent) {
const transform = this._currentTransform,
groupSelector = this._groupSelector,
isClick =
!groupSelector || (groupSelector.left === 0 && groupSelector.top === 0);
ShaMan123 marked this conversation as resolved.
Show resolved Hide resolved
const transform = this._currentTransform;
this._cacheTransformEventData(e);
const target = this._target;
const isClick = this._isClick;
this._handleEvent(e, 'up:before');
// if right/middle click just fire events and return
// target undefined will make the _handleEvent search the target
Expand Down Expand Up @@ -840,41 +896,17 @@ export class Canvas extends SelectableCanvas {
target && (target.__corner = 0);
if (shouldRender) {
this.requestRenderAll();
} else if (!isClick) {
} else if (
!isClick &&
!(
isInteractiveTextObject(this._activeObject) &&
this._activeObject.isEditing
)
) {
this.renderTop();
}
}

/**
* @private
* Handle event firing for target and subtargets
* @param {String} eventType event to fire (up, down or move)
* @param {Event} e event from mouse
* @param {object} [data] event data overrides
* @return {object} options
*/
_simpleEventHandler<
T extends keyof (CanvasEvents | ObjectEvents),
E extends TPointerEvent | DragEvent
>(
eventType: T,
{
e,
...data
}: Omit<(CanvasEvents & ObjectEvents)[T], 'target' | 'subTargets'> &
TEvent<E>
) {
const target = this.findTarget(e),
subTargets = this.targets || [];
// @ts-expect-error TODO fix generic e
return this._basicEventHandler(eventType, {
e,
target,
subTargets,
...data,
});
}

_basicEventHandler<T extends keyof (CanvasEvents | ObjectEvents)>(
eventType: T,
options: (CanvasEvents & ObjectEvents)[T]
Expand Down Expand Up @@ -1031,6 +1063,7 @@ export class Canvas extends SelectableCanvas {
* @param {Event} e Event object fired on mousedown
*/
__onMouseDown(e: TPointerEvent) {
this._isClick = true;
this._cacheTransformEventData(e);
this._handleEvent(e, 'down:before');
let target: FabricObject | undefined = this._target;
Expand Down Expand Up @@ -1173,6 +1206,7 @@ export class Canvas extends SelectableCanvas {
* @param {Event} e Event object fired on mousemove
*/
__onMouseMove(e: TPointerEvent) {
this._isClick = false;
this._handleEvent(e, 'move:before');
this._cacheTransformEventData(e);

Expand Down
Loading