Skip to content

Commit

Permalink
Add keyboard service, handle undo/redo as a pilot
Browse files Browse the repository at this point in the history
  • Loading branch information
MusicFreak456 committed Nov 28, 2023
1 parent c43b388 commit 9a6eb84
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/services/history/HistoryService.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { useHistoryStore } from '@/store/history/HistoryStore'
import type IHistoryEvent from './IHistoryEvent'
import AggregateEvent from '@/store/history/event/AggregateEvent'
import KeyboardService from '../keyboard/KeyboardService'

class HistoryService {
private aggregating = false
private aggragateBuffer: Array<IHistoryEvent> = []

constructor() {
const canvasKeyboard = KeyboardService.get('canvas')
canvasKeyboard.registerCallback(['Control', 'z'], () => this.undo())
canvasKeyboard.registerCallback(['Control', 'y'], () => this.redo())
}

public undo() {
const historyStore = useHistoryStore()
const lastEvent = historyStore.popEvent()
Expand Down
58 changes: 58 additions & 0 deletions src/services/keyboard/KeyMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { InvalidArguments } from '@/utils/exceptions/InvalidArguments'

export type KeyCallback = () => void
type KeyTree = Map<string, KeyTree> | KeyCallback

export default class KeyMap {
private keyTreeRoot: KeyTree = new Map<string, KeyTree>()

public get(keySequence: string[]) {
return this.getLeaf(keySequence, this.keyTreeRoot)
}

public set(keySequence: string[], callback: KeyCallback) {
if (keySequence.length < 1) return
this.keyTreeRoot = this.setLeaf(keySequence, this.keyTreeRoot, callback)
}

private getLeaf(keySequence: string[], keyTree: KeyTree): KeyCallback | null {
if (keySequence.length < 1) {
/* Reached bottom of sequence */
if (keyTree instanceof Map) return null
return keyTree
}

if (!(keyTree instanceof Map)) {
/* Reached bottom of the tree */
return null
}

const firstKey = keySequence[0]
if (!keyTree.has(firstKey)) return null
return this.getLeaf(keySequence.slice(1), keyTree.get(firstKey)!)
}

private setLeaf(
keySequence: string[],
keyTree: KeyTree,
leaf: KeyCallback
): KeyTree {
if (keySequence.length < 1) {
/* Reached bottom of the sequence */
return leaf
}

if (!(keyTree instanceof Map)) {
/* Reached bottom of the tree */
throw new InvalidArguments('KeyMap.set - conflicting shortcuts')
}

const firstKey = keySequence[0]
if (keyTree.has(firstKey))
return this.setLeaf(keySequence.slice(1), keyTree.get(firstKey)!, leaf)
return keyTree.set(
firstKey,
this.setLeaf(keySequence.slice(1), new Map(), leaf)
)
}
}
65 changes: 65 additions & 0 deletions src/services/keyboard/KeyboardService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Logger, { type ILogger } from 'js-logger'
import KeyMap, { type KeyCallback } from './KeyMap'

type KeyboardServiceMap = Map<string, KeyboardService>

class KeyboardService {
private logger: ILogger
private keyTree: KeyMap = new KeyMap()

constructor(keyspace: string) {
this.logger = Logger.get(`KeyboardService[${keyspace}]`)
}

public registerCallback(keySequence: string[], callback: KeyCallback) {
this.keyTree.set(keySequence, callback)
}

public handleClick(keySequence: string[]) {
const callback = this.keyTree.get(keySequence)
if (callback) callback()
}
}

class GlobalKeyboardService {
private logger = Logger.get('KeyboardService')
private keyspaces: KeyboardServiceMap = new Map<string, KeyboardService>()
private selectedKeyspace: string | null = null

constructor() {
const clickHandler = (event: KeyboardEvent) => this.handleClick(event)
window.removeEventListener('keydown', clickHandler)
window.addEventListener('keydown', clickHandler)
}

public get(keyspace: string) {
if (this.keyspaces.has(keyspace)) return this.keyspaces.get(keyspace)!

const newKeyboardService = new KeyboardService(keyspace)
this.keyspaces.set(keyspace, newKeyboardService)
this.logger.debug(`New keyspace added: ${keyspace}`)

return newKeyboardService
}

public activateKeyspace(keyspace: string | null) {
const previousKeyspace = this.selectedKeyspace
this.selectedKeyspace = keyspace
this.logger.debug(`Activated keyspace ${keyspace}`)
return previousKeyspace
}

public handleClick(event: KeyboardEvent) {
const keySequence: Array<string> = []
/* For now support only combinations with ctrl */
if (event.ctrlKey && event.key != 'Control') keySequence.push('Control')
keySequence.push(event.key)

if (this.selectedKeyspace) {
const service = this.get(this.selectedKeyspace)
service.handleClick(keySequence)
}
}
}

export default new GlobalKeyboardService()
2 changes: 2 additions & 0 deletions src/views/TheBoard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import TheSidebar from '@/components/whiteboard/sidebar/TheSidebar.vue'
import { useSidebarStore } from '@/store/SidebarStore'
import PageLoader from '@/components/loading/PageLoader.vue'
import TheMagic from '@/components/whiteboard/magic/TheMagic.vue'
import KeyboardService from '@/services/keyboard/KeyboardService'
const logger = Logger.get('MainWhiteboard.vue')
Expand Down Expand Up @@ -47,6 +48,7 @@ watch(
function handleCanvasReady() {
initState.canvasReady = true
logger.debug('Canvas ready indication received!')
KeyboardService.activateKeyspace('canvas')
}
function handleMagicReady() {
Expand Down

0 comments on commit 9a6eb84

Please sign in to comment.