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

feat(Object): pass ctx to dnd methods #9844

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [next]

- chore(FabricObject): pass `e` to `shouldStartDragging` [#9843](https://github.com/fabricjs/fabric.js/pull/9843)
- feat(Object): pass `ctx` to dnd methods [#9844](https://github.com/fabricjs/fabric.js/pull/9844)
- ci(): Add Jest coverage to the report [#9836](https://github.com/fabricjs/fabric.js/pull/9836)
- test(): Add cursor animation testing and migrate some easy one to jest [#9829](https://github.com/fabricjs/fabric.js/pull/9829)
- fix(Group, Controls): Fix interactive group actions when negative scaling is involved [#9811](https://github.com/fabricjs/fabric.js/pull/9811)
Expand Down
20 changes: 13 additions & 7 deletions src/EventTypeDefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,22 @@ interface OutEvent {
nextTarget?: FabricObject;
}

export type DragEventRenderingEffectData = {
e: DragEvent;
dragSource: FabricObject | undefined;
dropTarget: FabricObject | undefined;
prevDropTarget: FabricObject | undefined;
viewportPoint: Point;
scenePoint: Point;
};

export interface DragEventData extends TEvent<DragEvent> {
target?: FabricObject;
subTargets?: FabricObject[];
dragSource?: FabricObject;
canDrop?: boolean;
didDrop?: boolean;
dropTarget?: FabricObject;
}

export interface DropEventData extends DragEventData {
/**
* @deprecated
* use viewportPoint instead.
Expand All @@ -192,15 +198,15 @@ export interface DropEventData extends DragEventData {
}

interface DnDEvents {
dragstart: TEventWithTarget<DragEvent>;
dragstart: DragEventData;
drag: DragEventData;
dragover: DragEventData;
dragenter: DragEventData & InEvent;
dragleave: DragEventData & OutEvent;
dragend: DragEventData;
'drop:before': DropEventData;
drop: DropEventData;
'drop:after': DropEventData;
'drop:before': DragEventData;
drop: DragEventData;
'drop:after': DragEventData;
}

interface CanvasDnDEvents extends DnDEvents {
Expand Down
85 changes: 56 additions & 29 deletions src/canvas/Canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NONE } from '../constants';
import type {
CanvasEvents,
DragEventData,
DragEventRenderingEffectData,
ObjectEvents,
TPointerEvent,
TPointerEventNames,
Expand Down Expand Up @@ -288,7 +289,11 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
const activeObject = this.getActiveObject();
if (activeObject && activeObject.onDragStart(e)) {
this._dragSource = activeObject;
const options = { e, target: activeObject };
const options: DragEventData = {
...getEventPoints(this, e),
e,
target: activeObject,
};
this.fire('dragstart', options);
activeObject.fire('dragstart', options);
addListener(
Expand All @@ -307,35 +312,35 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
* Doing so will render the correct effect for all cases including an overlap between `source` and `target`.
* @private
*/
private _renderDragEffects(
e: DragEvent,
source?: FabricObject,
target?: FabricObject
) {
private _renderDragEffects(context: DragEventRenderingEffectData) {
const { dragSource, dropTarget, prevDropTarget } = context;
let dirty = false;
// clear top context
const dropTarget = this._dropTarget;
if (dropTarget && dropTarget !== source && dropTarget !== target) {
dropTarget.clearContextTop();
if (
prevDropTarget &&
prevDropTarget !== dragSource &&
prevDropTarget !== dropTarget
) {
prevDropTarget.clearContextTop();
dirty = true;
}
source?.clearContextTop();
target !== source && target?.clearContextTop();
dragSource?.clearContextTop();
dropTarget !== dragSource && dropTarget?.clearContextTop();
// render effects
const ctx = this.contextTop;
ctx.save();
ctx.transform(...this.viewportTransform);
if (source) {
if (dragSource) {
ctx.save();
source.transform(ctx);
source.renderDragSourceEffect(e);
dragSource.transform(ctx);
dragSource.renderDragSourceEffect(ctx, context);
ctx.restore();
dirty = true;
}
if (target) {
if (dropTarget) {
ctx.save();
target.transform(ctx);
target.renderDropTargetEffect(e);
dropTarget.transform(ctx);
dropTarget.renderDropTargetEffect(ctx, context);
ctx.restore();
dirty = true;
}
Expand All @@ -352,7 +357,8 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
private _onDragEnd(e: DragEvent) {
const didDrop = !!e.dataTransfer && e.dataTransfer.dropEffect !== NONE,
dropTarget = didDrop ? this._activeObject : undefined,
options = {
options: DragEventData = {
...getEventPoints(this, e),
e,
target: this._dragSource as FabricObject,
subTargets: this.targets,
Expand All @@ -378,7 +384,8 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
* @param {DragEvent} e
*/
private _onDragProgress(e: DragEvent) {
const options = {
const options: DragEventData = {
...getEventPoints(this, e),
e,
target: this._dragSource as FabricObject | undefined,
dragSource: this._dragSource as FabricObject | undefined,
Expand All @@ -394,11 +401,13 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
*/
protected findDragTargets(e: DragEvent) {
this.targets = [];
const points = getEventPoints(this, e);
const target = this._searchPossibleTargets(
this._objects,
this.getViewportPoint(e)
points.viewportPoint
);
return {
...points,
target,
targets: [...this.targets],
};
Expand All @@ -412,9 +421,10 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
*/
private _onDragOver(e: DragEvent) {
const eventType = 'dragover';
const { target, targets } = this.findDragTargets(e);
const { target, targets, ...points } = this.findDragTargets(e);
const dragSource = this._dragSource as FabricObject;
const options = {
const options: DragEventData = {
...points,
e,
target,
subTargets: targets,
Expand Down Expand Up @@ -446,7 +456,13 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
subTarget.fire(eventType, options);
}
// render drag effects now that relations between source and target is clear
this._renderDragEffects(e, dragSource, dropTarget);
this._renderDragEffects({
...points,
e,
dragSource,
dropTarget,
prevDropTarget: this._dropTarget,
});
this._dropTarget = dropTarget;
}

Expand All @@ -456,8 +472,9 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
* @param {Event} [e] Event object fired on Event.js shake
*/
private _onDragEnter(e: DragEvent) {
const { target, targets } = this.findDragTargets(e);
const options = {
const { target, targets, ...points } = this.findDragTargets(e);
const options: DragEventData = {
...points,
e,
target,
subTargets: targets,
Expand All @@ -474,7 +491,9 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
* @param {Event} [e] Event object fired on Event.js shake
*/
private _onDragLeave(e: DragEvent) {
const options = {
const points = getEventPoints(this, e);
const options: DragEventData = {
...points,
e,
target: this._draggedoverTarget,
subTargets: this.targets,
Expand All @@ -484,7 +503,13 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {

// fire dragleave on targets
this._fireEnterLeaveEvents(undefined, options);
this._renderDragEffects(e, this._dragSource);
this._renderDragEffects({
...points,
e,
dragSource: this._dragSource,
dropTarget: undefined,
prevDropTarget: this._dropTarget,
});
this._dropTarget = undefined;
// clear targets
this.targets = [];
Expand All @@ -500,13 +525,13 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
* @param {Event} e
*/
private _onDrop(e: DragEvent) {
const { target, targets } = this.findDragTargets(e);
const { target, targets, ...points } = this.findDragTargets(e);
const options = this._basicEventHandler('drop:before', {
...points,
e,
target,
subTargets: targets,
dragSource: this._dragSource,
...getEventPoints(this, e),
});
// will be set by the drop target
options.didDrop = false;
Expand Down Expand Up @@ -918,6 +943,8 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
for (let i = 0; i < targets.length; i++) {
targets[i] !== target && targets[i].fire(`mouse${eventType}`, options);
}

return options;
}

/**
Expand Down
8 changes: 2 additions & 6 deletions src/shapes/IText/DraggableTextDelegate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { Canvas } from '../../canvas/Canvas';
import type {
DragEventData,
DropEventData,
TPointerEvent,
} from '../../EventTypeDefs';
import type { DragEventData, TPointerEvent } from '../../EventTypeDefs';
import { Point } from '../../Point';
import type { IText } from './IText';
import { setStyle } from '../../util/dom_style';
Expand Down Expand Up @@ -268,7 +264,7 @@ export class DraggableTextDelegate {
* in order to change the drop value or to customize styling respectively, by listening to the `drop:before` event
* https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#performing_a_drop
*/
dropHandler(ev: DropEventData) {
dropHandler(ev: DragEventData) {
const { e } = ev;
const didDrop = e.defaultPrevented;
this.__isDraggingOver = false;
Expand Down
17 changes: 8 additions & 9 deletions src/shapes/IText/IText.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Canvas } from '../../canvas/Canvas';
import { Canvas } from '../../canvas/Canvas';
import '../../../jest.extend';
import { Group } from '../Group';
import { IText } from './IText';
Expand All @@ -25,15 +25,14 @@ describe('IText', () => {
const group = new Group([text]);
group.set({ scaleX: scale, scaleY: scale, angle });
group.setCoords();
const fillRect = jest.fn();
const getZoom = jest.fn().mockReturnValue(zoom);
const mockContext = { fillRect };
const mockCanvas = { contextTop: mockContext, getZoom };
jest.replaceProperty(text, 'canvas', mockCanvas as unknown as Canvas);
const canvas = new Canvas();
canvas.setZoom(zoom);
jest.replaceProperty(text, 'canvas', canvas);
const spy = jest.spyOn(canvas.getTopContext(), 'fillRect');

text.renderCursorAt(1);
const call = fillRect.mock.calls[0];
expect({ width: call[2], height: call[3] }).toMatchSnapshot({
text.renderCursorAt(canvas.getTopContext(), 1);
const [left, top, width, height] = spy.mock.calls[0];
expect({ width, height }).toMatchSnapshot({
cloneDeepWith: (value) =>
typeof value === 'number' ? value.toFixed(3) : undefined,
});
Expand Down
21 changes: 12 additions & 9 deletions src/shapes/IText/IText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '../Text/constants';
import { CENTER, LEFT, RIGHT } from '../../constants';
import type { ObjectToCanvasElementOptions } from '../Object/Object';
import type { DragEventRenderingEffectData } from '../../EventTypeDefs';

type CursorBoundaries = {
left: number;
Expand Down Expand Up @@ -478,13 +479,12 @@ export class IText<
}

/**
* Renders cursor on context Top, outside the animation cycle, on request
* Renders cursor on {@link ctx}, outside the animation cycle, on request.
* Used for the drag/drop effect.
* If contextTop is not available, do nothing.
*/
renderCursorAt(selectionStart: number) {
renderCursorAt(ctx: CanvasRenderingContext2D, selectionStart: number) {
const boundaries = this._getCursorBoundaries(selectionStart, true);
this._renderCursor(this.canvas!.contextTop, boundaries, selectionStart);
this._renderCursor(ctx, boundaries, selectionStart);
}

/**
Expand Down Expand Up @@ -550,21 +550,24 @@ export class IText<
}

/**
* Renders drag start text selection
* @override Render drag start text selection
*/
renderDragSourceEffect() {
renderDragSourceEffect(ctx: CanvasRenderingContext2D) {
const dragStartSelection =
this.draggableTextDelegate.getDragStartSelection()!;
this._renderSelection(
this.canvas!.contextTop,
ctx,
dragStartSelection,
this._getCursorBoundaries(dragStartSelection.selectionStart, true)
);
}

renderDropTargetEffect(e: DragEvent) {
renderDropTargetEffect(
ctx: CanvasRenderingContext2D,
{ e }: DragEventRenderingEffectData
) {
const dragSelection = this.getSelectionStartFromPointer(e);
this.renderCursorAt(dragSelection);
this.renderCursorAt(ctx, dragSelection);
}

/**
Expand Down
Loading
Loading