@@ -18,33 +18,138 @@ function compositeKeyFromKeyEvent (event) {
1818 const compositeKey = `${ ctrl } ${ shift } ${ meta } ${ key } `
1919 return compositeKey
2020}
21+ const simpleKey = ( event ) => {
22+ return event . key ? event . key . toLowerCase ( ) : undefined
23+ }
2124
22- const makeActions = ( sources ) => {
23- /* sources.watcher.forEach(function (data) {
24- console.log('watchedFile', data)
25- })
26- sources.drops.forEach(function (data) {
27- console.log('drop', data)
28- } )
29- sources.fs.forEach(function (data ) {
30- console.log('fs operations', data)
31- })
32- sources.paramChanges.forEach(function (data) {
33- console.log('param changes', data )
34- }) */
25+ const getKeyCombos = ( options , keyUps$ , keyDown$ ) => {
26+ const defaults = {
27+ dropRepeats : false
28+ }
29+ const { dropRepeats } = Object . assign ( { } , defaults , options )
30+
31+ keyDown$ = keyDown$ . multicast ( ) . debounce ( 10 )
32+ if ( dropRepeats ) {
33+ keyDown$ = keyDown$
34+ . skipRepeatsWith ( ( event , previousEvent ) => {
35+ return simpleKey ( event ) === simpleKey ( previousEvent )
36+ } )
37+ }
3538
39+ const keyStuffEnd$ = keyDown$ . throttle ( 1000 ) . delay ( 2000 )
40+ const keyCombos$ = keyDown$
41+ . merge ( keyUps$ . map ( x => 'end' ) )
42+ . merge ( keyStuffEnd$ . map ( x => 'end' ) )
43+ . loop ( ( values , event ) => {
44+ if ( event === 'end' || simpleKey ( event ) === 'enter' || simpleKey ( event ) === 'escape' ) {
45+ const value = {
46+ event : values . length > 0 ? values [ 0 ] . event : undefined ,
47+ compositeKey : values . map ( x => x . compositeKey ) . join ( '+' )
48+ }
49+ return { seed : [ ] , value}
50+ } else {
51+ const compositeKey = simpleKey ( event )
52+ values . push ( { event, compositeKey} )
53+ }
54+ return { seed : values }
55+ } , [ ] )
56+ . filter ( x => x !== undefined )
57+ . filter ( x => x . event !== undefined )
58+ // .tap(x => console.log('key stuff', x))
59+ . multicast ( )
60+
61+ return keyCombos$
62+ }
63+
64+ const makeActions = ( sources ) => {
3665 // keyboard shortcut handling
37- const keyDowns$ = most . fromEvent ( 'keyup' , document )
38- const actionsFromKey$ = most . sample ( function ( event , state ) {
39- const compositeKey = compositeKeyFromKeyEvent ( event )
66+ const keyUps$ = most . fromEvent ( 'keyup' , document ) . multicast ( )
67+ const keyDown$ = most . fromEvent ( 'keydown' , document ) . multicast ( )
68+ // we get all key combos, accepting repeated key strokes
69+ const keyCombos$ = getKeyCombos ( { dropRepeats : false } , keyUps$ , keyDown$ )
70+
71+ // we match key stroke combos to actions
72+ const actionsFromKey$ = most . sample ( function ( { event, compositeKey} , state ) {
4073 const matchingAction = head ( state . shortcuts . filter ( shortcut => shortcut . key === compositeKey ) )
4174 if ( matchingAction ) {
4275 const { command, args} = matchingAction
4376 return { type : command , data : args }
4477 }
4578 return undefined
46- } , keyDowns$ , keyDowns$ , sources . state$ )
79+ } , keyCombos$ , keyCombos$ , sources . state$ )
80+ . filter ( x => x !== undefined )
81+
82+ // set shortcuts
83+ const setShortcuts$ = most . mergeArray ( [
84+ sources . store
85+ . filter ( data => data && data . shortcuts )
86+ . map ( data => data . shortcuts )
87+ ] )
88+ . map ( data => ( { type : 'setShortcuts' , data} ) )
89+
90+ // set a specific shortcut
91+ const shortcutCommandUp$ = sources . dom . select ( '.shortcutCommand' ) . events ( 'keyup' ) . multicast ( )
92+ const shortcutCommandDown$ = sources . dom . select ( '.shortcutCommand' ) . events ( 'keydown' ) . multicast ( )
93+ const shortcutCommandKey$ = getKeyCombos (
94+ { dropRepeats : true } ,
95+ shortcutCommandUp$ ,
96+ shortcutCommandDown$
97+ )
98+ shortcutCommandUp$
99+ . forEach ( ( event ) => {
100+ event . preventDefault ( )
101+ event . stopPropagation ( )
102+ return false
103+ } )
104+ shortcutCommandDown$
105+ . forEach ( ( event ) => {
106+ event . preventDefault ( )
107+ event . stopPropagation ( )
108+ return false
109+ } )
110+
111+ const setShortcut$ = most . mergeArray ( [
112+ shortcutCommandKey$
113+ . map ( ( { event, compositeKey} ) => ( { event, compositeKey, inProgress : true } ) )
114+
115+ . merge (
116+ sources . dom . select ( '.shortcutCommand' ) . events ( 'focus' )
117+ . map ( event => ( { event, compositeKey : '' , inProgress : true } ) )
118+ )
119+ . merge (
120+ sources . dom . select ( '.shortcutCommand' ) . events ( 'blur' )
121+ . map ( event => ( { event, compositeKey : '' , inProgress : false } ) )
122+ )
123+ . merge (
124+ shortcutCommandUp$
125+ . filter ( event => simpleKey ( event ) === 'escape' )
126+ . map ( event => ( { event, compositeKey : '' , inProgress : false } ) )
127+ )
128+ . merge (
129+ shortcutCommandUp$
130+ . filter ( event => simpleKey ( event ) === 'enter' )
131+ . map ( event => ( { event, done : true } ) )
132+ )
133+ . scan ( function ( acc , current ) {
134+ const { event, compositeKey, inProgress, done} = current
135+ const command = event . target . dataset . command
136+ const args = event . target . dataset . args
137+
138+ let updated = Object . assign ( { } , acc , { command, args, inProgress, done} )
139+ if ( compositeKey !== undefined && compositeKey !== '' ) {
140+ updated . key = compositeKey
141+ updated . tmpKey = compositeKey
142+ }
143+ if ( 'done' in updated && updated . done ) {
144+ delete updated . inProgress
145+ delete updated . tmpKey
146+ }
147+ return updated
148+ } , { } )
47149 . filter ( x => x !== undefined )
150+ ] )
151+ . map ( data => ( { type : 'setShortcut' , data} ) )
152+ . multicast ( )
48153
49154 const changeTheme$ = most . mergeArray ( [
50155 sources . dom . select ( '#themeSwitcher' ) . events ( 'change' )
@@ -105,6 +210,9 @@ const makeActions = (sources) => {
105210 return {
106211 // generic key shortuct handler
107212 actionsFromKey$,
213+ // set shortcut(s)
214+ setShortcuts$,
215+ setShortcut$,
108216 // generic clear error action
109217 clearErrors$,
110218 setErrors$,
0 commit comments