Skip to content

Commit

Permalink
Drag and drop support off of actors
Browse files Browse the repository at this point in the history
Adds several events for dragging of actors, this will allow developers to
implemented their own style of 'drag and drop', such as 'pulling'
or 'stretching'

New Events:
 - Enter 'enter'
 - Leave 'leave'
 - Pointer Enter 'pointerenter'
 - Pointer Leave 'pointerleave'
 - Pointer Drag Start 'pointerdragstart'
 - Pointer Drag End 'pointerdragend'
 - Pointer Drag Move 'pointerdragmove'
 - Pointer Drag Enter 'pointerdragenter'
 - Pointer Drag Leave 'pointerdragleave'

New Classes:
 - PointerDragEvent extends PointerEvent
 - GlobalCoordinates (contains Vectors for world, page, screen)

New ICapturePointerConfig Property:
 - captureDragEvents (whether to emit drag events to the actor)

Additional:
 - PointerEvent now contains original pointer object.
 - Deprecated some coordinate properties in favour of GlobalCoordinates
 property or a derivative.

Resolves: excaliburjs#134
  • Loading branch information
pathurs committed Feb 24, 2018
1 parent 06e1890 commit 1c717b0
Show file tree
Hide file tree
Showing 8 changed files with 840 additions and 205 deletions.
2 changes: 1 addition & 1 deletion sandbox/tests/input/pointer.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<dt>Number of Pointers</dt>
<dd id="pointer-num">0</dd>
<dt>Pointer (Primary) Button</dt>
<dd id="pointer-btn"></dd>
<dd id="pointer-btn">None</dd>
<dt>Last Pointer Page Pos (x,y)</dt>
<dd id="pointer-page-pos"></dd>
<dt>Last Pointer Screen Pos (x,y)</dt>
Expand Down
46 changes: 24 additions & 22 deletions sandbox/tests/input/pointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,46 @@ var game = new ex.Engine({
});
var box = new ex.Actor(200, 200, 100, 100, ex.Color.Red);
var cursor = new ex.Actor(0, 0, 10, 10, ex.Color.Chartreuse);
var boxPointerDown = false;
var boxPointerDragging = false;

var uiElement = new ex.UIActor(200, 0, 200, 200);
uiElement.color = ex.Color.Azure.clone();
uiElement.color = ex.Color.Azure;
uiElement.on('pointerdown', (p: ex.Input.PointerEvent) => {
console.log(p);
uiElement.color = ex.Color.Red.clone();
uiElement.color = ex.Color.Red;
});

// Enable pointer input for box
//box.enableCapturePointer = true;

// Enable tracking mouse movement for box
//box.capturePointer.captureMoveEvents = true;

// Change color of box when clicked
box.on("pointerup", (pe: ex.Input.PointerEvent) => {
boxPointerDown = false;

box.on("pointerdown", (pe: ex.Input.PointerEvent) => {
if (box.color == ex.Color.Red) {
box.color = ex.Color.Blue;
} else {
box.color = ex.Color.Red;
}
});

// Set drag flag
box.on("pointerdragstart", (pe: ex.Input.PointerEvent) => {
boxPointerDragging = true;
});

// Set drag flag
box.on("pointerdragend", (pe: ex.Input.PointerEvent) => {
boxPointerDragging = false;
});

// Drag box around
box.on("pointermove", (pe: ex.Input.PointerEvent) => {
if (boxPointerDown) {
box.pos.x = pe.x;
box.pos.y = pe.y;
box.on("pointerdragmove", (pe: ex.Input.PointerEvent) => {
if (boxPointerDragging) {
box.pos = pe.pointer.lastWorldPos;
}
});

// Set pointer down flag
box.on("pointerdown", (pe: ex.Input.PointerEvent) => {
boxPointerDown = true;
// Drag box around
box.on("pointerdragleave", (pe: ex.Input.PointerEvent) => {
if (boxPointerDragging) {
box.pos = pe.pointer.lastWorldPos;
}
});

box.on("pointerwheel", (pe: ex.Input.WheelEvent) => {
Expand All @@ -53,16 +56,15 @@ box.on("pointerwheel", (pe: ex.Input.WheelEvent) => {

// Follow cursor
game.input.pointers.primary.on("move", (pe: ex.Input.PointerEvent) => {
cursor.pos.x = pe.x;
cursor.pos.y = pe.y;
cursor.pos = pe.pointer.lastWorldPos;
});

// Button type
game.input.pointers.primary.on("down", (pe: ex.Input.PointerEvent) => {
document.getElementById("pointer-btn").innerHTML = ex.Input.PointerButton[pe.button];
});
game.input.pointers.primary.on("up", (pe: ex.Input.PointerEvent) => {
document.getElementById("pointer-btn").innerHTML = "";
document.getElementById("pointer-btn").innerHTML = "None";
});

// Wheel
Expand Down
56 changes: 46 additions & 10 deletions src/engine/Actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { BoundingBox } from './Collision/BoundingBox';
import { Texture } from './Resources/Texture';
import {
InitializeEvent, KillEvent, PreUpdateEvent, PostUpdateEvent,
PreDrawEvent, PostDrawEvent, PreDebugDrawEvent, PostDebugDrawEvent,
PreDrawEvent, PostDrawEvent, PreDebugDrawEvent, PostDebugDrawEvent,
GameEvent, PostCollisionEvent, PreCollisionEvent, CollisionStartEvent, CollisionEndEvent
} from './Events';
import { PointerEvent, PointerDragEvent } from './Input/Pointer';
import { Engine } from './Engine';
import { Color } from './Drawing/Color';
import { Sprite } from './Drawing/Sprite';
Expand Down Expand Up @@ -382,13 +383,13 @@ export class ActorImpl extends Class implements IActionable, IEvented {
*
* The default is `null` which prevents a rectangle from being drawn.
*/
public get color() : Color {
public get color(): Color {
return this._color;
}
public set color(v : Color) {
public set color(v: Color) {
this._color = v.clone();
}
private _color : Color;
private _color: Color;

/**
* Whether or not to enable the [[CapturePointer]] trait that propagates
Expand All @@ -400,7 +401,8 @@ export class ActorImpl extends Class implements IActionable, IEvented {
* Configuration for [[CapturePointer]] trait
*/
public capturePointer: Traits.ICapturePointerConfig = {
captureMoveEvents: false
captureMoveEvents: false,
captureDragEvents: false
};

private _zIndex: number = 0;
Expand Down Expand Up @@ -435,7 +437,7 @@ export class ActorImpl extends Class implements IActionable, IEvented {
// set default opacity of an actor to the color
this.opacity = color.a;
}

// Build default pipeline
//this.traits.push(new ex.Traits.EulerMovement());
// TODO: TileMaps should be converted to a collision area
Expand Down Expand Up @@ -484,14 +486,34 @@ export class ActorImpl extends Class implements IActionable, IEvented {
}
}

private _capturePointerEvents: string[] = [
'pointerup', 'pointerdown', 'pointermove', 'pointerenter', 'pointerleave',
'pointerdragstart', 'pointerdragend', 'pointerdragmove', 'pointerdragenter', 'pointerdragleave'
];

private _captureMoveEvents: string[] = [
'pointermove', 'pointerenter', 'pointerleave',
'pointerdragmove', 'pointerdragenter', 'pointerdragleave'
];

private _captureDragEvents: string[] = [
'pointerdragstart', 'pointerdragend', 'pointerdragmove', 'pointerdragenter', 'pointerdragleave'
];

private _checkForPointerOptIn(eventName: string) {
if (eventName) {
const normalized = eventName.toLowerCase();
if (normalized === 'pointerup' || normalized === 'pointerdown' || normalized === 'pointermove') {

if (this._capturePointerEvents.indexOf(normalized) !== -1) {
this.enableCapturePointer = true;
if (normalized === 'pointermove') {

if (this._captureMoveEvents.indexOf(normalized) !== -1) {
this.capturePointer.captureMoveEvents = true;
}

if (this._captureDragEvents.indexOf(normalized) !== -1) {
this.capturePointer.captureDragEvents = true;
}
}
}
}
Expand All @@ -510,9 +532,16 @@ export class ActorImpl extends Class implements IActionable, IEvented {
public on(eventName: Events.postdebugdraw, handler: (event?: PostDebugDrawEvent) => void): void;
public on(eventName: Events.pointerup, handler: (event?: PointerEvent) => void): void;
public on(eventName: Events.pointerdown, handler: (event?: PointerEvent) => void): void;
public on(eventName: Events.pointerenter, handler: (event?: PointerEvent) => void): void;
public on(eventName: Events.pointerleave, handler: (event?: PointerEvent) => void): void;
public on(eventName: Events.pointermove, handler: (event?: PointerEvent) => void): void;
public on(eventName: Events.pointercancel, handler: (event?: PointerEvent) => void): void;
public on(eventName: Events.pointerwheel, handler: (event?: WheelEvent) => void): void;
public on(eventName: Events.pointerdragstart, handler: (event?: PointerDragEvent) => void): void;
public on(eventName: Events.pointerdragend, handler: (event?: PointerDragEvent) => void): void;
public on(eventName: Events.pointerdragenter, handler: (event?: PointerDragEvent) => void): void;
public on(eventName: Events.pointerdragleave, handler: (event?: PointerDragEvent) => void): void;
public on(eventName: Events.pointerdragmove, handler: (event?: PointerDragEvent) => void): void;
public on(eventName: string, handler: (event?: GameEvent<any>) => void): void;
public on(eventName: string, handler: (event?: any) => void): void {
this._checkForPointerOptIn(eventName);
Expand All @@ -533,9 +562,16 @@ export class ActorImpl extends Class implements IActionable, IEvented {
public once(eventName: Events.postdebugdraw, handler: (event?: PostDebugDrawEvent) => void): void;
public once(eventName: Events.pointerup, handler: (event?: PointerEvent) => void): void;
public once(eventName: Events.pointerdown, handler: (event?: PointerEvent) => void): void;
public once(eventName: Events.pointerenter, handler: (event?: PointerEvent) => void): void;
public once(eventName: Events.pointerleave, handler: (event?: PointerEvent) => void): void;
public once(eventName: Events.pointermove, handler: (event?: PointerEvent) => void): void;
public once(eventName: Events.pointercancel, handler: (event?: PointerEvent) => void): void;
public once(eventName: Events.pointerwheel, handler: (event?: WheelEvent) => void): void;
public once(eventName: Events.pointerdragstart, handler: (event?: PointerDragEvent) => void): void;
public once(eventName: Events.pointerdragend, handler: (event?: PointerDragEvent) => void): void;
public once(eventName: Events.pointerdragenter, handler: (event?: PointerDragEvent) => void): void;
public once(eventName: Events.pointerdragleave, handler: (event?: PointerDragEvent) => void): void;
public once(eventName: Events.pointerdragmove, handler: (event?: PointerDragEvent) => void): void;
public once(eventName: string, handler: (event?: GameEvent<any>) => void): void;
public once(eventName: string, handler: (event?: any) => void): void {
this._checkForPointerOptIn(eventName);
Expand Down Expand Up @@ -825,7 +861,7 @@ export class ActorImpl extends Class implements IActionable, IEvented {
/**
* Returns the actor's [[BoundingBox]] calculated for this instant in world space.
*/
public getBounds() {
public getBounds(): BoundingBox {
// todo cache bounding box
var anchor = this._getCalculatedAnchor();
var pos = this.getWorldPos();
Expand Down Expand Up @@ -1024,7 +1060,7 @@ export class ActorImpl extends Class implements IActionable, IEvented {
}

// Update child actors
for (var i = 0; i < this.children.length; i++) {
for (var i = 0; i < this.children.length; i++) {
this.children[i].update(engine, delta);
}

Expand Down
37 changes: 36 additions & 1 deletion src/engine/Algebra.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Engine } from 'Engine';
import * as Util from 'Util/Util';

/**
* A 2D vector on a plane.
*/
Expand Down Expand Up @@ -415,7 +418,7 @@ export class Line {
} else if (y !== null) {
return new Vector((y - b) / m, y);
} else {
throw new Error('You must provide an X or a Y value');
throw new Error('You must provide an X or a Y value');
}
}

Expand Down Expand Up @@ -496,4 +499,36 @@ export class Projection {
}
return 0;
}
}

export class GlobalCoordinates {
public static fromPagePosition(x: number, y: number, engine: Engine): GlobalCoordinates;
public static fromPagePosition(pos: Vector, engine: Engine): GlobalCoordinates;
public static fromPagePosition(xOrPos: number | Vector, yOrEngine: number | Engine, engineOrUndefined?: Engine): GlobalCoordinates {
var pageX: number;
var pageY: number;
var pagePos: Vector;
var engine: Engine;

if (arguments.length === 3) {
pageX = <number>xOrPos;
pageY = <number>yOrEngine;
pagePos = new Vector(pageX, pageY);
engine = engineOrUndefined;
} else {
pagePos = <Vector>xOrPos;
pageX = pagePos.x;
pageY = pagePos.y;
engine = <Engine>yOrEngine;
}

var screenX: number = pageX - Util.getPosition(engine.canvas).x;
var screenY: number = pageY - Util.getPosition(engine.canvas).y;
var screenPos = new Vector(screenX, screenY);
var worldPos = engine.screenToWorldCoordinates(screenPos);

return new GlobalCoordinates(worldPos, pagePos, screenPos);
}

constructor(public worldPos: Vector, public pagePos: Vector, public screenPos: Vector) { }
}
13 changes: 11 additions & 2 deletions src/engine/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,29 @@ export type stop = 'stop';
export type pointerup = 'pointerup';
export type pointerdown = 'pointerdown';
export type pointermove = 'pointermove';
export type pointerenter = 'pointerenter';
export type pointerleave = 'pointerleave';
export type pointercancel = 'pointercancel';
export type pointerwheel = 'pointerwheel';

export type up = 'up';
export type down = 'down';
export type move = 'move';
export type enter = 'enter';
export type leave = 'leave';
export type cancel = 'cancel';
export type wheel = 'wheel';

export type press = 'press';
export type release = 'release';
export type hold = 'hold';

export type pointerdragstart = 'pointerdragstart';
export type pointerdragend = 'pointerdragend';
export type pointerdragenter = 'pointerdragenter';
export type pointerdragleave = 'pointerdragleave';
export type pointerdragmove = 'pointerdragmove';

/**
* Base event type in Excalibur that all other event types derive from. Not all event types are thrown on all Excalibur game objects,
* some events are unique to a type, others are not.
Expand Down Expand Up @@ -276,7 +286,7 @@ export class HiddenEvent extends GameEvent<Engine> {
* Event thrown on an [[Actor|actor]] when a collision will occur this frame if it resolves
*/
export class PreCollisionEvent extends GameEvent<Actor> {

/**
* @param actor The actor the event was thrown on
* @param other The actor that will collided with the current actor
Expand Down Expand Up @@ -389,7 +399,6 @@ export class EnterViewPortEvent extends GameEvent<Actor> {
}
}


export class EnterTriggerEvent extends GameEvent<Actor> {
constructor(public target: Trigger, public actor: Actor) {
super();
Expand Down
Loading

0 comments on commit 1c717b0

Please sign in to comment.