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(render-engine): facade adds onCellPointerOver,onCellDragOver,onCellDrop #2240

Merged
merged 8 commits into from
May 22, 2024
47 changes: 45 additions & 2 deletions packages/engine-render/src/base-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Disposable, Observable } from '@univerjs/core';

import type { EVENT_TYPE } from './basics/const';
import { CURSOR_TYPE, RENDER_CLASS_TYPE } from './basics/const';
import type { IMouseEvent, IPointerEvent, IWheelEvent } from './basics/i-events';
import type { IDragEvent, IMouseEvent, IPointerEvent, IWheelEvent } from './basics/i-events';
import type { IObjectFullState, ITransformChangeState } from './basics/interfaces';
import { TRANSFORM_CHANGE_OBSERVABLE_TYPE } from './basics/interfaces';
import { generateRandomKey, toPx } from './basics/tools';
Expand Down Expand Up @@ -72,6 +72,14 @@ export abstract class BaseObject extends Disposable {

onPointerEnterObserver = new Observable<IPointerEvent | IMouseEvent>();

onDragLeaveObserver = new Observable<IDragEvent | IMouseEvent>();

onDragOverObserver = new Observable<IDragEvent | IMouseEvent>();

onDragEnterObserver = new Observable<IDragEvent | IMouseEvent>();

onDropObserver = new Observable<IDragEvent | IMouseEvent>();

onIsAddedToParentObserver = new Observable<any>();

onDisposeObserver = new Observable<BaseObject>();
Expand Down Expand Up @@ -670,9 +678,40 @@ export abstract class BaseObject extends Disposable {
return true;
}

triggerDragLeave(evt: IDragEvent | IMouseEvent) {
if (!this.onDragLeaveObserver.notifyObservers(evt)?.stopPropagation) {
this._parent?.triggerDragLeave(evt);
return false;
}
return true;
}

triggerDragOver(evt: IDragEvent | IMouseEvent) {
if (!this.onDragOverObserver.notifyObservers(evt)?.stopPropagation) {
this._parent?.triggerDragOver(evt);
return false;
}
return true;
}

triggerDragEnter(evt: IDragEvent | IMouseEvent) {
if (!this.onDragEnterObserver.notifyObservers(evt)?.stopPropagation) {
this._parent?.triggerDragEnter(evt);
return false;
}
return true;
}

triggerDrop(evt: IDragEvent | IMouseEvent) {
if (!this.onDropObserver.notifyObservers(evt)?.stopPropagation) {
this._parent?.triggerDrop(evt);
return false;
}
return true;
}

override dispose() {
super.dispose();

this.onTransformChangeObservable.clear();
this.onPointerDownObserver.clear();
this.onPointerMoveObserver.clear();
Expand All @@ -682,6 +721,10 @@ export abstract class BaseObject extends Disposable {
this.onPointerLeaveObserver.clear();
this.onPointerOverObserver.clear();
this.onPointerEnterObserver.clear();
this.onDragLeaveObserver.clear();
this.onDragOverObserver.clear();
this.onDragEnterObserver.clear();
this.onDropObserver.clear();
this.onDblclickObserver.clear();
this.onTripleClickObserver.clear();
this.onIsAddedToParentObserver.clear();
Expand Down
11 changes: 11 additions & 0 deletions packages/engine-render/src/basics/i-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,17 @@ export interface IPointerEvent extends IMouseEvent {
pointerType: string;
}

/**
* Native friendly interface for DragEvent Object
*/
export interface IDragEvent extends IMouseEvent {
// Properties
/**
* Holds the drag operation's data
*/
dataTransfer: DataTransfer;
}

/**
* Native friendly interface for WheelEvent Object
*/
Expand Down
113 changes: 113 additions & 0 deletions packages/engine-render/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ export class Engine extends ThinEngine<Scene> {

private _pointerLeaveEvent!: (evt: any) => void;

private _dragEnterEvent!: (evt: any) => void;

private _dragLeaveEvent!: (evt: any) => void;

private _dragOverEvent!: (evt: any) => void;

private _dropEvent!: (evt: any) => void;

private _remainCapture: number = -1;

/** previous pointer position */
Expand All @@ -103,6 +111,8 @@ export class Engine extends ThinEngine<Scene> {

this._handleKeyboardAction();
this._handlePointerAction();
this._handleDragAction();

if (mode !== CanvasRenderMode.Printing) {
this._matchMediaHandler();
}
Expand Down Expand Up @@ -233,6 +243,10 @@ export class Engine extends ThinEngine<Scene> {
canvasEle.removeEventListener(`${eventPrefix}down`, this._pointerDownEvent);
canvasEle.removeEventListener(`${eventPrefix}up`, this._pointerUpEvent);
canvasEle.removeEventListener('blur', this._pointerBlurEvent);
canvasEle.removeEventListener('dragenter', this._dragEnterEvent);
canvasEle.removeEventListener('dragleave', this._dragLeaveEvent);
canvasEle.removeEventListener('dragover', this._dragOverEvent);
canvasEle.removeEventListener('drop', this._dropEvent);
canvasEle.removeEventListener(this._getWheelEventName(), this._pointerWheelEvent);

this._activeRenderLoops = [];
Expand Down Expand Up @@ -664,6 +678,105 @@ export class Engine extends ThinEngine<Scene> {
);
}

private _handleDragAction() {
this._dragEnterEvent = (evt: any) => {
const deviceType = this._getPointerType(evt);
// Store previous values for event
const deviceEvent = evt as IPointerEvent;
deviceEvent.deviceType = deviceType;

deviceEvent.currentState = 4;

this.onInputChangedObservable.notifyObservers(deviceEvent);
};

this._dragLeaveEvent = (evt: any) => {
const deviceType = this._getPointerType(evt);
// Store previous values for event
const deviceEvent = evt as IPointerEvent;
deviceEvent.deviceType = deviceType;

deviceEvent.currentState = 5;

this.onInputChangedObservable.notifyObservers(deviceEvent);
};

this._dragOverEvent = (evt: any) => {
// prevent default to allow drop
evt.preventDefault();

const deviceType = this._getPointerType(evt);
// Store previous values for event
const previousHorizontal = this.pointer[PointerInput.Horizontal];
const previousVertical = this.pointer[PointerInput.Vertical];
const previousDeltaHorizontal = this.pointer[PointerInput.DeltaHorizontal];
const previousDeltaVertical = this.pointer[PointerInput.DeltaVertical];

this.pointer[PointerInput.Horizontal] = evt.clientX;
this.pointer[PointerInput.Vertical] = evt.clientY;
this.pointer[PointerInput.DeltaHorizontal] = evt.movementX;
this.pointer[PointerInput.DeltaVertical] = evt.movementY;
// console.log('pointerMoveEvent_1', previousHorizontal, evt.clientX, previousVertical, evt.clientY, this._pointer);
const deviceEvent = evt as IPointerEvent;
deviceEvent.deviceType = deviceType;

if (previousHorizontal !== evt.clientX) {
deviceEvent.inputIndex = PointerInput.Horizontal;
deviceEvent.previousState = previousHorizontal;
deviceEvent.currentState = this.pointer[PointerInput.Horizontal];

this.onInputChangedObservable.notifyObservers(deviceEvent);
}
if (previousVertical !== evt.clientY) {
deviceEvent.inputIndex = PointerInput.Vertical;
deviceEvent.previousState = previousVertical;
deviceEvent.currentState = this.pointer[PointerInput.Vertical];

this.onInputChangedObservable.notifyObservers(deviceEvent);
}
if (this.pointer[PointerInput.DeltaHorizontal] !== 0) {
deviceEvent.inputIndex = PointerInput.DeltaHorizontal;
deviceEvent.previousState = previousDeltaHorizontal;
deviceEvent.currentState = this.pointer[PointerInput.DeltaHorizontal];

this.onInputChangedObservable.notifyObservers(deviceEvent);
}
if (this.pointer[PointerInput.DeltaVertical] !== 0) {
deviceEvent.inputIndex = PointerInput.DeltaVertical;
deviceEvent.previousState = previousDeltaVertical;
deviceEvent.currentState = this.pointer[PointerInput.DeltaVertical];

this.onInputChangedObservable.notifyObservers(deviceEvent);
}

// Lets Propagate the event for move with same position.
if (!this._usingSafari && evt.button !== -1) {
deviceEvent.inputIndex = evt.button + 2;
deviceEvent.previousState = this.pointer[evt.button + 2];
this.pointer[evt.button + 2] = this.pointer[evt.button + 2] ? 0 : 1; // Reverse state of button if evt.button has value
deviceEvent.currentState = this.pointer[evt.button + 2];
this.onInputChangedObservable.notifyObservers(deviceEvent);
}
};

this._dropEvent = (evt: any) => {
const deviceType = this._getPointerType(evt);
// Store previous values for event
const deviceEvent = evt as IPointerEvent;
deviceEvent.deviceType = deviceType;

deviceEvent.currentState = 6;

this.onInputChangedObservable.notifyObservers(deviceEvent);
};

const canvasEle = this.getCanvasElement();
canvasEle.addEventListener('dragenter', this._dragEnterEvent);
canvasEle.addEventListener('dragleave', this._dragLeaveEvent);
canvasEle.addEventListener('dragover', this._dragOverEvent);
canvasEle.addEventListener('drop', this._dropEvent);
}

private _getWheelEventName(): string {
const wheelEventName =
'onwheel' in document.createElement('div')
Expand Down
86 changes: 85 additions & 1 deletion packages/engine-render/src/scene.input-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Disposable, type Nullable, type Observer, toDisposable } from '@univerj

import type { BaseObject } from './base-object';
import { RENDER_CLASS_TYPE } from './basics/const';
import type { IEvent, IKeyboardEvent, IMouseEvent, IPointerEvent, IWheelEvent } from './basics/i-events';
import type { IDragEvent, IEvent, IKeyboardEvent, IMouseEvent, IPointerEvent, IWheelEvent } from './basics/i-events';
import { DeviceType, PointerInput } from './basics/i-events';
import { Vector2 } from './basics/vector2';
import type { ThinScene } from './thin-scene';
Expand Down Expand Up @@ -65,6 +65,15 @@ export class InputManager extends Disposable {

private _onKeyUp!: (evt: IKeyboardEvent) => void;

// Drag
private _onDragEnter!: (evt: IDragEvent) => void;

private _onDragLeave!: (evt: IDragEvent) => void;

private _onDragOver!: (evt: IDragEvent) => void;

private _onDrop!: (evt: IDragEvent) => void;

private _scene!: ThinScene;

private _currentMouseEnterPicked: Nullable<BaseObject | ThinScene>;
Expand Down Expand Up @@ -108,6 +117,10 @@ export class InputManager extends Disposable {
this._onMouseWheel = null as unknown as (evt: IWheelEvent) => void;
this._onKeyDown = null as unknown as (evt: IKeyboardEvent) => void;
this._onKeyUp = null as unknown as (evt: IKeyboardEvent) => void;
this._onDragEnter = null as unknown as (evt: IDragEvent) => void;
this._onDragLeave = null as unknown as (evt: IDragEvent) => void;
this._onDragOver = null as unknown as (evt: IDragEvent) => void;
this._onDrop = null as unknown as (evt: IDragEvent) => void;
}

// Handle events such as triggering mouseleave and mouseenter.
Expand All @@ -124,6 +137,20 @@ export class InputManager extends Disposable {
}
}

// Handle events such as triggering dragleave and dragenter.
dragLeaveEnterHandler(evt: IMouseEvent) {
const o = this._currentObject;
if (o === null || o === undefined) {
this._currentMouseEnterPicked?.triggerDragLeave(evt);
this._currentMouseEnterPicked = null;
} else if (o !== this._currentMouseEnterPicked) {
const previousPicked = this._currentMouseEnterPicked;
this._currentMouseEnterPicked = o;
previousPicked?.triggerDragLeave(evt);
o?.triggerDragEnter(evt);
}
}

// eslint-disable-next-line max-lines-per-function
attachControl(
hasDown: boolean = true,
Expand Down Expand Up @@ -264,6 +291,44 @@ export class InputManager extends Disposable {
}
};

this._onDragEnter = (evt: IDragEvent) => {
this._currentObject = this._getCurrentObject(evt.offsetX, evt.offsetY);
this._currentObject?.triggerDragOver(evt);

this.dragLeaveEnterHandler(evt);
};

this._onDragLeave = (evt: IDragEvent) => {
this._currentObject = null;

this.dragLeaveEnterHandler(evt);
};

this._onDragOver = (evt: IDragEvent) => {
this._currentObject = this._getCurrentObject(evt.offsetX, evt.offsetY);
const isStop = this._currentObject?.triggerDragOver(evt);

this.dragLeaveEnterHandler(evt);

if (this._checkDirectSceneEventTrigger(!isStop, this._currentObject)) {
if (this._scene.onDragOverObserver.hasObservers()) {
this._scene.onDragOverObserver.notifyObservers(evt);
this._scene.getEngine()?.setRemainCapture();
}
}
};

this._onDrop = (evt: IDragEvent) => {
const currentObject = this._getCurrentObject(evt.offsetX, evt.offsetY);
const isStop = currentObject?.triggerDrop(evt);

if (this._checkDirectSceneEventTrigger(!isStop, currentObject)) {
if (this._scene.onDropObserver.hasObservers()) {
this._scene.onDropObserver.notifyObservers(evt);
}
}
};

// eslint-disable-next-line complexity
this._onInputObserver = engine.onInputChangedObservable.add((eventData: IEvent) => {
const evt: IEvent = eventData;
Expand All @@ -278,6 +343,25 @@ export class InputManager extends Disposable {
}
}

// Drag Events
if ((eventData as IDragEvent).dataTransfer) {
if (hasMove &&
(eventData.inputIndex === PointerInput.Horizontal ||
eventData.inputIndex === PointerInput.Vertical ||
eventData.inputIndex === PointerInput.DeltaHorizontal ||
eventData.inputIndex === PointerInput.DeltaVertical)) {
this._onDragOver(evt as IDragEvent);
} else if (hasEnter && eventData.currentState === 4) {
this._onDragEnter(evt as IDragEvent);
} else if (hasLeave && eventData.currentState === 5) {
this._onDragLeave(evt as IDragEvent);
} else if (hasUp && eventData.currentState === 6) {
this._onDrop(evt as IDragEvent);
}

return;
}

// Pointer Events
if (eventData.deviceType === DeviceType.Mouse || eventData.deviceType === DeviceType.Touch) {
if (
Expand Down
Loading
Loading