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

Add crown event handler #23

Merged
merged 1 commit into from
Sep 11, 2022
Merged
Show file tree
Hide file tree
Changes from all 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 @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,
})