Helios-Core.st 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. Smalltalk current createPackage: 'Helios-Core'!
  2. Object subclass: #HLModel
  3. instanceVariableNames: 'announcer environment'
  4. package: 'Helios-Core'!
  5. !HLModel methodsFor: 'accessing'!
  6. announcer
  7. ^ announcer ifNil: [ announcer := Announcer new ]
  8. !
  9. environment
  10. ^ environment ifNil: [ self manager environment ]
  11. !
  12. environment: anEnvironment
  13. environment := anEnvironment
  14. !
  15. manager
  16. ^ HLManager current
  17. !
  18. systemAnnouncer
  19. ^ self environment systemAnnouncer
  20. ! !
  21. Widget subclass: #HLTab
  22. instanceVariableNames: 'widget label root'
  23. package: 'Helios-Core'!
  24. !HLTab methodsFor: 'accessing'!
  25. activate
  26. self manager activate: self
  27. !
  28. add
  29. self manager addTab: self
  30. !
  31. displayLabel
  32. ^ self label size > 20
  33. ifTrue: [ (self label first: 20), '...' ]
  34. ifFalse: [ self label ]
  35. !
  36. focus
  37. self widget canHaveFocus ifTrue: [
  38. self widget focus ]
  39. !
  40. label
  41. ^ label ifNil: [ '' ]
  42. !
  43. label: aString
  44. label := aString
  45. !
  46. manager
  47. ^ HLManager current
  48. !
  49. widget
  50. ^ widget
  51. !
  52. widget: aWidget
  53. widget := aWidget
  54. ! !
  55. !HLTab methodsFor: 'actions'!
  56. hide
  57. root ifNotNil: [ root asJQuery css: 'visibility' put: 'hidden' ]
  58. !
  59. registerBindings
  60. self widget registerBindings
  61. !
  62. remove
  63. root ifNotNil: [ root asJQuery remove ]
  64. !
  65. show
  66. root
  67. ifNil: [ self appendToJQuery: 'body' asJQuery ]
  68. ifNotNil: [ root asJQuery css: 'visibility' put: 'visible' ]
  69. ! !
  70. !HLTab methodsFor: 'rendering'!
  71. renderOn: html
  72. root := html div
  73. class: 'tab';
  74. yourself.
  75. self renderTab
  76. !
  77. renderTab
  78. root contents: [ :html |
  79. html div
  80. class: 'amber_box';
  81. with: [ self widget renderOn: html ] ]
  82. ! !
  83. !HLTab methodsFor: 'testing'!
  84. isActive
  85. ^ self manager activeTab = self
  86. ! !
  87. !HLTab class methodsFor: 'instance creation'!
  88. on: aWidget labelled: aString
  89. ^ self new
  90. widget: aWidget;
  91. label: aString;
  92. yourself
  93. ! !
  94. Widget subclass: #HLWidget
  95. instanceVariableNames: 'wrapper'
  96. package: 'Helios-Core'!
  97. !HLWidget methodsFor: 'accessing'!
  98. manager
  99. ^ HLManager current
  100. !
  101. wrapper
  102. ^ wrapper
  103. ! !
  104. !HLWidget methodsFor: 'actions'!
  105. alert: aString
  106. window alert: aString
  107. !
  108. confirm: aString ifTrue: aBlock
  109. self manager confirm: aString ifTrue: aBlock
  110. !
  111. execute: aCommand
  112. HLManager current keyBinder
  113. activate;
  114. applyBinding: aCommand asBinding
  115. ! !
  116. !HLWidget methodsFor: 'keybindings'!
  117. registerBindings
  118. self registerBindingsOn: self manager keyBinder bindings
  119. !
  120. registerBindingsOn: aBindingGroup
  121. ! !
  122. !HLWidget methodsFor: 'rendering'!
  123. renderContentOn: html
  124. !
  125. renderOn: html
  126. wrapper := html div.
  127. [ :renderer | self renderContentOn: renderer ] appendToJQuery: wrapper asJQuery
  128. ! !
  129. !HLWidget methodsFor: 'testing'!
  130. canHaveFocus
  131. ^ false
  132. ! !
  133. !HLWidget methodsFor: 'updating'!
  134. refresh
  135. self wrapper ifNil: [ ^ self ].
  136. self wrapper asJQuery empty.
  137. [ :html | self renderContentOn: html ] appendToJQuery: self wrapper asJQuery
  138. ! !
  139. !HLWidget class methodsFor: 'accessing'!
  140. openAsTab
  141. self canBeOpenAsTab ifFalse: [ ^ self ].
  142. HLManager current addTab: (HLTab on: self new labelled: self tabLabel)
  143. !
  144. tabLabel
  145. ^ 'Tab'
  146. !
  147. tabPriority
  148. ^ 500
  149. ! !
  150. !HLWidget class methodsFor: 'testing'!
  151. canBeOpenAsTab
  152. ^ false
  153. ! !
  154. HLWidget subclass: #HLConfirmation
  155. instanceVariableNames: 'confirmationString actionBlock cancelBlock'
  156. package: 'Helios-Core'!
  157. !HLConfirmation methodsFor: 'accessing'!
  158. actionBlock
  159. ^ actionBlock ifNil: [ [] ]
  160. !
  161. actionBlock: aBlock
  162. actionBlock := aBlock
  163. !
  164. cancelBlock
  165. ^ cancelBlock ifNil: [ [] ]
  166. !
  167. cancelBlock: aBlock
  168. cancelBlock := aBlock
  169. !
  170. confirmationString
  171. ^ confirmationString ifNil: [ 'Confirm' ]
  172. !
  173. confirmationString: aString
  174. confirmationString := aString
  175. ! !
  176. !HLConfirmation methodsFor: 'actions'!
  177. cancel
  178. self cancelBlock value.
  179. self remove
  180. !
  181. confirm
  182. self actionBlock value.
  183. self remove
  184. !
  185. remove
  186. (window jQuery: '.confirmation') removeClass: 'active'.
  187. [
  188. (window jQuery: '#overlay') remove.
  189. (window jQuery: '.confirmation') remove
  190. ] valueWithTimeout: 300
  191. ! !
  192. !HLConfirmation methodsFor: 'rendering'!
  193. renderContentOn: html
  194. | confirmButton |
  195. html div id: 'overlay'.
  196. html div
  197. class: 'confirmation';
  198. with: [
  199. html span with: self confirmationString.
  200. html div
  201. class: 'buttons';
  202. with: [
  203. html button
  204. class: 'button';
  205. with: 'Cancel';
  206. onClick: [ self cancel ].
  207. confirmButton := html button
  208. class: 'button default';
  209. with: 'Confirm';
  210. onClick: [ self confirm ] ] ].
  211. confirmButton asJQuery focus.
  212. (window jQuery: '.confirmation') addClass: 'active'.
  213. self setupKeyBindings
  214. !
  215. setupKeyBindings
  216. (window jQuery: '.confirmation') keyup: [ :e |
  217. e keyCode = 27 ifTrue: [ self cancel ] ]
  218. ! !
  219. HLWidget subclass: #HLDebugger
  220. instanceVariableNames: ''
  221. package: 'Helios-Core'!
  222. HLWidget subclass: #HLFocusableWidget
  223. instanceVariableNames: ''
  224. package: 'Helios-Core'!
  225. !HLFocusableWidget methodsFor: 'accessing'!
  226. focusClass
  227. ^ 'focused'
  228. ! !
  229. !HLFocusableWidget methodsFor: 'events'!
  230. blur
  231. self wrapper asJQuery blur
  232. !
  233. focus
  234. self wrapper asJQuery focus
  235. !
  236. hasFocus
  237. ^ self wrapper notNil and: [ self wrapper asJQuery is: ':focus' ]
  238. ! !
  239. !HLFocusableWidget methodsFor: 'rendering'!
  240. renderContentOn: html
  241. !
  242. renderOn: html
  243. self registerBindings.
  244. wrapper := html div
  245. class: 'hl_widget';
  246. yourself.
  247. wrapper with: [ self renderContentOn: html ].
  248. wrapper
  249. at: 'tabindex' put: '0';
  250. onBlur: [ self wrapper asJQuery removeClass: self focusClass ];
  251. onFocus: [ self wrapper asJQuery addClass: self focusClass ]
  252. ! !
  253. !HLFocusableWidget methodsFor: 'testing'!
  254. canHaveFocus
  255. ^ true
  256. ! !
  257. HLFocusableWidget subclass: #HLListWidget
  258. instanceVariableNames: 'items selectedItem mapping'
  259. package: 'Helios-Core'!
  260. !HLListWidget methodsFor: 'accessing'!
  261. cssClassForItem: anObject
  262. ^ self selectedItem = anObject
  263. ifTrue: [ 'active' ]
  264. ifFalse: [ 'inactive' ]
  265. !
  266. iconForItem: anObject
  267. ^ ''
  268. !
  269. items
  270. ^ items ifNil: [ items := self defaultItems ]
  271. !
  272. items: aCollection
  273. items := aCollection
  274. !
  275. positionOf: aListItem
  276. <
  277. return aListItem.parent().children().get().indexOf(aListItem.get(0)) + 1
  278. >
  279. !
  280. selectedItem
  281. ^ selectedItem
  282. !
  283. selectedItem: anObject
  284. selectedItem := anObject
  285. ! !
  286. !HLListWidget methodsFor: 'actions'!
  287. activateFirstListItem
  288. self activateListItem: (window jQuery: ((wrapper asJQuery find: 'li.inactive') get: 0))
  289. !
  290. activateItem: anObject
  291. self activateListItem: (mapping
  292. at: anObject
  293. ifAbsent: [ ^ self ]) asJQuery
  294. !
  295. activateListItem: aListItem
  296. | item |
  297. (aListItem get: 0) ifNil: [ ^self ].
  298. aListItem parent children removeClass: 'active'.
  299. aListItem addClass: 'active'.
  300. self ensureVisible: aListItem.
  301. "Activate the corresponding item"
  302. item := (self items at: (aListItem attr: 'list-data') asNumber).
  303. self selectedItem == item ifFalse: [
  304. self selectItem: item ]
  305. !
  306. activateNextListItem
  307. self activateListItem: (window jQuery: '.focused .nav-pills .active') next.
  308. "select the first item if none is selected"
  309. (window jQuery: '.focused .nav-pills .active') get ifEmpty: [
  310. self activateFirstListItem ]
  311. !
  312. activatePreviousListItem
  313. self activateListItem: (window jQuery: '.focused .nav-pills .active') prev
  314. !
  315. ensureVisible: aListItem
  316. "Move the scrollbar to show the active element"
  317. | perent position |
  318. position := self positionOf: aListItem.
  319. parent := aListItem parent.
  320. aListItem position top < 0 ifTrue: [
  321. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem position top - 10) ].
  322. aListItem position top + aListItem height > parent height ifTrue: [
  323. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem height - (parent height - aListItem position top)) +10 ]
  324. !
  325. focus
  326. super focus.
  327. self items isEmpty ifFalse: [
  328. self selectedItem ifNil: [ self activateFirstListItem ] ]
  329. !
  330. refresh
  331. super refresh.
  332. self ensureVisible: (mapping
  333. at: self selectedItem
  334. ifAbsent: [ ^ self ]) asJQuery
  335. !
  336. selectItem: anObject
  337. self selectedItem: anObject
  338. ! !
  339. !HLListWidget methodsFor: 'defaults'!
  340. defaultItems
  341. ^ #()
  342. ! !
  343. !HLListWidget methodsFor: 'events'!
  344. setupKeyBindings
  345. "TODO: refactor this!!"
  346. | active interval delay repeatInterval |
  347. active := false.
  348. repeatInterval := 70.
  349. self wrapper asJQuery unbind: 'keydown'.
  350. self wrapper asJQuery keydown: [ :e |
  351. (e which = 38 and: [ active = false ]) ifTrue: [
  352. active := true.
  353. self activatePreviousListItem.
  354. delay := [
  355. interval := [ self activatePreviousListItem ]
  356. valueWithInterval: repeatInterval ]
  357. valueWithTimeout: 300 ].
  358. (e which = 40 and: [ active = false ]) ifTrue: [
  359. active := true.
  360. self activateNextListItem.
  361. delay := [
  362. interval := [ self activateNextListItem ]
  363. valueWithInterval: repeatInterval ]
  364. valueWithTimeout: 300 ] ].
  365. self wrapper asJQuery keyup: [ :e |
  366. active ifTrue: [
  367. active := false.
  368. interval ifNotNil: [ interval clearInterval ].
  369. delay ifNotNil: [ delay clearTimeout] ] ]
  370. ! !
  371. !HLListWidget methodsFor: 'initialization'!
  372. initialize
  373. super initialize.
  374. mapping := Dictionary new.
  375. ! !
  376. !HLListWidget methodsFor: 'private'!
  377. registerMappingFrom: anObject to: aTag
  378. mapping at: anObject put: aTag
  379. ! !
  380. !HLListWidget methodsFor: 'rendering'!
  381. renderButtonsOn: html
  382. !
  383. renderContentOn: html
  384. html ul
  385. class: 'nav nav-pills nav-stacked';
  386. with: [ self renderListOn: html ].
  387. html div class: 'pane_actions form-actions'; with: [
  388. self renderButtonsOn: html ].
  389. self setupKeyBindings
  390. !
  391. renderItem: anObject on: html
  392. | li |
  393. li := html li.
  394. self registerMappingFrom: anObject to: li.
  395. li
  396. class: (self cssClassForItem: anObject);
  397. at: 'list-data' put: (self items indexOf: anObject) asString;
  398. with: [
  399. html a
  400. with: [
  401. (html tag: 'i') class: (self iconForItem: anObject).
  402. self renderItemLabel: anObject on: html ];
  403. onClick: [
  404. self activateListItem: li asJQuery ] ]
  405. !
  406. renderItemLabel: anObject on: html
  407. html with: anObject asString
  408. !
  409. renderListOn: html
  410. mapping := Dictionary new.
  411. self items do: [ :each |
  412. self renderItem: each on: html ]
  413. ! !
  414. HLListWidget subclass: #HLNavigationListWidget
  415. instanceVariableNames: 'previous next'
  416. package: 'Helios-Core'!
  417. !HLNavigationListWidget methodsFor: 'accessing'!
  418. next
  419. ^ next
  420. !
  421. next: aWidget
  422. next := aWidget.
  423. aWidget previous = self ifFalse: [ aWidget previous: self ]
  424. !
  425. previous
  426. ^ previous
  427. !
  428. previous: aWidget
  429. previous := aWidget.
  430. aWidget next = self ifFalse: [ aWidget next: self ]
  431. ! !
  432. !HLNavigationListWidget methodsFor: 'actions'!
  433. nextFocus
  434. self next ifNotNil: [ self next focus ]
  435. !
  436. previousFocus
  437. self previous ifNotNil: [ self previous focus ]
  438. ! !
  439. !HLNavigationListWidget methodsFor: 'events'!
  440. setupKeyBindings
  441. super setupKeyBindings.
  442. self wrapper asJQuery keydown: [ :e |
  443. e which = 39 ifTrue: [
  444. self nextFocus ].
  445. e which = 37 ifTrue: [
  446. self previousFocus ] ]
  447. ! !
  448. HLWidget subclass: #HLManager
  449. instanceVariableNames: 'tabs activeTab keyBinder environment history'
  450. package: 'Helios-Core'!
  451. !HLManager methodsFor: 'accessing'!
  452. activeTab
  453. ^ activeTab
  454. !
  455. environment
  456. "The default environment used by all Helios objects"
  457. ^ environment ifNil: [ environment := self defaultEnvironment ]
  458. !
  459. environment: anEnvironment
  460. environment := anEnvironment
  461. !
  462. history
  463. ^ history ifNil: [ history := OrderedCollection new ]
  464. !
  465. history: aCollection
  466. history := aCollection
  467. !
  468. keyBinder
  469. ^ keyBinder ifNil: [ keyBinder := HLKeyBinder new ]
  470. !
  471. tabs
  472. ^ tabs ifNil: [ tabs := OrderedCollection new ]
  473. ! !
  474. !HLManager methodsFor: 'actions'!
  475. activate: aTab
  476. self keyBinder flushBindings.
  477. aTab registerBindings.
  478. activeTab := aTab.
  479. self
  480. refresh;
  481. addToHistory: aTab;
  482. show: aTab
  483. !
  484. addTab: aTab
  485. self tabs add: aTab.
  486. self activate: aTab
  487. !
  488. addToHistory: aTab
  489. self removeFromHistory: aTab.
  490. self history add: aTab
  491. !
  492. confirm: aString ifFalse: aBlock
  493. (HLConfirmation new
  494. confirmationString: aString;
  495. cancelBlock: aBlock;
  496. yourself)
  497. appendToJQuery: 'body' asJQuery
  498. !
  499. confirm: aString ifTrue: aBlock
  500. (HLConfirmation new
  501. confirmationString: aString;
  502. actionBlock: aBlock;
  503. yourself)
  504. appendToJQuery: 'body' asJQuery
  505. !
  506. removeActiveTab
  507. self removeTab: self activeTab
  508. !
  509. removeFromHistory: aTab
  510. self history: (self history reject: [ :each | each == aTab ])
  511. !
  512. removeTab: aTab
  513. (self tabs includes: aTab) ifFalse: [ ^ self ].
  514. self removeFromHistory: aTab.
  515. self tabs remove: aTab.
  516. self keyBinder flushBindings.
  517. aTab remove.
  518. self refresh.
  519. self history ifNotEmpty: [
  520. self history last activate ]
  521. ! !
  522. !HLManager methodsFor: 'defaults'!
  523. defaultEnvironment
  524. "If helios is loaded from within a frame, answer the parent window environment"
  525. window parent ifNil: [ ^ Environment new ].
  526. ^ ((window parent at: 'smalltalk')
  527. at: 'Environment') new
  528. ! !
  529. !HLManager methodsFor: 'initialization'!
  530. initialize
  531. super initialize.
  532. self keyBinder setupEvents
  533. ! !
  534. !HLManager methodsFor: 'rendering'!
  535. refresh
  536. (window jQuery: '.navbar') remove.
  537. self appendToJQuery: 'body' asJQuery
  538. !
  539. renderAddOn: html
  540. html li
  541. class: 'dropdown';
  542. with: [
  543. html a
  544. class: 'dropdown-toggle';
  545. at: 'data-toggle' put: 'dropdown';
  546. with: [
  547. html with: 'Open...'.
  548. (html tag: 'b') class: 'caret' ].
  549. html ul
  550. class: 'dropdown-menu';
  551. with: [
  552. ((HLWidget withAllSubclasses
  553. select: [ :each | each canBeOpenAsTab ])
  554. sorted: [ :a :b | a tabPriority < b tabPriority ])
  555. do: [ :each |
  556. html li with: [
  557. html a
  558. with: each tabLabel;
  559. onClick: [ each openAsTab ] ] ] ] ]
  560. !
  561. renderContentOn: html
  562. html div
  563. class: 'navbar navbar-fixed-top';
  564. with: [ html div
  565. class: 'navbar-inner';
  566. with: [ self renderTabsOn: html ] ]
  567. !
  568. renderTabsOn: html
  569. html ul
  570. class: 'nav';
  571. with: [
  572. self tabs do: [ :each |
  573. html li
  574. class: (each isActive ifTrue: [ 'active' ] ifFalse: [ 'inactive' ]);
  575. with: [
  576. html a
  577. with: [
  578. ((html tag: 'i') class: 'icon-remove')
  579. onClick: [ self removeTab: each ].
  580. html with: each displayLabel ];
  581. onClick: [ each activate ] ] ].
  582. self renderAddOn: html ]
  583. !
  584. show: aTab
  585. self tabs do: [ :each | each hide ].
  586. aTab show; focus
  587. ! !
  588. HLManager class instanceVariableNames: 'current'!
  589. !HLManager class methodsFor: 'accessing'!
  590. current
  591. ^ current ifNil: [ current := self basicNew initialize ]
  592. ! !
  593. !HLManager class methodsFor: 'initialization'!
  594. initialize
  595. self current appendToJQuery: 'body' asJQuery
  596. ! !
  597. !HLManager class methodsFor: 'instance creation'!
  598. new
  599. "Use current instead"
  600. self shouldNotImplement
  601. ! !
  602. HLWidget subclass: #HLSUnit
  603. instanceVariableNames: ''
  604. package: 'Helios-Core'!
  605. !HLSUnit class methodsFor: 'accessing'!
  606. tabLabel
  607. ^ 'SUnit'
  608. !
  609. tabPriority
  610. ^ 1000
  611. ! !
  612. !HLSUnit class methodsFor: 'testing'!
  613. canBeOpenAsTab
  614. ^ true
  615. ! !
  616. HLWidget subclass: #HLTranscript
  617. instanceVariableNames: ''
  618. package: 'Helios-Core'!
  619. !HLTranscript class methodsFor: 'accessing'!
  620. tabLabel
  621. ^ 'Transcript'
  622. !
  623. tabPriority
  624. ^ 600
  625. ! !
  626. !HLTranscript class methodsFor: 'testing'!
  627. canBeOpenAsTab
  628. ^ true
  629. ! !