-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement fireEvent and createEvent (#13)
* Implement events * Add tests * Ensure export * Add wheel observable and more tests
- Loading branch information
Showing
4 changed files
with
258 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { | ||
Engine, | ||
Mesh, | ||
MeshBuilder, | ||
NullEngine, | ||
Scene, | ||
Vector2, | ||
} from '@babylonjs/core'; | ||
import { Event, createEvent, fireEvent } from './event'; | ||
import { EventMap, eventMap } from './eventMap'; | ||
import { AdvancedDynamicTexture, Button, Control, Grid } from '@babylonjs/gui'; | ||
|
||
describe('event', () => { | ||
let scene: Scene, | ||
engine: Engine, | ||
texture: AdvancedDynamicTexture, | ||
containerControl: Grid, | ||
expectedControl: Control, | ||
uiPlane: Mesh; | ||
|
||
beforeAll(() => { | ||
engine = new NullEngine(); | ||
}); | ||
|
||
beforeEach(() => { | ||
scene = new Scene(engine); | ||
|
||
uiPlane = MeshBuilder.CreatePlane('container'); | ||
texture = AdvancedDynamicTexture.CreateForMesh(uiPlane); | ||
|
||
containerControl = new Grid('container'); | ||
containerControl.addColumnDefinition(1); | ||
containerControl.addColumnDefinition(1); | ||
texture.addControl(containerControl); | ||
|
||
expectedControl = new Button('button'); | ||
containerControl.addControl(expectedControl, 0, 0); | ||
}); | ||
|
||
afterEach(() => { | ||
texture.dispose(); | ||
uiPlane.material?.dispose(); | ||
uiPlane.dispose(); | ||
scene.dispose(); | ||
}); | ||
|
||
afterAll(() => { | ||
engine.dispose(); | ||
}); | ||
|
||
it.each(Object.keys(eventMap))( | ||
'should create a default event for %s', | ||
(key: keyof EventMap) => { | ||
const eventFunc = createEvent[key]; | ||
expect(eventFunc).toBeDefined(); | ||
|
||
const event = eventMap[key]; | ||
expect(eventFunc(expectedControl)).toEqual({ | ||
key, | ||
observableName: event.observableName, | ||
eventData: event.defaultInit, | ||
}); | ||
} | ||
); | ||
|
||
it.each(Object.keys(eventMap))( | ||
'should fire the %s event', | ||
(key: keyof EventMap) => { | ||
const spy = jest.fn(); | ||
const { observableName } = eventMap[key]; | ||
|
||
expectedControl[observableName].add(spy as null); | ||
|
||
const fireFunc = fireEvent[key]; | ||
expect(fireFunc).toBeDefined(); | ||
|
||
fireFunc(expectedControl); | ||
|
||
expect(spy).toHaveBeenCalledTimes(1); | ||
} | ||
); | ||
|
||
it('should throw when fireEvent is called with a bad observable name', () => { | ||
expect(() => { | ||
fireEvent(expectedControl, { | ||
key: 'bogusEvent', | ||
observableName: 'notAnObservable', | ||
eventData: {}, | ||
} as unknown as Event<'pointerClick'>); | ||
}).toThrow( | ||
new Error( | ||
`Unable to fire an event - event of type "bogusEvent" does not exist on "${expectedControl.getClassName()}"` | ||
) | ||
); | ||
}); | ||
|
||
it('should allow passing custom data to fireEvent', () => { | ||
const spy = jest.fn(); | ||
expectedControl.onWheelObservable.add((eventData) => { | ||
spy(eventData); | ||
}); | ||
|
||
fireEvent.wheel(expectedControl, new Vector2(0.5, 0.5)); | ||
|
||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith(new Vector2(0.5, 0.5)); | ||
}); | ||
|
||
it('should allow passing custom data to createEvent', () => { | ||
const event = createEvent.wheel(expectedControl, new Vector2(0.5, 0.5)); | ||
|
||
expect(event).toEqual({ | ||
key: 'wheel', | ||
observableName: 'onWheelObservable', | ||
eventData: new Vector2(0.5, 0.5), | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { Observable } from '@babylonjs/core'; | ||
import { Control } from '@babylonjs/gui'; | ||
import { EventMap, eventMap } from './eventMap'; | ||
|
||
export type Event<K extends keyof EventMap> = { | ||
key: K; | ||
observableName: EventMap[K]['observableName']; | ||
eventData: EventMap[K]['defaultInit']; | ||
}; | ||
|
||
function fireEventFn<K extends keyof EventMap>( | ||
control: Control, | ||
event: Event<K> | ||
) { | ||
const eventObservable = control[event.observableName]; | ||
|
||
if (!(eventObservable instanceof Observable)) { | ||
throw new Error( | ||
`Unable to fire an event - event of type "${ | ||
event.key | ||
}" does not exist on "${control.getClassName()}"` | ||
); | ||
} | ||
|
||
(eventObservable as Observable<EventMap[K]['defaultInit']>).notifyObservers( | ||
event.eventData | ||
); | ||
} | ||
|
||
function createEventFn<K extends keyof EventMap>( | ||
eventName: K, | ||
_control: Control, | ||
init: EventMap[K]['defaultInit'] | ||
): Event<K> { | ||
const event = eventMap[eventName]; | ||
return { | ||
key: eventName, | ||
observableName: event.observableName, | ||
eventData: init, | ||
}; | ||
} | ||
|
||
Object.keys(eventMap).forEach((key: keyof EventMap) => { | ||
const { defaultInit } = eventMap[key]; | ||
createEventFn[key] = (node: Control, init?: typeof defaultInit) => | ||
createEventFn(key, node, init ?? defaultInit); | ||
|
||
fireEventFn[key] = (node: Control, init?: typeof defaultInit) => { | ||
fireEventFn(node, createEventFn[key](node, init)); | ||
}; | ||
}); | ||
|
||
type CreateEventFn = ( | ||
k: keyof EventMap, | ||
c: Control, | ||
i: EventMap[keyof EventMap]['defaultInit'] | ||
) => Event<keyof EventMap>; | ||
|
||
type CreateEventObject = { | ||
[K in keyof EventMap]: ( | ||
c: Control, | ||
i?: EventMap[K]['defaultInit'] | ||
) => Event<K>; | ||
}; | ||
|
||
type fireEventFn = (c: Control, e: Event<keyof EventMap>) => void; | ||
|
||
type fireEventObject = { | ||
[K in keyof EventMap]: (c: Control, i?: EventMap[K]['defaultInit']) => void; | ||
}; | ||
|
||
export const createEvent = createEventFn as CreateEventFn & CreateEventObject; | ||
export const fireEvent = fireEventFn as fireEventFn & fireEventObject; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { Vector2 } from '@babylonjs/core'; | ||
import { Vector2WithInfo } from '@babylonjs/gui'; | ||
|
||
export type EventMap = { | ||
pointerMove: { | ||
observableName: 'onPointerUpObservable'; | ||
defaultInit: Vector2WithInfo; | ||
}; | ||
pointerEnter: { | ||
observableName: 'onPointerEnterObservable'; | ||
defaultInit: Vector2WithInfo; | ||
}; | ||
pointerOut: { | ||
observableName: 'onPointerOutObservable'; | ||
defaultInit: Vector2WithInfo; | ||
}; | ||
pointerDown: { | ||
observableName: 'onPointerDownObservable'; | ||
defaultInit: Vector2WithInfo; | ||
}; | ||
pointerUp: { | ||
observableName: 'onPointerUpObservable'; | ||
defaultInit: Vector2WithInfo; | ||
}; | ||
pointerClick: { | ||
observableName: 'onPointerClickObservable'; | ||
defaultInit: Vector2WithInfo; | ||
}; | ||
wheel: { | ||
observableName: 'onWheelObservable'; | ||
defaultInit: Vector2; | ||
}; | ||
}; | ||
|
||
const DEFAULT_VECTOR2_WITH_INFO = new Vector2WithInfo(Vector2.Zero()); | ||
|
||
export const eventMap: EventMap = { | ||
pointerMove: { | ||
observableName: 'onPointerUpObservable', | ||
defaultInit: DEFAULT_VECTOR2_WITH_INFO, | ||
}, | ||
pointerEnter: { | ||
observableName: 'onPointerEnterObservable', | ||
defaultInit: DEFAULT_VECTOR2_WITH_INFO, | ||
}, | ||
pointerOut: { | ||
observableName: 'onPointerOutObservable', | ||
defaultInit: DEFAULT_VECTOR2_WITH_INFO, | ||
}, | ||
pointerDown: { | ||
observableName: 'onPointerDownObservable', | ||
defaultInit: DEFAULT_VECTOR2_WITH_INFO, | ||
}, | ||
pointerUp: { | ||
observableName: 'onPointerUpObservable', | ||
defaultInit: DEFAULT_VECTOR2_WITH_INFO, | ||
}, | ||
pointerClick: { | ||
observableName: 'onPointerClickObservable', | ||
defaultInit: DEFAULT_VECTOR2_WITH_INFO, | ||
}, | ||
wheel: { | ||
observableName: 'onWheelObservable', | ||
defaultInit: Vector2.Zero(), | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './queries'; | ||
export * from './event'; |