Helios-KeyBindings.st 7.6 KB

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