Helios-KeyBindings.st 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. Smalltalk current createPackage: 'Helios-KeyBindings'!
  2. Object subclass: #HLBinding
  3. instanceVariableNames: 'key label'
  4. package: 'Helios-KeyBindings'!
  5. !HLBinding methodsFor: 'accessing'!
  6. key
  7. ^ key
  8. !
  9. key: anInteger
  10. key := anInteger
  11. !
  12. label
  13. ^ label
  14. !
  15. label: aString
  16. label := aString
  17. !
  18. shortcut
  19. ^ String fromCharCode: self key
  20. ! !
  21. !HLBinding methodsFor: 'actions'!
  22. applyOn: aKeyBinder
  23. self subclassResponsibility
  24. ! !
  25. !HLBinding methodsFor: 'rendering'!
  26. renderOn: aBindingHelper html: html
  27. ! !
  28. !HLBinding methodsFor: 'testing'!
  29. isBindingAction
  30. ^ false
  31. !
  32. isBindingGroup
  33. ^ false
  34. ! !
  35. !HLBinding class methodsFor: 'instance creation'!
  36. on: anInteger labelled: aString
  37. ^ self new
  38. key: anInteger;
  39. label: aString;
  40. yourself
  41. ! !
  42. HLBinding subclass: #HLBindingAction
  43. instanceVariableNames: 'callback'
  44. package: 'Helios-KeyBindings'!
  45. !HLBindingAction methodsFor: 'accessing'!
  46. callback
  47. ^ callback
  48. !
  49. callback: aBlock
  50. callback := aBlock
  51. ! !
  52. !HLBindingAction methodsFor: 'actions'!
  53. applyOn: aKeyBinder
  54. aKeyBinder applyBindingAction: self
  55. ! !
  56. !HLBindingAction methodsFor: 'testing'!
  57. isBindingAction
  58. ^ true
  59. ! !
  60. HLBinding subclass: #HLBindingGroup
  61. instanceVariableNames: 'bindings'
  62. package: 'Helios-KeyBindings'!
  63. !HLBindingGroup methodsFor: 'accessing'!
  64. add: aBinding
  65. ^ self bindings add: aBinding
  66. !
  67. addActionKey: anInteger labelled: aString callback: aBlock
  68. self add: ((HLBindingAction on: anInteger labelled: aString)
  69. callback: aBlock;
  70. yourself)
  71. !
  72. addActionKey: anInteger labelled: aString command: aCommand
  73. self add: ((HLBindingAction on: anInteger labelled: aString)
  74. command: aCommand;
  75. yourself)
  76. !
  77. addGroupKey: anInteger labelled: aString
  78. self add: (HLBindingGroup on: anInteger labelled: aString)
  79. !
  80. at: aString
  81. ^ self bindings
  82. detect: [ :each | each label = aString ]
  83. ifNone: [ nil ]
  84. !
  85. atKey: anInteger
  86. ^ self bindings
  87. detect: [ :each | each key = anInteger ]
  88. ifNone: [ nil ]
  89. !
  90. bindings
  91. ^ bindings ifNil: [ bindings := OrderedCollection new ]
  92. ! !
  93. !HLBindingGroup methodsFor: 'actions'!
  94. applyOn: aKeyBinder
  95. aKeyBinder applyBindingGroup: self
  96. ! !
  97. !HLBindingGroup methodsFor: 'rendering'!
  98. renderOn: aBindingHelper html: html
  99. aBindingHelper renderBindingGroup: self on: html
  100. ! !
  101. !HLBindingGroup methodsFor: 'testing'!
  102. isBindingGroup
  103. ^ true
  104. ! !
  105. Object subclass: #HLKeyBinder
  106. instanceVariableNames: 'modifierKey active helper bindings selectedBinding'
  107. package: 'Helios-KeyBindings'!
  108. !HLKeyBinder methodsFor: 'accessing'!
  109. activationKey
  110. "SPACE"
  111. ^ 32
  112. !
  113. activationKeyLabel
  114. ^ 'ctrl + space'
  115. !
  116. bindings
  117. ^ bindings ifNil: [ bindings := HLBindingGroup new ]
  118. !
  119. escapeKey
  120. "ESC"
  121. ^ 27
  122. !
  123. helper
  124. ^ helper
  125. !
  126. selectedBinding
  127. ^ selectedBinding ifNil: [ self bindings ]
  128. ! !
  129. !HLKeyBinder methodsFor: 'actions'!
  130. activate
  131. active := true.
  132. self helper show
  133. !
  134. applyBinding: aBinding
  135. aBinding applyOn: self
  136. !
  137. applyBindingAction: aBinding
  138. aBinding callback value.
  139. self deactivate
  140. !
  141. applyBindingGroup: aBinding
  142. selectedBinding := aBinding.
  143. self helper refresh
  144. !
  145. deactivate
  146. active := false.
  147. selectedBinding := nil.
  148. self helper hide
  149. !
  150. flushBindings
  151. bindings := nil
  152. ! !
  153. !HLKeyBinder methodsFor: 'events'!
  154. handleActiveKeyDown: event
  155. "ESC or ctrl+g deactivate the keyBinder"
  156. (event which = self escapeKey or: [
  157. event which = 71 and: [ event ctrlKey ] ])
  158. ifTrue: [
  159. self deactivate.
  160. event preventDefault.
  161. ^ false ].
  162. "Handle the keybinding"
  163. ^ self handleBindingFor: event
  164. !
  165. handleBindingFor: anEvent
  166. | binding |
  167. binding := self selectedBinding atKey: anEvent which.
  168. binding ifNotNil: [
  169. self applyBinding: binding.
  170. anEvent preventDefault.
  171. ^ false ]
  172. !
  173. handleInactiveKeyDown: event
  174. event which = self activationKey ifTrue: [
  175. event ctrlKey ifTrue: [
  176. self activate.
  177. event preventDefault.
  178. ^ false ] ]
  179. !
  180. handleKeyDown: event
  181. ^ self isActive
  182. ifTrue: [ self handleActiveKeyDown: event ]
  183. ifFalse: [ self handleInactiveKeyDown: event ]
  184. !
  185. setupEvents
  186. (window jQuery: 'body') keydown: [ :event | self handleKeyDown: event ]
  187. ! !
  188. !HLKeyBinder methodsFor: 'initialization'!
  189. initialize
  190. super initialize.
  191. helper := HLKeyBinderHelper on: self.
  192. helper renderStart.
  193. active := false
  194. ! !
  195. !HLKeyBinder methodsFor: 'testing'!
  196. isActive
  197. ^ active ifNil: [ false ]
  198. !
  199. systemIsMac
  200. ^ navigator platform match: 'Mac'
  201. ! !
  202. HLWidget subclass: #HLKeyBinderHelper
  203. instanceVariableNames: 'keyBinder'
  204. package: 'Helios-KeyBindings'!
  205. !HLKeyBinderHelper methodsFor: 'accessing'!
  206. cssClass
  207. ^ 'key_helper'
  208. !
  209. keyBinder
  210. ^ keyBinder
  211. !
  212. keyBinder: aKeyBinder
  213. keyBinder := aKeyBinder
  214. !
  215. selectedBinding
  216. ^ self keyBinder selectedBinding
  217. ! !
  218. !HLKeyBinderHelper methodsFor: 'actions'!
  219. hide
  220. ('.', self cssClass) asJQuery remove
  221. !
  222. show
  223. self appendToJQuery: 'body' asJQuery
  224. ! !
  225. !HLKeyBinderHelper methodsFor: 'keyBindings'!
  226. registerBindings
  227. "Do nothing"
  228. ! !
  229. !HLKeyBinderHelper methodsFor: 'rendering'!
  230. renderBindingGroup: aBindingGroup on: html
  231. (aBindingGroup bindings
  232. sorted: [ :a :b | a key < b key ])
  233. do: [ :each |
  234. html span class: 'command'; with: [
  235. html span class: 'label'; with: each shortcut asLowercase.
  236. html a
  237. class: 'action';
  238. with: each label;
  239. onClick: [ self keyBinder applyBinding: each ] ] ]
  240. !
  241. renderBindingOn: html
  242. self selectedBinding renderOn: self html: html
  243. !
  244. renderContentOn: html
  245. html div class: self cssClass; with: [
  246. self
  247. renderSelectionOn:html;
  248. renderBindingOn: html ]
  249. !
  250. renderSelectionOn: html
  251. html span
  252. class: 'selected';
  253. with: (self selectedBinding label ifNil: [ 'Action' ])
  254. !
  255. renderStart
  256. [ :html |
  257. html div
  258. id: 'keybinding-start-helper';
  259. with: 'Press ', self keyBinder activationKeyLabel, ' to start' ] appendToJQuery: 'body' asJQuery.
  260. [ (window jQuery: '#keybinding-start-helper') fadeOut: 1000 ]
  261. valueWithTimeout: 2000
  262. ! !
  263. !HLKeyBinderHelper class methodsFor: 'instance creation'!
  264. on: aKeyBinder
  265. ^ self new
  266. keyBinder: aKeyBinder;
  267. yourself
  268. ! !