Skip to content

Commit

Permalink
Add crown event handler (#23)
Browse files Browse the repository at this point in the history
Closes #10

Adds crown scroll event handler, on web matched to mouse wheel
  • Loading branch information
j-piasecki authored Sep 11, 2022
1 parent 747a0f8 commit 35abc74
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 3 deletions.
1 change: 1 addition & 0 deletions @zapp/core/src/NodeType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export enum NodeType {

Remember = 'remember',
Effect = 'effect',
Event = 'event',
}
2 changes: 2 additions & 0 deletions @zapp/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {
} from './Application.js'
export { remember } from './working_tree/effects/remember.js'
export { rememberLauncherForResult } from './working_tree/effects/rememberLauncherForResult.js'
export { registerCrownEventHandler } from './working_tree/effects/registerCrownEventHandler.js'
export { RememberedMutableValue } from './working_tree/effects/RememberedMutableValue.js'
export { sideEffect } from './working_tree/effects/sideEffect.js'
export { Arc } from './working_tree/views/Arc.js'
Expand Down Expand Up @@ -41,6 +42,7 @@ export { withTiming } from './working_tree/effects/animation/TimingAnimation.js'
export { Renderer, setViewManager as __setViewManager, RenderNode } from './renderer/Renderer.js'
export { ViewManager } from './renderer/ViewManager.js'
export { PointerEventManager } from './renderer/PointerEventManager.js'
export { GlobalEventManager } from './renderer/GlobalEventManager.js'
export { NodeType } from './NodeType.js'
export { ZappInterface, Zapp, setZappInterface as __setZappInterface } from './ZappInterface.js'
export { NavigatorInterface, Navigator, RegisteredCallback, setNavigator as __setNavigator } from './Navigator.js'
Expand Down
30 changes: 30 additions & 0 deletions @zapp/core/src/renderer/GlobalEventManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { EventType } from '../working_tree/EventNode.js'

export interface EventHandler {
type: EventType
handler: (...args: any[]) => boolean
}

export abstract class GlobalEventManager {
private static handlers: EventHandler[] = []

public static clearHandlers() {
GlobalEventManager.handlers = []
}

public static registerHandler(handler: EventHandler) {
GlobalEventManager.handlers.push(handler)
}

public static dispatchCrownEvent(delta: number): boolean {
// iterate upwards so the deeper nodes receive the event earlier
for (let i = GlobalEventManager.handlers.length - 1; i >= 0; i--) {
const handler = GlobalEventManager.handlers[i]
if (handler.type === EventType.Crown && handler.handler(delta)) {
return true
}
}

return false
}
}
8 changes: 8 additions & 0 deletions @zapp/core/src/renderer/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { CustomViewProps } from '../working_tree/views/Custom.js'
import { PointerEventManager } from './PointerEventManager.js'
import { LayoutManager } from './LayoutManager.js'
import { ViewManager } from './ViewManager.js'
import { EventNode } from '../working_tree/EventNode.js'
import { GlobalEventManager } from './GlobalEventManager.js'

interface Layout {
width: number
Expand Down Expand Up @@ -67,6 +69,7 @@ export abstract class Renderer {
}

public static commit(root: ViewNode) {
GlobalEventManager.clearHandlers()
Renderer.newTree = Renderer.createNode(root)
}

Expand Down Expand Up @@ -269,6 +272,11 @@ export abstract class Renderer {
for (const child of node.children) {
if (child instanceof ViewNode) {
result.children.push(Renderer.createNode(child, result))
} else if (child instanceof EventNode) {
GlobalEventManager.registerHandler({
type: child.eventType,
handler: child.handler,
})
}
}

Expand Down
12 changes: 12 additions & 0 deletions @zapp/core/src/working_tree/EventNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { WorkingNode } from './WorkingNode.js'

export enum EventType {
Button,
Crown,
Gesture,
}

export class EventNode extends WorkingNode {
public handler: (...args: any[]) => boolean
public eventType: EventType
}
16 changes: 16 additions & 0 deletions @zapp/core/src/working_tree/ViewNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { WorkingNode, WorkingNodeProps } from './WorkingNode.js'
import { WorkingTree } from './WorkingTree.js'
import { findRelativePath } from '../utils.js'
import { CustomViewProps } from './views/Custom.js'
import { EventNode } from './EventNode.js'

export interface ViewNodeProps extends WorkingNodeProps {
body?: () => void
Expand Down Expand Up @@ -79,6 +80,21 @@ export class ViewNode extends WorkingNode {
return result
}

public event() {
// events may only be created inside view node
const currentView = WorkingTree.current as ViewNode

const result = new EventNode({
id: (currentView.nextActionId++).toString(),
type: NodeType.Event,
})

result.parent = currentView.override ?? WorkingTree.current
result.path = this.path.concat(this.id)

return result
}

public override reset(): void {
this.rememberedContext = undefined
this.override = undefined
Expand Down
13 changes: 13 additions & 0 deletions @zapp/core/src/working_tree/effects/registerCrownEventHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { EventType } from '../EventNode.js'
import { ViewNode } from '../ViewNode.js'
import { WorkingTree } from '../WorkingTree.js'

export function registerCrownEventHandler(handler: (delta: number) => boolean) {
const current = WorkingTree.current as ViewNode
const context = current.event()

context.handler = handler
context.eventType = EventType.Crown

current.children.push(context)
}
15 changes: 14 additions & 1 deletion @zapp/watch/src/SimpleScreen.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { ScreenBody, ConfigBuilderArg, Zapp, PointerEventManager, WorkingTree, Navigator } from '@zapp/core'
import {
ScreenBody,
ConfigBuilderArg,
Zapp,
PointerEventManager,
WorkingTree,
Navigator,
GlobalEventManager,
} from '@zapp/core'

export function SimpleScreen(configBuilder: ConfigBuilderArg, body?: (params?: Record<string, unknown>) => void) {
Page({
Expand Down Expand Up @@ -26,6 +34,10 @@ export function SimpleScreen(configBuilder: ConfigBuilderArg, body?: (params?: R

return false
})

hmApp.registerSpinEvent((_key: unknown, degree: number) => {
return GlobalEventManager.dispatchCrownEvent(degree)
})
},
build() {
ScreenBody(configBuilder, () => {
Expand All @@ -35,6 +47,7 @@ export function SimpleScreen(configBuilder: ConfigBuilderArg, body?: (params?: R
onDestroy() {
Zapp.stopLoop()
hmApp.unregisterGestureEvent()
hmApp.unregistSpinEvent()
},
})
}
19 changes: 18 additions & 1 deletion @zapp/web/src/ZappWeb.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import { PointerEventManager, Renderer, WorkingTree, ZappInterface, Animation } from '@zapp/core'
import { PointerEventManager, Renderer, WorkingTree, ZappInterface, Animation, GlobalEventManager } from '@zapp/core'

export class ZappWeb extends ZappInterface {
private running = false
private crownDelta = 0
private previousCrownDelta = 0
private crownResetTimeout = -1

public startLoop() {
this.running = true
WorkingTree.requestUpdate()
requestAnimationFrame(this.update)

window.addEventListener('wheel', (e) => {
this.crownDelta += e.deltaY / 10

clearTimeout(this.crownResetTimeout)
setTimeout(() => {
this.crownDelta = 0
}, 100)
})
}

stopLoop(): void {
this.running = false
}

private update = () => {
if (this.crownDelta !== this.previousCrownDelta) {
this.previousCrownDelta = this.crownDelta
GlobalEventManager.dispatchCrownEvent(this.crownDelta)
}

PointerEventManager.processEvents()
Animation.nextFrame(Date.now())

Expand Down
11 changes: 10 additions & 1 deletion watch-test/page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ColumnConfig,
ArcConfig,
Navigator,
registerCrownEventHandler,
} from '@zapp/core'

let cycle = [
Expand Down Expand Up @@ -68,8 +69,16 @@ SimpleScreen(Config('screen'), () => {
textVisible.value = !textVisible.value
}),
() => {
const deltaV = remember(0)
const counter = remember(0)
registerCrownEventHandler((delta) => {
deltaV.value = delta
counter.value = counter.value + 1
return false
})

if (textVisible.value) {
Text(TextConfig('text').textColor(0xffffff).textSize(24), 'Random text')
Text(TextConfig('text').textColor(0xffffff).textSize(24), '' + deltaV.value + ', ' + counter.value)
} else {
ActivityIndicator(ArcConfig('ac').width(60).height(60).color(0xffffff).lineWidth(10))
}
Expand Down
14 changes: 14 additions & 0 deletions watch-test/page/page3.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ArcConfig,
Navigator,
rememberLauncherForResult,
registerCrownEventHandler,
} from '@zapp/core'

SimpleScreen(Config('screen'), (params) => {
Expand All @@ -37,6 +38,19 @@ SimpleScreen(Config('screen'), (params) => {
launcher.launch()
}),
() => {
const height = remember(10)
const targetHeight = remember(10)

registerCrownEventHandler((delta) => {
targetHeight.value = Math.max(10, targetHeight.value + delta * -1)
height.value = withTiming(targetHeight.value, { easing: Easing.easeOutCubic })
return true
})

Column(ColumnConfig('column2').alignment(Alignment.Center).arrangement(Arrangement.Center), () => {
Stack(StackConfig('bar').width(50).height(height.value).background(0xff0000))
})

Column(ColumnConfig('column'), () => {
Text(TextConfig('text').textColor(0xffffff).textSize(40), `3, ${params.data}`)
Text(TextConfig('text2').textColor(0xffffff).textSize(40), `Selected: ${selectedNumber.value}`)
Expand Down
20 changes: 20 additions & 0 deletions web-test/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Zapp,
rememberLauncherForResult,
Navigator,
registerCrownEventHandler,
} from '@zapp/core'
import { NavBar, RouteInfo } from './NavBar'
import { Page } from './Page'
Expand All @@ -36,6 +37,7 @@ const routesInfo: RouteInfo[] = [
{ displayName: 'Row example', routeName: 'row' },
{ displayName: 'Animation example', routeName: 'animation' },
{ displayName: 'StartForResult example', routeName: 'startForResult' },
{ displayName: 'Crown events example', routeName: 'crownEvent' },
]

function StackExample() {
Expand Down Expand Up @@ -478,6 +480,23 @@ function NumberPickerExample() {
})
}

function CrownEventExample() {
Page(routesInfo, () => {
const height = remember(10)
const targetHeight = remember(10)

registerCrownEventHandler((delta: number) => {
targetHeight.value = Math.max(10, targetHeight.value + delta * -1)
height.value = withTiming(targetHeight.value, { easing: Easing.easeOutCubic })
return true
})

Column(ColumnConfig('column').alignment(Alignment.Center).arrangement(Arrangement.Center), () => {
Stack(StackConfig('bar').width(50).height(height.value).background(0xff0000))
})
})
}

registerNavigationRoutes('dynamicLayout', {
dynamicLayout: DynamicLayoutExample,
stack: StackExample,
Expand All @@ -486,4 +505,5 @@ registerNavigationRoutes('dynamicLayout', {
animation: AnimationExample,
startForResult: StartForResultExample,
picker: NumberPickerExample,
crownEvent: CrownEventExample,
})

0 comments on commit 35abc74

Please sign in to comment.