Helios-Core.st 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715
  1. Smalltalk current createPackage: 'Helios-Core'!
  2. Object subclass: #HLModel
  3. instanceVariableNames: 'announcer environment'
  4. package: 'Helios-Core'!
  5. !HLModel commentStamp!
  6. I am the abstract superclass of all models of Helios.
  7. I am the "Model" part of the MVC pattern implementation in Helios.
  8. I provide access to an `Environment` object and both a local (model-specific) and global (system-specific) announcer.
  9. The `#withChangesDo:` method is handy for performing model changes ensuring that all widgets are aware of the change and can prevent it from happening.
  10. Modifications of the system should be done via commands (see `HLCommand` and subclasses).!
  11. !HLModel methodsFor: 'accessing'!
  12. announcer
  13. ^ announcer ifNil: [ announcer := Announcer new ]
  14. !
  15. environment
  16. ^ environment ifNil: [ self manager environment ]
  17. !
  18. environment: anEnvironment
  19. environment := anEnvironment
  20. !
  21. manager
  22. ^ HLManager current
  23. !
  24. systemAnnouncer
  25. ^ self environment systemAnnouncer
  26. ! !
  27. !HLModel methodsFor: 'error handling'!
  28. withChangesDo: aBlock
  29. [
  30. self announcer announce: (HLAboutToChange new
  31. actionBlock: aBlock).
  32. aBlock value.
  33. ]
  34. on: HLChangeForbidden
  35. do: [ :ex | ]
  36. ! !
  37. !HLModel methodsFor: 'testing'!
  38. isBrowserModel
  39. ^ false
  40. !
  41. isReferencesModel
  42. ^ false
  43. !
  44. isToolModel
  45. ^ false
  46. ! !
  47. HLModel subclass: #HLToolModel
  48. instanceVariableNames: 'selectedClass selectedPackage selectedProtocol selectedSelector'
  49. package: 'Helios-Core'!
  50. !HLToolModel commentStamp!
  51. I am a model specific to package and class manipulation. All browsers should either use me or a subclass as their model.
  52. I provide methods for package, class, protocol and method manipulation and access, forwarding to my environment.
  53. I also handle compilation of classes and methods as well as compilation and parsing errors.!
  54. !HLToolModel methodsFor: 'accessing'!
  55. allSelectors
  56. ^ self environment allSelectors
  57. !
  58. availableClassNames
  59. ^ self environment availableClassNames
  60. !
  61. availablePackageNames
  62. ^ self environment availablePackageNames
  63. !
  64. availablePackages
  65. ^ self environment availablePackageNames
  66. !
  67. availableProtocols
  68. ^ self environment availableProtocolsFor: self selectedClass
  69. !
  70. packages
  71. ^ self environment packages
  72. !
  73. selectedClass
  74. ^ selectedClass
  75. !
  76. selectedClass: aClass
  77. (self selectedClass = aClass and: [ aClass isNil ])
  78. ifTrue: [ ^ self ].
  79. self withChangesDo: [
  80. selectedClass = aClass ifTrue: [
  81. self selectedProtocol: nil ].
  82. aClass
  83. ifNil: [ selectedClass := nil ]
  84. ifNotNil: [
  85. self selectedPackage: aClass theNonMetaClass package.
  86. self showInstance
  87. ifTrue: [ selectedClass := aClass theNonMetaClass ]
  88. ifFalse: [ selectedClass := aClass theMetaClass ] ].
  89. self selectedProtocol: nil.
  90. self announcer announce: (HLClassSelected on: self selectedClass) ]
  91. !
  92. selectedMethod
  93. ^ self selectedClass ifNotNil: [
  94. self selectedClass methodDictionary
  95. at: selectedSelector
  96. ifAbsent: [ nil ] ]
  97. !
  98. selectedMethod: aCompiledMethod
  99. selectedSelector = aCompiledMethod ifTrue: [ ^ self ].
  100. self withChangesDo: [
  101. aCompiledMethod
  102. ifNil: [ selectedSelector := nil ]
  103. ifNotNil: [
  104. selectedClass := aCompiledMethod methodClass.
  105. selectedSelector := aCompiledMethod selector ].
  106. self announcer announce: (HLMethodSelected on: aCompiledMethod) ]
  107. !
  108. selectedPackage
  109. ^ selectedPackage
  110. !
  111. selectedPackage: aPackage
  112. selectedPackage = aPackage ifTrue: [ ^ self ].
  113. self withChangesDo: [
  114. selectedPackage := aPackage.
  115. self selectedClass: nil.
  116. self announcer announce: (HLPackageSelected on: aPackage) ]
  117. !
  118. selectedProtocol
  119. ^ selectedProtocol
  120. !
  121. selectedProtocol: aString
  122. selectedProtocol = aString ifTrue: [ ^ self ].
  123. self withChangesDo: [
  124. selectedProtocol := aString.
  125. self selectedMethod: nil.
  126. self announcer announce: (HLProtocolSelected on: aString) ]
  127. ! !
  128. !HLToolModel methodsFor: 'actions'!
  129. addInstVarNamed: aString
  130. self environment addInstVarNamed: aString to: self selectedClass.
  131. self announcer announce: (HLInstVarAdded new
  132. theClass: self selectedClass;
  133. variableName: aString;
  134. yourself)
  135. !
  136. save: aString
  137. self announcer announce: HLSourceCodeSaved new.
  138. (self shouldCompileClassDefinition: aString)
  139. ifTrue: [ self compileClassDefinition: aString ]
  140. ifFalse: [ self compileMethod: aString ]
  141. !
  142. saveSourceCode
  143. self announcer announce: HLSaveSourceCode new
  144. ! !
  145. !HLToolModel methodsFor: 'commands actions'!
  146. commitPackage
  147. "self
  148. withHelperLabelled: 'Committing package ', self selectedPackage name, '...'
  149. do: [ "self environment commitPackage: self selectedPackage" ]"
  150. !
  151. copyClassTo: aClassName
  152. self withChangesDo: [
  153. self environment
  154. copyClass: self selectedClass theNonMetaClass
  155. to: aClassName ]
  156. !
  157. moveClassToPackage: aPackageName
  158. self withChangesDo: [
  159. self environment
  160. moveClass: self selectedClass theNonMetaClass
  161. toPackage: aPackageName ]
  162. !
  163. moveMethodToClass: aClassName
  164. self withChangesDo: [
  165. self environment
  166. moveMethod: self selectedMethod
  167. toClass: aClassName ]
  168. !
  169. moveMethodToProtocol: aProtocol
  170. self withChangesDo: [
  171. self environment
  172. moveMethod: self selectedMethod
  173. toProtocol: aProtocol ]
  174. !
  175. openClassNamed: aString
  176. | class |
  177. self withChangesDo: [
  178. class := self environment classNamed: aString.
  179. self selectedPackage: class package.
  180. self selectedClass: class ]
  181. !
  182. removeClass
  183. self withChangesDo: [
  184. self manager
  185. confirm: 'Do you REALLY want to remove class ', self selectedClass name
  186. ifTrue: [ self environment removeClass: self selectedClass ] ]
  187. !
  188. removeMethod
  189. self withChangesDo: [
  190. self manager
  191. confirm: 'Do you REALLY want to remove method ', self selectedMethod methodClass name,' >> #', self selectedMethod selector
  192. ifTrue: [ self environment removeMethod: self selectedMethod ] ]
  193. !
  194. removeProtocol
  195. self withChangesDo: [
  196. self manager
  197. confirm: 'Do you REALLY want to remove protocol ', self selectedProtocol
  198. ifTrue: [ self environment
  199. removeProtocol: self selectedProtocol
  200. from: self selectedClass ] ]
  201. !
  202. renameClassTo: aClassName
  203. self withChangesDo: [
  204. self environment
  205. renameClass: self selectedClass theNonMetaClass
  206. to: aClassName ]
  207. !
  208. renameProtocolTo: aString
  209. self withChangesDo: [
  210. self environment
  211. renameProtocol: self selectedProtocol
  212. to: aString
  213. in: self selectedClass ]
  214. ! !
  215. !HLToolModel methodsFor: 'compiling'!
  216. compileClassComment: aString
  217. self environment
  218. compileClassComment: aString
  219. for: self selectedClass
  220. !
  221. compileClassDefinition: aString
  222. self environment compileClassDefinition: aString
  223. !
  224. compileMethod: aString
  225. | method |
  226. self withCompileErrorHandling: [
  227. method := self environment
  228. compileMethod: aString
  229. for: self selectedClass
  230. protocol: self compilationProtocol.
  231. self selectedMethod: method ]
  232. ! !
  233. !HLToolModel methodsFor: 'defaults'!
  234. allProtocol
  235. ^ '-- all --'
  236. !
  237. unclassifiedProtocol
  238. ^ 'as yet unclassified'
  239. ! !
  240. !HLToolModel methodsFor: 'error handling'!
  241. handleCompileError: anError
  242. self announcer announce: (HLCompileErrorRaised new
  243. error: anError;
  244. yourself)
  245. !
  246. handleParseError: anError
  247. | split line column messageToInsert |
  248. split := anError messageText tokenize: ' : '.
  249. messageToInsert := split second.
  250. "21 = 'Parse error on line ' size + 1"
  251. split := split first copyFrom: 21 to: split first size.
  252. split := split tokenize: ' column '.
  253. line := split first.
  254. column := split second.
  255. self announcer announce: (HLParseErrorRaised new
  256. line: line asNumber;
  257. column: column asNumber;
  258. message: messageToInsert;
  259. error: anError;
  260. yourself)
  261. !
  262. handleUnkownVariableError: anError
  263. self announcer announce: (HLUnknownVariableErrorRaised new
  264. error: anError;
  265. yourself)
  266. !
  267. withCompileErrorHandling: aBlock
  268. self environment
  269. evaluate: [
  270. self environment
  271. evaluate: [
  272. self environment
  273. evaluate: aBlock
  274. on: ParseError
  275. do: [:ex | self handleParseError: ex ] ]
  276. on: UnknownVariableError
  277. do: [ :ex | self handleUnkownVariableError: ex ] ]
  278. on: CompilerError
  279. do: [ :ex | self handleCompileError: ex ]
  280. ! !
  281. !HLToolModel methodsFor: 'private'!
  282. compilationProtocol
  283. | currentProtocol |
  284. currentProtocol := self selectedProtocol.
  285. currentProtocol ifNil: [ currentProtocol := self unclassifiedProtocol ].
  286. self selectedMethod ifNotNil: [ currentProtocol := self selectedMethod protocol ].
  287. ^ currentProtocol = self allProtocol
  288. ifTrue: [ self unclassifiedProtocol ]
  289. ifFalse: [ currentProtocol ]
  290. !
  291. withHelperLabelled: aString do: aBlock
  292. "TODO: doesn't belong here"
  293. (window jQuery: '#helper') remove.
  294. [ :html |
  295. html div
  296. id: 'helper';
  297. with: aString ] appendToJQuery: 'body' asJQuery.
  298. [
  299. aBlock value.
  300. (window jQuery: '#helper') remove
  301. ]
  302. valueWithTimeout: 10
  303. ! !
  304. !HLToolModel methodsFor: 'testing'!
  305. isToolModel
  306. ^ true
  307. !
  308. shouldCompileClassDefinition: aString
  309. ^ self selectedClass isNil or: [
  310. aString match: '^[A-Z]' ]
  311. ! !
  312. !HLToolModel class methodsFor: 'actions'!
  313. on: anEnvironment
  314. ^ self new
  315. environment: anEnvironment;
  316. yourself
  317. ! !
  318. ProgressHandler subclass: #HLProgressHandler
  319. instanceVariableNames: ''
  320. package: 'Helios-Core'!
  321. !HLProgressHandler commentStamp!
  322. I am a specific progress handler for Helios, displaying progresses in a modal window.!
  323. !HLProgressHandler methodsFor: 'progress handling'!
  324. do: aBlock on: aCollection displaying: aString
  325. HLProgress default
  326. do: aBlock
  327. on: aCollection
  328. displaying: aString
  329. ! !
  330. Widget subclass: #HLTab
  331. instanceVariableNames: 'widget label root'
  332. package: 'Helios-Core'!
  333. !HLTab commentStamp!
  334. I am a widget specialized into building another widget as an Helios tab.
  335. I should not be used directly, `HLWidget class >> #openAsTab` should be used instead.
  336. ## Example
  337. HLWorkspace openAsTab!
  338. !HLTab methodsFor: 'accessing'!
  339. activate
  340. self manager activate: self
  341. !
  342. add
  343. self manager addTab: self
  344. !
  345. cssClass
  346. ^ self widget tabClass
  347. !
  348. displayLabel
  349. ^ self label size > 20
  350. ifTrue: [ (self label first: 20), '...' ]
  351. ifFalse: [ self label ]
  352. !
  353. focus
  354. self widget canHaveFocus ifTrue: [
  355. self widget focus ]
  356. !
  357. label
  358. ^ label ifNil: [ '' ]
  359. !
  360. label: aString
  361. label := aString
  362. !
  363. manager
  364. ^ HLManager current
  365. !
  366. widget
  367. ^ widget
  368. !
  369. widget: aWidget
  370. widget := aWidget
  371. ! !
  372. !HLTab methodsFor: 'actions'!
  373. hide
  374. root ifNotNil: [ root asJQuery css: 'visibility' put: 'hidden' ]
  375. !
  376. registerBindings
  377. self widget registerBindings
  378. !
  379. remove
  380. self widget unregister.
  381. root ifNotNil: [ root asJQuery remove ]
  382. !
  383. show
  384. root
  385. ifNil: [ self appendToJQuery: 'body' asJQuery ]
  386. ifNotNil: [ root asJQuery css: 'visibility' put: 'visible' ]
  387. ! !
  388. !HLTab methodsFor: 'rendering'!
  389. renderOn: html
  390. root := html div
  391. class: 'tab';
  392. yourself.
  393. self renderTab
  394. !
  395. renderTab
  396. root contents: [ :html |
  397. html div
  398. class: 'amber_box';
  399. with: [ self widget renderOn: html ] ]
  400. ! !
  401. !HLTab methodsFor: 'testing'!
  402. isActive
  403. ^ self manager activeTab = self
  404. ! !
  405. !HLTab class methodsFor: 'instance creation'!
  406. on: aWidget labelled: aString
  407. ^ self new
  408. widget: aWidget;
  409. label: aString;
  410. yourself
  411. ! !
  412. Widget subclass: #HLWidget
  413. instanceVariableNames: 'wrapper'
  414. package: 'Helios-Core'!
  415. !HLWidget commentStamp!
  416. I am the abstract superclass of all Helios widgets.
  417. I provide common methods, additional behavior to widgets useful for Helios, like dialog creation, command execution and tab creation.
  418. ## API
  419. 1. Rendering
  420. Instead of overriding `#renderOn:` as with other Widget subclasses, my subclasses should override `#renderContentOn:`.
  421. 2. Refreshing
  422. To re-render a widget, use `#refresh`.
  423. 3. Key bindings registration and tabs
  424. When displayed as a tab, the widget has a chance to register keybindings with the `#registerBindingsOn:` hook method.
  425. 4. Unregistration
  426. When a widget has subscribed to announcements or other actions that need to be cleared when closing the tab, the hook method `#unregister` will be called by helios.
  427. 5. Tabs
  428. To enable a widget class to be open as a tab, override the class-side `#canBeOpenAsTab` method to answer `true`. `#tabClass` and `#tabPriority` can be overridden too to respectively change the css class of the tab and the order of tabs in the main menu.
  429. 6. Command execution
  430. An helios command (instance of `HLCommand` or one of its subclass) can be executed with `#execute:`.!
  431. !HLWidget methodsFor: 'accessing'!
  432. manager
  433. ^ HLManager current
  434. !
  435. tabClass
  436. ^ self class tabClass
  437. !
  438. wrapper
  439. ^ wrapper
  440. ! !
  441. !HLWidget methodsFor: 'actions'!
  442. alert: aString
  443. window alert: aString
  444. !
  445. confirm: aString ifTrue: aBlock
  446. self manager confirm: aString ifTrue: aBlock
  447. !
  448. execute: aCommand
  449. HLManager current keyBinder
  450. activate;
  451. applyBinding: aCommand asBinding
  452. !
  453. request: aString do: aBlock
  454. self manager request: aString do: aBlock
  455. !
  456. request: aString value: valueString do: aBlock
  457. self manager
  458. request: aString
  459. value: valueString
  460. do: aBlock
  461. !
  462. unregister
  463. "This method is called whenever the receiver is closed (as a tab).
  464. Widgets subscribing to announcements should unregister there"
  465. ! !
  466. !HLWidget methodsFor: 'keybindings'!
  467. registerBindings
  468. self registerBindingsOn: self manager keyBinder bindings
  469. !
  470. registerBindingsOn: aBindingGroup
  471. ! !
  472. !HLWidget methodsFor: 'rendering'!
  473. renderContentOn: html
  474. !
  475. renderOn: html
  476. wrapper := html div.
  477. [ :renderer | self renderContentOn: renderer ] appendToJQuery: wrapper asJQuery
  478. ! !
  479. !HLWidget methodsFor: 'testing'!
  480. canHaveFocus
  481. ^ false
  482. ! !
  483. !HLWidget methodsFor: 'updating'!
  484. refresh
  485. self wrapper ifNil: [ ^ self ].
  486. self wrapper asJQuery empty.
  487. [ :html | self renderContentOn: html ] appendToJQuery: self wrapper asJQuery
  488. ! !
  489. !HLWidget class methodsFor: 'accessing'!
  490. openAsTab
  491. self canBeOpenAsTab ifFalse: [ ^ self ].
  492. HLManager current addTab: (HLTab on: self new labelled: self tabLabel)
  493. !
  494. tabClass
  495. ^ ''
  496. !
  497. tabLabel
  498. ^ 'Tab'
  499. !
  500. tabPriority
  501. ^ 500
  502. ! !
  503. !HLWidget class methodsFor: 'testing'!
  504. canBeOpenAsTab
  505. ^ false
  506. ! !
  507. HLWidget subclass: #HLFocusableWidget
  508. instanceVariableNames: ''
  509. package: 'Helios-Core'!
  510. !HLFocusableWidget commentStamp!
  511. I am a widget that can be focused.
  512. ## API
  513. Instead of overriding `#renderOn:` as with other `Widget` subclasses, my subclasses should override `#renderContentOn:`.
  514. To bring the focus to the widget, use the `#focus` method.!
  515. !HLFocusableWidget methodsFor: 'accessing'!
  516. focusClass
  517. ^ 'focused'
  518. ! !
  519. !HLFocusableWidget methodsFor: 'events'!
  520. blur
  521. self wrapper asJQuery blur
  522. !
  523. focus
  524. self wrapper asJQuery focus
  525. !
  526. hasFocus
  527. ^ self wrapper notNil and: [ self wrapper asJQuery is: ':focus' ]
  528. ! !
  529. !HLFocusableWidget methodsFor: 'rendering'!
  530. renderContentOn: html
  531. !
  532. renderOn: html
  533. wrapper := html div
  534. class: 'hl_widget';
  535. yourself.
  536. wrapper with: [ self renderContentOn: html ].
  537. wrapper
  538. at: 'tabindex' put: '0';
  539. onBlur: [ self wrapper asJQuery removeClass: self focusClass ];
  540. onFocus: [ self wrapper asJQuery addClass: self focusClass ]
  541. ! !
  542. !HLFocusableWidget methodsFor: 'testing'!
  543. canHaveFocus
  544. ^ true
  545. ! !
  546. HLFocusableWidget subclass: #HLListWidget
  547. instanceVariableNames: 'items selectedItem mapping'
  548. package: 'Helios-Core'!
  549. !HLListWidget methodsFor: 'accessing'!
  550. cssClassForItem: anObject
  551. ^ ''
  552. !
  553. items
  554. ^ items ifNil: [ items := self defaultItems ]
  555. !
  556. items: aCollection
  557. items := aCollection
  558. !
  559. listCssClassForItem: anObject
  560. ^ self selectedItem = anObject
  561. ifTrue: [ 'active' ]
  562. ifFalse: [ 'inactive' ]
  563. !
  564. positionOf: aListItem
  565. <
  566. return aListItem.parent().children().get().indexOf(aListItem.get(0)) + 1
  567. >
  568. !
  569. selectedItem
  570. ^ selectedItem
  571. !
  572. selectedItem: anObject
  573. selectedItem := anObject
  574. ! !
  575. !HLListWidget methodsFor: 'actions'!
  576. activateFirstListItem
  577. self activateListItem: (window jQuery: ((wrapper asJQuery find: 'li.inactive') get: 0))
  578. !
  579. activateItem: anObject
  580. self activateListItem: (mapping
  581. at: anObject
  582. ifAbsent: [ ^ self ]) asJQuery
  583. !
  584. activateListItem: aListItem
  585. | item |
  586. (aListItem get: 0) ifNil: [ ^self ].
  587. aListItem parent children removeClass: 'active'.
  588. aListItem addClass: 'active'.
  589. self ensureVisible: aListItem.
  590. "Activate the corresponding item"
  591. item := (self items at: (aListItem attr: 'list-data') asNumber).
  592. self selectedItem == item ifFalse: [
  593. self selectItem: item ]
  594. !
  595. activateNextListItem
  596. self activateListItem: (self wrapper asJQuery find: 'li.active') next.
  597. "select the first item if none is selected"
  598. (self wrapper asJQuery find: ' .active') get ifEmpty: [
  599. self activateFirstListItem ]
  600. !
  601. activatePreviousListItem
  602. self activateListItem: (self wrapper asJQuery find: 'li.active') prev
  603. !
  604. ensureVisible: aListItem
  605. "Move the scrollbar to show the active element"
  606. | perent position |
  607. position := self positionOf: aListItem.
  608. parent := aListItem parent.
  609. aListItem position top < 0 ifTrue: [
  610. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem position top - 10) ].
  611. aListItem position top + aListItem height > parent height ifTrue: [
  612. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem height - (parent height - aListItem position top)) +10 ]
  613. !
  614. focus
  615. super focus.
  616. self items isEmpty ifFalse: [
  617. self selectedItem ifNil: [ self activateFirstListItem ] ]
  618. !
  619. refresh
  620. super refresh.
  621. self ensureVisible: (mapping
  622. at: self selectedItem
  623. ifAbsent: [ ^ self ]) asJQuery
  624. !
  625. selectItem: anObject
  626. self selectedItem: anObject
  627. ! !
  628. !HLListWidget methodsFor: 'defaults'!
  629. defaultItems
  630. ^ #()
  631. ! !
  632. !HLListWidget methodsFor: 'events'!
  633. setupKeyBindings
  634. "TODO: refactor this!!"
  635. | active interval delay repeatInterval |
  636. active := false.
  637. repeatInterval := 70.
  638. self wrapper asJQuery unbind: 'keydown'.
  639. self wrapper asJQuery keydown: [ :e |
  640. (e which = 38 and: [ active = false ]) ifTrue: [
  641. active := true.
  642. self activatePreviousListItem.
  643. delay := [
  644. interval := [
  645. (self wrapper asJQuery hasClass: self focusClass)
  646. ifTrue: [
  647. self activatePreviousListItem ]
  648. ifFalse: [
  649. active := false.
  650. interval ifNotNil: [ interval clearInterval ].
  651. delay ifNotNil: [ delay clearTimeout] ] ]
  652. valueWithInterval: repeatInterval ]
  653. valueWithTimeout: 300 ].
  654. (e which = 40 and: [ active = false ]) ifTrue: [
  655. active := true.
  656. self activateNextListItem.
  657. delay := [
  658. interval := [
  659. (self wrapper asJQuery hasClass: self focusClass)
  660. ifTrue: [
  661. self activateNextListItem ]
  662. ifFalse: [
  663. active := false.
  664. interval ifNotNil: [ interval clearInterval ].
  665. delay ifNotNil: [ delay clearTimeout] ] ]
  666. valueWithInterval: repeatInterval ]
  667. valueWithTimeout: 300 ] ].
  668. self wrapper asJQuery keyup: [ :e |
  669. active ifTrue: [
  670. active := false.
  671. interval ifNotNil: [ interval clearInterval ].
  672. delay ifNotNil: [ delay clearTimeout] ] ]
  673. ! !
  674. !HLListWidget methodsFor: 'initialization'!
  675. initialize
  676. super initialize.
  677. mapping := Dictionary new.
  678. ! !
  679. !HLListWidget methodsFor: 'private'!
  680. registerMappingFrom: anObject to: aTag
  681. mapping at: anObject put: aTag
  682. ! !
  683. !HLListWidget methodsFor: 'rendering'!
  684. renderButtonsOn: html
  685. !
  686. renderContentOn: html
  687. html ul
  688. class: 'nav nav-pills nav-stacked';
  689. with: [ self renderListOn: html ].
  690. html div class: 'pane_actions form-actions'; with: [
  691. self renderButtonsOn: html ].
  692. self setupKeyBindings
  693. !
  694. renderItem: anObject on: html
  695. | li |
  696. li := html li.
  697. self registerMappingFrom: anObject to: li.
  698. li
  699. at: 'list-data' put: (self items indexOf: anObject) asString;
  700. class: (self listCssClassForItem: anObject);
  701. with: [
  702. html a
  703. with: [
  704. (html tag: 'i') class: (self cssClassForItem: anObject).
  705. self renderItemLabel: anObject on: html ];
  706. onClick: [
  707. self activateListItem: li asJQuery ] ]
  708. !
  709. renderItemLabel: anObject on: html
  710. html with: anObject asString
  711. !
  712. renderListOn: html
  713. mapping := Dictionary new.
  714. self items do: [ :each |
  715. self renderItem: each on: html ]
  716. ! !
  717. HLListWidget subclass: #HLNavigationListWidget
  718. instanceVariableNames: 'previous next'
  719. package: 'Helios-Core'!
  720. !HLNavigationListWidget methodsFor: 'accessing'!
  721. next
  722. ^ next
  723. !
  724. next: aWidget
  725. next := aWidget.
  726. aWidget previous = self ifFalse: [ aWidget previous: self ]
  727. !
  728. previous
  729. ^ previous
  730. !
  731. previous: aWidget
  732. previous := aWidget.
  733. aWidget next = self ifFalse: [ aWidget next: self ]
  734. ! !
  735. !HLNavigationListWidget methodsFor: 'actions'!
  736. nextFocus
  737. self next ifNotNil: [ self next focus ]
  738. !
  739. previousFocus
  740. self previous ifNotNil: [ self previous focus ]
  741. ! !
  742. !HLNavigationListWidget methodsFor: 'events'!
  743. setupKeyBindings
  744. super setupKeyBindings.
  745. self wrapper asJQuery keydown: [ :e |
  746. e which = 39 ifTrue: [
  747. self nextFocus ].
  748. e which = 37 ifTrue: [
  749. self previousFocus ] ]
  750. ! !
  751. HLNavigationListWidget subclass: #HLToolListWidget
  752. instanceVariableNames: 'model'
  753. package: 'Helios-Core'!
  754. !HLToolListWidget methodsFor: 'accessing'!
  755. commandCategory
  756. ^ self label
  757. !
  758. label
  759. ^ 'List'
  760. !
  761. menuCommands
  762. "Answer a collection of commands to be put in the cog menu"
  763. ^ ((HLToolCommand concreteClasses
  764. select: [ :each | each isValidFor: self model ])
  765. collect: [ :each | each for: self model ])
  766. select: [ :each |
  767. each category = self commandCategory and: [
  768. each isAction and: [ each isActive ] ] ]
  769. !
  770. model
  771. ^ model
  772. !
  773. model: aBrowserModel
  774. model := aBrowserModel.
  775. self
  776. observeSystem;
  777. observeModel
  778. !
  779. selectedItem: anItem
  780. "Selection changed, update the cog menu"
  781. super selectedItem: anItem.
  782. self updateMenu
  783. ! !
  784. !HLToolListWidget methodsFor: 'actions'!
  785. activateListItem: anItem
  786. self model withChangesDo: [ super activateListItem: anItem ]
  787. !
  788. activateNextListItem
  789. self model withChangesDo: [ super activateNextListItem ]
  790. !
  791. activatePreviousListItem
  792. self model withChangesDo: [ super activatePreviousListItem ]
  793. !
  794. observeModel
  795. !
  796. observeSystem
  797. !
  798. unregister
  799. super unregister.
  800. self model announcer unsubscribe: self.
  801. self model systemAnnouncer unsubscribe: self
  802. ! !
  803. !HLToolListWidget methodsFor: 'rendering'!
  804. renderContentOn: html
  805. self renderHeadOn: html.
  806. super renderContentOn: html
  807. !
  808. renderHeadOn: html
  809. html div
  810. class: 'list-label';
  811. with: [
  812. html with: self label.
  813. self renderMenuOn: html ]
  814. !
  815. renderMenuOn: html
  816. | commands |
  817. commands := self menuCommands.
  818. commands isEmpty ifTrue: [ ^ self ].
  819. html div
  820. class: 'btn-group cog';
  821. with: [
  822. html a
  823. class: 'btn dropdown-toggle';
  824. at: 'data-toggle' put: 'dropdown';
  825. with: [ (html tag: 'i') class: 'icon-cog' ].
  826. html ul
  827. class: 'dropdown-menu pull-right';
  828. with: [
  829. self menuCommands do: [ :each |
  830. html li with: [ html a
  831. with: each menuLabel;
  832. onClick: [ self execute: each ] ] ] ] ]
  833. ! !
  834. !HLToolListWidget methodsFor: 'updating'!
  835. updateMenu
  836. (self wrapper asJQuery find: '.cog') remove.
  837. [ :html | self renderMenuOn: html ]
  838. appendToJQuery: (self wrapper asJQuery find: '.list-label')
  839. ! !
  840. !HLToolListWidget class methodsFor: 'instance creation'!
  841. on: aModel
  842. ^ self new
  843. model: aModel;
  844. yourself
  845. ! !
  846. HLWidget subclass: #HLManager
  847. instanceVariableNames: 'tabs activeTab keyBinder environment history'
  848. package: 'Helios-Core'!
  849. !HLManager methodsFor: 'accessing'!
  850. activeTab
  851. ^ activeTab
  852. !
  853. environment
  854. "The default environment used by all Helios objects"
  855. ^ environment ifNil: [ environment := self defaultEnvironment ]
  856. !
  857. environment: anEnvironment
  858. environment := anEnvironment
  859. !
  860. history
  861. ^ history ifNil: [ history := OrderedCollection new ]
  862. !
  863. history: aCollection
  864. history := aCollection
  865. !
  866. keyBinder
  867. ^ keyBinder ifNil: [ keyBinder := HLKeyBinder new ]
  868. !
  869. tabs
  870. ^ tabs ifNil: [ tabs := OrderedCollection new ]
  871. ! !
  872. !HLManager methodsFor: 'actions'!
  873. activate: aTab
  874. self keyBinder flushBindings.
  875. aTab registerBindings.
  876. activeTab := aTab.
  877. self
  878. refresh;
  879. addToHistory: aTab;
  880. show: aTab
  881. !
  882. addTab: aTab
  883. self tabs add: aTab.
  884. self activate: aTab
  885. !
  886. addToHistory: aTab
  887. self removeFromHistory: aTab.
  888. self history add: aTab
  889. !
  890. confirm: aString ifFalse: aBlock
  891. (HLConfirmation new
  892. confirmationString: aString;
  893. cancelBlock: aBlock;
  894. yourself)
  895. appendToJQuery: 'body' asJQuery
  896. !
  897. confirm: aString ifTrue: aBlock
  898. (HLConfirmation new
  899. confirmationString: aString;
  900. actionBlock: aBlock;
  901. yourself)
  902. appendToJQuery: 'body' asJQuery
  903. !
  904. registerErrorHandler: anErrorHandler
  905. self environment registerErrorHandler: anErrorHandler
  906. !
  907. registerInspector: anInspector
  908. self environment registerInspector: anInspector
  909. !
  910. registerProgressHandler: aProgressHandler
  911. self environment registerProgressHandler: aProgressHandler
  912. !
  913. removeActiveTab
  914. self removeTab: self activeTab
  915. !
  916. removeFromHistory: aTab
  917. self history: (self history reject: [ :each | each == aTab ])
  918. !
  919. removeTab: aTab
  920. (self tabs includes: aTab) ifFalse: [ ^ self ].
  921. self removeFromHistory: aTab.
  922. self tabs remove: aTab.
  923. self keyBinder flushBindings.
  924. aTab remove.
  925. self refresh.
  926. self history ifNotEmpty: [
  927. self history last activate ]
  928. !
  929. request: aString do: aBlock
  930. self
  931. request: aString
  932. value: ''
  933. do: aBlock
  934. !
  935. request: aString value: valueString do: aBlock
  936. (HLRequest new
  937. confirmationString: aString;
  938. actionBlock: aBlock;
  939. value: valueString;
  940. yourself)
  941. appendToJQuery: 'body' asJQuery
  942. ! !
  943. !HLManager methodsFor: 'defaults'!
  944. defaultEnvironment
  945. "If helios is loaded from within a frame, answer the parent window environment"
  946. | parent |
  947. parent := window opener ifNil: [ window parent ].
  948. parent ifNil: [ ^ Environment new ].
  949. ^ ((parent at: 'smalltalk')
  950. at: 'Environment') new
  951. ! !
  952. !HLManager methodsFor: 'initialization'!
  953. initialize
  954. super initialize.
  955. HLErrorHandler register.
  956. HLProgressHandler register.
  957. self registerInspector: HLInspector.
  958. self registerErrorHandler: ErrorHandler current.
  959. self registerProgressHandler: ProgressHandler current.
  960. self keyBinder setupEvents
  961. ! !
  962. !HLManager methodsFor: 'rendering'!
  963. refresh
  964. (window jQuery: '.navbar') remove.
  965. self appendToJQuery: 'body' asJQuery
  966. !
  967. renderAddOn: html
  968. html li
  969. class: 'dropdown';
  970. with: [
  971. html a
  972. class: 'dropdown-toggle';
  973. at: 'data-toggle' put: 'dropdown';
  974. with: [
  975. html with: 'Open...'.
  976. (html tag: 'b') class: 'caret' ].
  977. html ul
  978. class: 'dropdown-menu';
  979. with: [
  980. ((HLWidget withAllSubclasses
  981. select: [ :each | each canBeOpenAsTab ])
  982. sorted: [ :a :b | a tabPriority < b tabPriority ])
  983. do: [ :each |
  984. html li with: [
  985. html a
  986. with: each tabLabel;
  987. onClick: [ each openAsTab ] ] ] ] ]
  988. !
  989. renderContentOn: html
  990. html div
  991. class: 'navbar navbar-fixed-top';
  992. with: [ html div
  993. class: 'navbar-inner';
  994. with: [ self renderTabsOn: html ] ]
  995. !
  996. renderTabsOn: html
  997. html ul
  998. class: 'nav';
  999. with: [
  1000. self tabs do: [ :each |
  1001. html li
  1002. class: (each isActive ifTrue: [ 'active' ] ifFalse: [ 'inactive' ]);
  1003. with: [
  1004. html a
  1005. with: [
  1006. ((html tag: 'i') class: 'close')
  1007. onClick: [ self removeTab: each ].
  1008. html span
  1009. class: each cssClass;
  1010. with: each displayLabel ];
  1011. onClick: [ each activate ] ] ].
  1012. self renderAddOn: html ]
  1013. !
  1014. show: aTab
  1015. self tabs do: [ :each | each hide ].
  1016. aTab show; focus
  1017. ! !
  1018. HLManager class instanceVariableNames: 'current'!
  1019. !HLManager class methodsFor: 'accessing'!
  1020. current
  1021. ^ current ifNil: [ current := self basicNew initialize ]
  1022. ! !
  1023. !HLManager class methodsFor: 'initialization'!
  1024. initialize
  1025. self current appendToJQuery: 'body' asJQuery
  1026. ! !
  1027. !HLManager class methodsFor: 'instance creation'!
  1028. new
  1029. "Use current instead"
  1030. self shouldNotImplement
  1031. ! !
  1032. HLWidget subclass: #HLModal
  1033. instanceVariableNames: ''
  1034. package: 'Helios-Core'!
  1035. !HLModal commentStamp!
  1036. I implement an abstract modal widget.!
  1037. !HLModal methodsFor: 'accessing'!
  1038. cssClass
  1039. ^ ''
  1040. ! !
  1041. !HLModal methodsFor: 'actions'!
  1042. cancel
  1043. self remove
  1044. !
  1045. remove
  1046. (window jQuery: '.dialog') removeClass: 'active'.
  1047. [
  1048. (window jQuery: '#overlay') remove.
  1049. (window jQuery: '.dialog') remove
  1050. ] valueWithTimeout: 300
  1051. ! !
  1052. !HLModal methodsFor: 'rendering'!
  1053. renderButtonsOn: html
  1054. !
  1055. renderContentOn: html
  1056. | confirmButton |
  1057. html div id: 'overlay'.
  1058. html div
  1059. class: 'dialog ', self cssClass;
  1060. with: [
  1061. self
  1062. renderMainOn: html;
  1063. renderButtonsOn: html ].
  1064. (window jQuery: '.dialog') addClass: 'active'.
  1065. self setupKeyBindings
  1066. !
  1067. renderMainOn: html
  1068. !
  1069. setupKeyBindings
  1070. (window jQuery: '.dialog') keyup: [ :e |
  1071. e keyCode = 27 ifTrue: [ self cancel ] ]
  1072. ! !
  1073. HLModal subclass: #HLConfirmation
  1074. instanceVariableNames: 'confirmationString actionBlock cancelBlock'
  1075. package: 'Helios-Core'!
  1076. !HLConfirmation commentStamp!
  1077. I display confirmation messages.
  1078. Instead of creating an instance directly, use `HLWidget >> #confirm:ifTrue:`.!
  1079. !HLConfirmation methodsFor: 'accessing'!
  1080. actionBlock
  1081. ^ actionBlock ifNil: [ [] ]
  1082. !
  1083. actionBlock: aBlock
  1084. actionBlock := aBlock
  1085. !
  1086. cancelBlock
  1087. ^ cancelBlock ifNil: [ [] ]
  1088. !
  1089. cancelBlock: aBlock
  1090. cancelBlock := aBlock
  1091. !
  1092. confirmationString
  1093. ^ confirmationString ifNil: [ 'Confirm' ]
  1094. !
  1095. confirmationString: aString
  1096. confirmationString := aString
  1097. ! !
  1098. !HLConfirmation methodsFor: 'actions'!
  1099. cancel
  1100. self cancelBlock value.
  1101. self remove
  1102. !
  1103. confirm
  1104. self actionBlock value.
  1105. self remove
  1106. !
  1107. remove
  1108. (window jQuery: '.dialog') removeClass: 'active'.
  1109. [
  1110. (window jQuery: '#overlay') remove.
  1111. (window jQuery: '.dialog') remove
  1112. ] valueWithTimeout: 300
  1113. ! !
  1114. !HLConfirmation methodsFor: 'rendering'!
  1115. renderButtonsOn: html
  1116. | confirmButton |
  1117. html div
  1118. class: 'buttons';
  1119. with: [
  1120. html button
  1121. class: 'button';
  1122. with: 'Cancel';
  1123. onClick: [ self cancel ].
  1124. confirmButton := html button
  1125. class: 'button default';
  1126. with: 'Confirm';
  1127. onClick: [ self confirm ] ].
  1128. confirmButton asJQuery focus
  1129. !
  1130. renderMainOn: html
  1131. html span with: self confirmationString
  1132. ! !
  1133. HLConfirmation subclass: #HLRequest
  1134. instanceVariableNames: 'input value'
  1135. package: 'Helios-Core'!
  1136. !HLRequest commentStamp!
  1137. I display a modal window requesting user input.
  1138. Instead of creating instances manually, use `HLWidget >> #request:do:` and `#request:value:do:`.!
  1139. !HLRequest methodsFor: 'accessing'!
  1140. cssClass
  1141. ^ 'large'
  1142. !
  1143. value
  1144. ^ value ifNil: [ '' ]
  1145. !
  1146. value: aString
  1147. value := aString
  1148. ! !
  1149. !HLRequest methodsFor: 'actions'!
  1150. confirm
  1151. self actionBlock value: input asJQuery val.
  1152. self remove
  1153. ! !
  1154. !HLRequest methodsFor: 'rendering'!
  1155. renderMainOn: html
  1156. super renderMainOn: html.
  1157. input := html textarea.
  1158. input asJQuery val: self value
  1159. ! !
  1160. HLModal subclass: #HLProgress
  1161. instanceVariableNames: 'progressBars visible'
  1162. package: 'Helios-Core'!
  1163. !HLProgress commentStamp!
  1164. I am a widget used to display progress modal dialogs.
  1165. My default instance is accessed with `HLProgress >> #default`.
  1166. See `HLProgressHandler`.!
  1167. !HLProgress methodsFor: 'accessing'!
  1168. progressBars
  1169. ^ progressBars ifNil: [ progressBars := OrderedCollection new ]
  1170. ! !
  1171. !HLProgress methodsFor: 'actions'!
  1172. addProgressBar: aProgressBar
  1173. self show.
  1174. self progressBars add: aProgressBar.
  1175. aProgressBar appendToJQuery: (self wrapper asJQuery find: '.dialog')
  1176. !
  1177. do: aBlock on: aCollection displaying: aString
  1178. | progressBar |
  1179. progressBar := HLProgressBar new
  1180. parent: self;
  1181. label: aString;
  1182. workBlock: aBlock;
  1183. collection: aCollection;
  1184. yourself.
  1185. self addProgressBar: progressBar.
  1186. progressBar start
  1187. !
  1188. remove
  1189. self isVisible ifTrue: [
  1190. visible := false.
  1191. super remove ]
  1192. !
  1193. removeProgressBar: aProgressBar
  1194. self progressBars remove: aProgressBar ifAbsent: [].
  1195. aProgressBar wrapper asJQuery remove.
  1196. self progressBars ifEmpty: [ self remove ]
  1197. !
  1198. show
  1199. self isVisible ifFalse: [
  1200. visible := true.
  1201. self appendToJQuery: 'body' asJQuery ]
  1202. ! !
  1203. !HLProgress methodsFor: 'rendering'!
  1204. renderButtonsOn: html
  1205. !
  1206. renderMainOn: html
  1207. self progressBars do: [ :each |
  1208. html with: each ]
  1209. ! !
  1210. !HLProgress methodsFor: 'testing'!
  1211. isVisible
  1212. ^ visible ifNil: [ false ]
  1213. ! !
  1214. HLProgress class instanceVariableNames: 'default'!
  1215. !HLProgress class methodsFor: 'accessing'!
  1216. default
  1217. ^ default ifNil: [ default := self new ]
  1218. ! !
  1219. HLWidget subclass: #HLProgressBar
  1220. instanceVariableNames: 'label parent workBlock collection bar'
  1221. package: 'Helios-Core'!
  1222. !HLProgressBar commentStamp!
  1223. I am a widget used to display a progress bar while iterating over a collection.!
  1224. !HLProgressBar methodsFor: 'accessing'!
  1225. collection
  1226. ^ collection
  1227. !
  1228. collection: aCollection
  1229. collection := aCollection
  1230. !
  1231. label
  1232. ^ label
  1233. !
  1234. label: aString
  1235. label := aString
  1236. !
  1237. parent
  1238. ^ parent
  1239. !
  1240. parent: aProgress
  1241. parent := aProgress
  1242. !
  1243. workBlock
  1244. ^ workBlock
  1245. !
  1246. workBlock: aBlock
  1247. workBlock := aBlock
  1248. ! !
  1249. !HLProgressBar methodsFor: 'actions'!
  1250. evaluateAt: anInteger
  1251. self updateProgress: (anInteger / self collection size) * 100.
  1252. anInteger <= self collection size
  1253. ifTrue: [
  1254. [
  1255. self workBlock value: (self collection at: anInteger).
  1256. self evaluateAt: anInteger + 1 ] valueWithTimeout: 10 ]
  1257. ifFalse: [ [ self remove ] valueWithTimeout: 500 ]
  1258. !
  1259. remove
  1260. self parent removeProgressBar: self
  1261. !
  1262. start
  1263. "Make sure the UI has some time to update itself between each iteration"
  1264. self evaluateAt: 1
  1265. !
  1266. updateProgress: anInteger
  1267. bar asJQuery css: 'width' put: anInteger asString, '%'
  1268. ! !
  1269. !HLProgressBar methodsFor: 'rendering'!
  1270. renderContentOn: html
  1271. html span with: self label.
  1272. html div
  1273. class: 'progress';
  1274. with: [
  1275. bar := html div
  1276. class: 'bar';
  1277. style: 'width: 0%' ]
  1278. ! !
  1279. HLProgressBar class instanceVariableNames: 'default'!
  1280. !HLProgressBar class methodsFor: 'accessing'!
  1281. default
  1282. ^ default ifNil: [ default := self new ]
  1283. ! !
  1284. HLWidget subclass: #HLSUnit
  1285. instanceVariableNames: ''
  1286. package: 'Helios-Core'!
  1287. !HLSUnit class methodsFor: 'accessing'!
  1288. tabClass
  1289. ^ 'sunit'
  1290. !
  1291. tabLabel
  1292. ^ 'SUnit'
  1293. !
  1294. tabPriority
  1295. ^ 1000
  1296. ! !
  1297. !HLSUnit class methodsFor: 'testing'!
  1298. canBeOpenAsTab
  1299. ^ true
  1300. ! !