Helios-Core.st 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  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. !
  309. activatePreviousListItem
  310. self activateListItem: (window jQuery: '.focused .nav-pills .active') prev
  311. !
  312. ensureVisible: aListItem
  313. "Move the scrollbar to show the active element"
  314. | perent position |
  315. position := self positionOf: aListItem.
  316. parent := aListItem parent.
  317. aListItem position top < 0 ifTrue: [
  318. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem position top - 10) ].
  319. aListItem position top + aListItem height > parent height ifTrue: [
  320. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem height - (parent height - aListItem position top)) +10 ]
  321. !
  322. focus
  323. super focus.
  324. self items isEmpty ifFalse: [
  325. self selectedItem ifNil: [ self activateFirstListItem ] ]
  326. !
  327. refresh
  328. super refresh.
  329. self ensureVisible: (mapping
  330. at: self selectedItem
  331. ifAbsent: [ ^ self ]) asJQuery
  332. !
  333. selectItem: anObject
  334. self selectedItem: anObject
  335. ! !
  336. !HLListWidget methodsFor: 'defaults'!
  337. defaultItems
  338. ^ #()
  339. ! !
  340. !HLListWidget methodsFor: 'events'!
  341. setupKeyBindings
  342. "TODO: refactor this!!"
  343. | active interval delay repeatInterval |
  344. active := false.
  345. repeatInterval := 70.
  346. self wrapper asJQuery unbind: 'keydown'.
  347. self wrapper asJQuery keydown: [ :e |
  348. (e which = 38 and: [ active = false ]) ifTrue: [
  349. active := true.
  350. self activatePreviousListItem.
  351. delay := [
  352. interval := [
  353. (self wrapper asJQuery hasClass: self focusClass)
  354. ifTrue: [
  355. self activatePreviousListItem ]
  356. ifFalse: [
  357. active := false.
  358. interval ifNotNil: [ interval clearInterval ].
  359. delay ifNotNil: [ delay clearTimeout] ] ]
  360. valueWithInterval: repeatInterval ]
  361. valueWithTimeout: 300 ].
  362. (e which = 40 and: [ active = false ]) ifTrue: [
  363. active := true.
  364. self activateNextListItem.
  365. delay := [
  366. interval := [
  367. (self wrapper asJQuery hasClass: self focusClass)
  368. ifTrue: [
  369. self activateNextListItem ]
  370. ifFalse: [
  371. active := false.
  372. interval ifNotNil: [ interval clearInterval ].
  373. delay ifNotNil: [ delay clearTimeout] ] ]
  374. valueWithInterval: repeatInterval ]
  375. valueWithTimeout: 300 ] ].
  376. self wrapper asJQuery keyup: [ :e |
  377. active ifTrue: [
  378. active := false.
  379. interval ifNotNil: [ interval clearInterval ].
  380. delay ifNotNil: [ delay clearTimeout] ] ]
  381. ! !
  382. !HLListWidget methodsFor: 'initialization'!
  383. initialize
  384. super initialize.
  385. mapping := Dictionary new.
  386. ! !
  387. !HLListWidget methodsFor: 'private'!
  388. registerMappingFrom: anObject to: aTag
  389. mapping at: anObject put: aTag
  390. ! !
  391. !HLListWidget methodsFor: 'rendering'!
  392. renderButtonsOn: html
  393. !
  394. renderContentOn: html
  395. html ul
  396. class: 'nav nav-pills nav-stacked';
  397. with: [ self renderListOn: html ].
  398. html div class: 'pane_actions form-actions'; with: [
  399. self renderButtonsOn: html ].
  400. self setupKeyBindings
  401. !
  402. renderItem: anObject on: html
  403. | li |
  404. li := html li.
  405. self registerMappingFrom: anObject to: li.
  406. li
  407. class: (self cssClassForItem: anObject);
  408. at: 'list-data' put: (self items indexOf: anObject) asString;
  409. with: [
  410. html a
  411. with: [
  412. (html tag: 'i') class: (self iconForItem: anObject).
  413. self renderItemLabel: anObject on: html ];
  414. onClick: [
  415. self activateListItem: li asJQuery ] ]
  416. !
  417. renderItemLabel: anObject on: html
  418. html with: anObject asString
  419. !
  420. renderListOn: html
  421. mapping := Dictionary new.
  422. self items do: [ :each |
  423. self renderItem: each on: html ]
  424. ! !
  425. HLListWidget subclass: #HLNavigationListWidget
  426. instanceVariableNames: 'previous next'
  427. package: 'Helios-Core'!
  428. !HLNavigationListWidget methodsFor: 'accessing'!
  429. next
  430. ^ next
  431. !
  432. next: aWidget
  433. next := aWidget.
  434. aWidget previous = self ifFalse: [ aWidget previous: self ]
  435. !
  436. previous
  437. ^ previous
  438. !
  439. previous: aWidget
  440. previous := aWidget.
  441. aWidget next = self ifFalse: [ aWidget next: self ]
  442. ! !
  443. !HLNavigationListWidget methodsFor: 'actions'!
  444. nextFocus
  445. self next ifNotNil: [ self next focus ]
  446. !
  447. previousFocus
  448. self previous ifNotNil: [ self previous focus ]
  449. ! !
  450. !HLNavigationListWidget methodsFor: 'events'!
  451. setupKeyBindings
  452. super setupKeyBindings.
  453. self wrapper asJQuery keydown: [ :e |
  454. e which = 39 ifTrue: [
  455. self nextFocus ].
  456. e which = 37 ifTrue: [
  457. self previousFocus ] ]
  458. ! !
  459. HLWidget subclass: #HLManager
  460. instanceVariableNames: 'tabs activeTab keyBinder environment history'
  461. package: 'Helios-Core'!
  462. !HLManager methodsFor: 'accessing'!
  463. activeTab
  464. ^ activeTab
  465. !
  466. environment
  467. "The default environment used by all Helios objects"
  468. ^ environment ifNil: [ environment := self defaultEnvironment ]
  469. !
  470. environment: anEnvironment
  471. environment := anEnvironment
  472. !
  473. history
  474. ^ history ifNil: [ history := OrderedCollection new ]
  475. !
  476. history: aCollection
  477. history := aCollection
  478. !
  479. keyBinder
  480. ^ keyBinder ifNil: [ keyBinder := HLKeyBinder new ]
  481. !
  482. tabs
  483. ^ tabs ifNil: [ tabs := OrderedCollection new ]
  484. ! !
  485. !HLManager methodsFor: 'actions'!
  486. activate: aTab
  487. self keyBinder flushBindings.
  488. aTab registerBindings.
  489. activeTab := aTab.
  490. self
  491. refresh;
  492. addToHistory: aTab;
  493. show: aTab
  494. !
  495. addTab: aTab
  496. self tabs add: aTab.
  497. self activate: aTab
  498. !
  499. addToHistory: aTab
  500. self removeFromHistory: aTab.
  501. self history add: aTab
  502. !
  503. confirm: aString ifFalse: aBlock
  504. (HLConfirmation new
  505. confirmationString: aString;
  506. cancelBlock: aBlock;
  507. yourself)
  508. appendToJQuery: 'body' asJQuery
  509. !
  510. confirm: aString ifTrue: aBlock
  511. (HLConfirmation new
  512. confirmationString: aString;
  513. actionBlock: aBlock;
  514. yourself)
  515. appendToJQuery: 'body' asJQuery
  516. !
  517. removeActiveTab
  518. self removeTab: self activeTab
  519. !
  520. removeFromHistory: aTab
  521. self history: (self history reject: [ :each | each == aTab ])
  522. !
  523. removeTab: aTab
  524. (self tabs includes: aTab) ifFalse: [ ^ self ].
  525. self removeFromHistory: aTab.
  526. self tabs remove: aTab.
  527. self keyBinder flushBindings.
  528. aTab remove.
  529. self refresh.
  530. self history ifNotEmpty: [
  531. self history last activate ]
  532. ! !
  533. !HLManager methodsFor: 'defaults'!
  534. defaultEnvironment
  535. "If helios is loaded from within a frame, answer the parent window environment"
  536. window parent ifNil: [ ^ Environment new ].
  537. ^ ((window parent at: 'smalltalk')
  538. at: 'Environment') new
  539. ! !
  540. !HLManager methodsFor: 'initialization'!
  541. initialize
  542. super initialize.
  543. self keyBinder setupEvents
  544. ! !
  545. !HLManager methodsFor: 'rendering'!
  546. refresh
  547. (window jQuery: '.navbar') remove.
  548. self appendToJQuery: 'body' asJQuery
  549. !
  550. renderAddOn: html
  551. html li
  552. class: 'dropdown';
  553. with: [
  554. html a
  555. class: 'dropdown-toggle';
  556. at: 'data-toggle' put: 'dropdown';
  557. with: [
  558. html with: 'Open...'.
  559. (html tag: 'b') class: 'caret' ].
  560. html ul
  561. class: 'dropdown-menu';
  562. with: [
  563. ((HLWidget withAllSubclasses
  564. select: [ :each | each canBeOpenAsTab ])
  565. sorted: [ :a :b | a tabPriority < b tabPriority ])
  566. do: [ :each |
  567. html li with: [
  568. html a
  569. with: each tabLabel;
  570. onClick: [ each openAsTab ] ] ] ] ]
  571. !
  572. renderContentOn: html
  573. html div
  574. class: 'navbar navbar-fixed-top';
  575. with: [ html div
  576. class: 'navbar-inner';
  577. with: [ self renderTabsOn: html ] ]
  578. !
  579. renderTabsOn: html
  580. html ul
  581. class: 'nav';
  582. with: [
  583. self tabs do: [ :each |
  584. html li
  585. class: (each isActive ifTrue: [ 'active' ] ifFalse: [ 'inactive' ]);
  586. with: [
  587. html a
  588. with: [
  589. ((html tag: 'i') class: 'icon-remove')
  590. onClick: [ self removeTab: each ].
  591. html with: each displayLabel ];
  592. onClick: [ each activate ] ] ].
  593. self renderAddOn: html ]
  594. !
  595. show: aTab
  596. self tabs do: [ :each | each hide ].
  597. aTab show; focus
  598. ! !
  599. HLManager class instanceVariableNames: 'current'!
  600. !HLManager class methodsFor: 'accessing'!
  601. current
  602. ^ current ifNil: [ current := self basicNew initialize ]
  603. ! !
  604. !HLManager class methodsFor: 'initialization'!
  605. initialize
  606. self current appendToJQuery: 'body' asJQuery
  607. ! !
  608. !HLManager class methodsFor: 'instance creation'!
  609. new
  610. "Use current instead"
  611. self shouldNotImplement
  612. ! !
  613. HLWidget subclass: #HLSUnit
  614. instanceVariableNames: ''
  615. package: 'Helios-Core'!
  616. !HLSUnit class methodsFor: 'accessing'!
  617. tabLabel
  618. ^ 'SUnit'
  619. !
  620. tabPriority
  621. ^ 1000
  622. ! !
  623. !HLSUnit class methodsFor: 'testing'!
  624. canBeOpenAsTab
  625. ^ true
  626. ! !
  627. HLWidget subclass: #HLTranscript
  628. instanceVariableNames: ''
  629. package: 'Helios-Core'!
  630. !HLTranscript class methodsFor: 'accessing'!
  631. tabLabel
  632. ^ 'Transcript'
  633. !
  634. tabPriority
  635. ^ 600
  636. ! !
  637. !HLTranscript class methodsFor: 'testing'!
  638. canBeOpenAsTab
  639. ^ true
  640. ! !