Helios-Core.st 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445
  1. Smalltalk createPackage: 'Helios-Core'!
  2. InterfacingObject 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. yourself).
  33. aBlock value.
  34. ]
  35. on: HLChangeForbidden
  36. do: [ :ex | ]
  37. ! !
  38. !HLModel methodsFor: 'testing'!
  39. isBrowserModel
  40. ^ false
  41. !
  42. isReferencesModel
  43. ^ false
  44. !
  45. isToolModel
  46. ^ false
  47. ! !
  48. HLModel subclass: #HLFinder
  49. instanceVariableNames: ''
  50. package: 'Helios-Core'!
  51. !HLFinder commentStamp!
  52. I am the `Finder` service handler of Helios.
  53. Finding a class will open a new class browser, while finding a method will open a references browser.!
  54. !HLFinder methodsFor: 'finding'!
  55. findClass: aClass
  56. HLBrowser openAsTab openClassNamed: aClass name
  57. !
  58. findMethod: aCompiledMethod
  59. HLBrowser openAsTab openMethod: aCompiledMethod
  60. !
  61. findString: aString
  62. | foundClass |
  63. foundClass := self environment classes
  64. detect: [ :each | each name = aString ]
  65. ifNone: [ nil ].
  66. foundClass
  67. ifNil: [ HLReferences openAsTab search: aString ]
  68. ifNotNil: [ self findClass: foundClass ]
  69. ! !
  70. HLModel subclass: #HLToolModel
  71. instanceVariableNames: 'selectedClass selectedPackage selectedProtocol selectedSelector'
  72. package: 'Helios-Core'!
  73. !HLToolModel commentStamp!
  74. I am a model specific to package and class manipulation. All browsers should either use me or a subclass as their model.
  75. I provide methods for package, class, protocol and method manipulation and access, forwarding to my environment.
  76. I also handle compilation of classes and methods as well as compilation and parsing errors.!
  77. !HLToolModel methodsFor: 'accessing'!
  78. allSelectors
  79. ^ self environment allSelectors
  80. !
  81. availableClassNames
  82. ^ self environment availableClassNames
  83. !
  84. availablePackageNames
  85. ^ self environment availablePackageNames
  86. !
  87. availablePackages
  88. ^ self environment availablePackageNames
  89. !
  90. availableProtocols
  91. ^ self environment availableProtocolsFor: self selectedClass
  92. !
  93. forceSelectedClass: aClass
  94. self withChangesDo: [
  95. self
  96. selectedClass: nil;
  97. selectedClass: aClass ]
  98. !
  99. forceSelectedMethod: aMethod
  100. self withChangesDo: [
  101. self
  102. selectedMethod: nil;
  103. selectedMethod: aMethod ]
  104. !
  105. forceSelectedPackage: aPackage
  106. self withChangesDo: [
  107. self
  108. selectedPackage: nil;
  109. selectedPackage: aPackage ]
  110. !
  111. forceSelectedProtocol: aProtocol
  112. self withChangesDo: [
  113. self
  114. selectedProtocol: nil;
  115. selectedProtocol: aProtocol ]
  116. !
  117. packageToCommit
  118. "Answer the package to commit depending on the context:
  119. - if a Method is selected, answer its package
  120. - else answer the `selectedPackage`"
  121. ^ self selectedMethod
  122. ifNil: [ self selectedPackage ]
  123. ifNotNil: [ :method | method package ]
  124. !
  125. packages
  126. ^ self environment packages
  127. !
  128. selectedClass
  129. ^ selectedClass
  130. !
  131. selectedClass: aClass
  132. (self selectedClass = aClass and: [ aClass isNil ])
  133. ifTrue: [ ^ self ].
  134. self withChangesDo: [
  135. aClass
  136. ifNil: [ selectedClass := nil ]
  137. ifNotNil: [
  138. self selectedPackage: aClass theNonMetaClass package.
  139. self showInstance
  140. ifTrue: [ selectedClass := aClass theNonMetaClass ]
  141. ifFalse: [ selectedClass := aClass theMetaClass ] ].
  142. selectedProtocol := nil.
  143. self selectedProtocol: self allProtocol.
  144. self announcer announce: (HLClassSelected on: self selectedClass) ]
  145. !
  146. selectedMethod
  147. ^ self selectedClass ifNotNil: [
  148. self selectedClass methodDictionary
  149. at: selectedSelector
  150. ifAbsent: [ nil ] ]
  151. !
  152. selectedMethod: aCompiledMethod
  153. selectedSelector = aCompiledMethod ifTrue: [ ^ self ].
  154. self withChangesDo: [
  155. aCompiledMethod
  156. ifNil: [ selectedSelector := nil ]
  157. ifNotNil: [
  158. selectedClass := aCompiledMethod methodClass.
  159. selectedPackage := selectedClass theNonMetaClass package.
  160. selectedSelector := aCompiledMethod selector ].
  161. self announcer announce: (HLMethodSelected on: aCompiledMethod) ]
  162. !
  163. selectedPackage
  164. ^ selectedPackage
  165. !
  166. selectedPackage: aPackage
  167. selectedPackage = aPackage ifTrue: [ ^ self ].
  168. self withChangesDo: [
  169. selectedPackage := aPackage.
  170. self selectedClass: nil.
  171. self announcer announce: (HLPackageSelected on: aPackage) ]
  172. !
  173. selectedProtocol
  174. ^ selectedProtocol
  175. !
  176. selectedProtocol: aString
  177. selectedProtocol = aString ifTrue: [ ^ self ].
  178. self withChangesDo: [
  179. selectedProtocol := aString.
  180. self selectedMethod: nil.
  181. self announcer announce: (HLProtocolSelected on: aString) ]
  182. ! !
  183. !HLToolModel methodsFor: 'actions'!
  184. addInstVarNamed: aString
  185. self environment addInstVarNamed: aString to: self selectedClass.
  186. self announcer announce: (HLInstVarAdded new
  187. theClass: self selectedClass;
  188. variableName: aString;
  189. yourself)
  190. !
  191. save: aString
  192. self announcer announce: HLSourceCodeSaved new.
  193. (self shouldCompileDefinition: aString)
  194. ifTrue: [ self compileClassDefinition: aString ]
  195. ifFalse: [ self compileMethod: aString ]
  196. !
  197. saveSourceCode
  198. self announcer announce: HLSaveSourceCode new
  199. ! !
  200. !HLToolModel methodsFor: 'commands actions'!
  201. commitPackageOnSuccess: aBlock onError: anotherBlock
  202. self environment
  203. commitPackage: self packageToCommit
  204. onSuccess: aBlock
  205. onError: anotherBlock
  206. !
  207. copyClassTo: aClassName
  208. self withChangesDo: [
  209. self environment
  210. copyClass: self selectedClass theNonMetaClass
  211. to: aClassName.
  212. self selectedClass: (self environment classNamed: aClassName) ]
  213. !
  214. moveClassToPackage: aPackageName
  215. self withChangesDo: [
  216. self environment
  217. moveClass: self selectedClass theNonMetaClass
  218. toPackage: aPackageName ]
  219. !
  220. moveMethodToClass: aClassName
  221. self withChangesDo: [
  222. self environment
  223. moveMethod: self selectedMethod
  224. toClass: aClassName ]
  225. !
  226. moveMethodToProtocol: aProtocol
  227. self withChangesDo: [
  228. self environment
  229. moveMethod: self selectedMethod
  230. toProtocol: aProtocol ]
  231. !
  232. openClassNamed: aString
  233. | class |
  234. self withChangesDo: [
  235. class := self environment classNamed: aString.
  236. self selectedPackage: class package.
  237. self selectedClass: class ]
  238. !
  239. removeClass
  240. self withChangesDo: [
  241. self manager
  242. confirm: 'Do you REALLY want to remove class ', self selectedClass theNonMetaClass name
  243. ifTrue: [ self environment removeClass: self selectedClass theNonMetaClass ] ]
  244. !
  245. removeMethod
  246. self withChangesDo: [
  247. self manager
  248. confirm: 'Do you REALLY want to remove method ', self selectedMethod methodClass name,' >> #', self selectedMethod selector
  249. ifTrue: [ self environment removeMethod: self selectedMethod ] ]
  250. !
  251. removeProtocol
  252. self withChangesDo: [
  253. self manager
  254. confirm: 'Do you REALLY want to remove protocol ', self selectedProtocol
  255. ifTrue: [ self environment
  256. removeProtocol: self selectedProtocol
  257. from: self selectedClass ] ]
  258. !
  259. renameClassTo: aClassName
  260. self withChangesDo: [
  261. self environment
  262. renameClass: self selectedClass theNonMetaClass
  263. to: aClassName ]
  264. !
  265. renamePackageTo: aPackageName
  266. self withChangesDo: [
  267. self environment
  268. renamePackage: self selectedPackage name
  269. to: aPackageName ]
  270. !
  271. renameProtocolTo: aString
  272. self withChangesDo: [
  273. self environment
  274. renameProtocol: self selectedProtocol
  275. to: aString
  276. in: self selectedClass ]
  277. ! !
  278. !HLToolModel methodsFor: 'compiling'!
  279. compileClassComment: aString
  280. self environment
  281. compileClassComment: aString
  282. for: self selectedClass
  283. !
  284. compileClassDefinition: aString
  285. self environment compileClassDefinition: aString
  286. !
  287. compileMethod: aString
  288. | method |
  289. self withCompileErrorHandling: [
  290. method := self environment
  291. compileMethod: aString
  292. for: self selectedClass
  293. protocol: self compilationProtocol.
  294. self selectedMethod: method ]
  295. ! !
  296. !HLToolModel methodsFor: 'defaults'!
  297. allProtocol
  298. ^ '-- all --'
  299. !
  300. unclassifiedProtocol
  301. ^ 'as yet unclassified'
  302. ! !
  303. !HLToolModel methodsFor: 'error handling'!
  304. handleCompileError: anError
  305. self announcer announce: (HLCompileErrorRaised new
  306. error: anError;
  307. yourself)
  308. !
  309. handleParseError: anError
  310. | split line column messageToInsert |
  311. split := anError messageText tokenize: ' : '.
  312. messageToInsert := split second.
  313. "21 = 'Parse error on line ' size + 1"
  314. split := split first copyFrom: 21 to: split first size.
  315. split := split tokenize: ' column '.
  316. line := split first.
  317. column := split second.
  318. self announcer announce: (HLParseErrorRaised new
  319. line: line asNumber;
  320. column: column asNumber;
  321. message: messageToInsert;
  322. error: anError;
  323. yourself)
  324. !
  325. handleUnkownVariableError: anError
  326. self announcer announce: (HLUnknownVariableErrorRaised new
  327. error: anError;
  328. yourself)
  329. !
  330. withCompileErrorHandling: aBlock
  331. self environment
  332. evaluate: [
  333. self environment
  334. evaluate: [
  335. self environment
  336. evaluate: aBlock
  337. on: ParseError
  338. do: [ :ex | self handleParseError: ex ] ]
  339. on: UnknownVariableError
  340. do: [ :ex | self handleUnkownVariableError: ex ] ]
  341. on: CompilerError
  342. do: [ :ex | self handleCompileError: ex ]
  343. ! !
  344. !HLToolModel methodsFor: 'private'!
  345. compilationProtocol
  346. | currentProtocol |
  347. currentProtocol := self selectedProtocol.
  348. currentProtocol ifNil: [ currentProtocol := self unclassifiedProtocol ].
  349. self selectedMethod ifNotNil: [ currentProtocol := self selectedMethod protocol ].
  350. ^ currentProtocol = self allProtocol
  351. ifTrue: [ self unclassifiedProtocol ]
  352. ifFalse: [ currentProtocol ]
  353. !
  354. withHelperLabelled: aString do: aBlock
  355. "TODO: doesn't belong here"
  356. '#helper' asJQuery remove.
  357. [ :html |
  358. html div
  359. id: 'helper';
  360. with: aString ] appendToJQuery: 'body' asJQuery.
  361. [
  362. aBlock value.
  363. '#helper' asJQuery remove
  364. ]
  365. valueWithTimeout: 10
  366. ! !
  367. !HLToolModel methodsFor: 'testing'!
  368. isToolModel
  369. ^ true
  370. !
  371. shouldCompileDefinition: aString
  372. ^ self selectedClass isNil or: [
  373. aString match: '^\s*[A-Z]' ]
  374. ! !
  375. !HLToolModel class methodsFor: 'actions'!
  376. on: anEnvironment
  377. ^ self new
  378. environment: anEnvironment;
  379. yourself
  380. ! !
  381. Object subclass: #HLProgressHandler
  382. instanceVariableNames: ''
  383. package: 'Helios-Core'!
  384. !HLProgressHandler commentStamp!
  385. I am a specific progress handler for Helios, displaying progresses in a modal window.!
  386. !HLProgressHandler methodsFor: 'progress handling'!
  387. do: aBlock on: aCollection displaying: aString
  388. HLProgressWidget default
  389. do: aBlock
  390. on: aCollection
  391. displaying: aString
  392. ! !
  393. Widget subclass: #HLWidget
  394. instanceVariableNames: 'wrapper'
  395. package: 'Helios-Core'!
  396. !HLWidget commentStamp!
  397. I am the abstract superclass of all Helios widgets.
  398. I provide common methods, additional behavior to widgets useful for Helios, like dialog creation, command execution and tab creation.
  399. ## API
  400. 1. Rendering
  401. Instead of overriding `#renderOn:` as with other Widget subclasses, my subclasses should override `#renderContentOn:`.
  402. 2. Refreshing
  403. To re-render a widget, use `#refresh`.
  404. 3. Key bindings registration and tabs
  405. When displayed as a tab, the widget has a chance to register keybindings with the `#registerBindingsOn:` hook method.
  406. 4. Unregistration
  407. 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.
  408. 5. Tabs
  409. 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.
  410. 6. Command execution
  411. An helios command (instance of `HLCommand` or one of its subclass) can be executed with `#execute:`.!
  412. !HLWidget methodsFor: 'accessing'!
  413. cssClass
  414. ^ 'hl_widget'
  415. !
  416. manager
  417. ^ HLManager current
  418. !
  419. removeTab
  420. self manager removeTabForWidget: self
  421. !
  422. setTabLabel: aString
  423. self manager announcer announce: (HLTabLabelChanged new
  424. widget: self;
  425. label: aString;
  426. yourself)
  427. !
  428. tabClass
  429. ^ self class tabClass
  430. !
  431. wrapper
  432. ^ wrapper
  433. ! !
  434. !HLWidget methodsFor: 'actions'!
  435. confirm: aString ifTrue: aBlock
  436. self manager confirm: aString ifTrue: aBlock
  437. !
  438. confirm: aString ifTrue: aBlock ifFalse: anotherBlock
  439. self manager
  440. confirm: aString
  441. ifTrue: aBlock
  442. ifFalse: anotherBlock
  443. !
  444. execute: aCommand
  445. HLManager current keyBinder
  446. activate;
  447. applyBinding: aCommand asBinding
  448. !
  449. inform: aString
  450. self manager inform: aString
  451. !
  452. openAsTab
  453. (HLTabWidget on: self labelled: self defaultTabLabel)
  454. add
  455. !
  456. request: aString do: aBlock
  457. self manager request: aString do: aBlock
  458. !
  459. request: aString value: valueString do: aBlock
  460. self manager
  461. request: aString
  462. value: valueString
  463. do: aBlock
  464. !
  465. unregister
  466. "This method is called whenever the receiver is closed (as a tab).
  467. Widgets subscribing to announcements should unregister there"
  468. ! !
  469. !HLWidget methodsFor: 'defaults'!
  470. defaultTabLabel
  471. ^ self class tabLabel
  472. ! !
  473. !HLWidget methodsFor: 'keybindings'!
  474. bindKeyDown: keyDownBlock keyUp: keyUpBlock
  475. self wrapper asJQuery
  476. keydown: keyDownBlock;
  477. keyup: keyUpBlock
  478. !
  479. registerBindings
  480. self registerBindingsOn: self manager keyBinder bindings
  481. !
  482. registerBindingsOn: aBindingGroup
  483. !
  484. unbindKeyDownKeyUp
  485. self wrapper asJQuery
  486. unbind: 'keydown';
  487. unbind: 'keyup'
  488. ! !
  489. !HLWidget methodsFor: 'rendering'!
  490. renderContentOn: html
  491. !
  492. renderOn: html
  493. wrapper := html div
  494. class: self cssClass;
  495. yourself.
  496. [ :renderer | self renderContentOn: renderer ] appendToJQuery: wrapper asJQuery
  497. ! !
  498. !HLWidget methodsFor: 'testing'!
  499. canHaveFocus
  500. ^ false
  501. ! !
  502. !HLWidget methodsFor: 'updating'!
  503. refresh
  504. self wrapper ifNil: [ ^ self ].
  505. self wrapper asJQuery empty.
  506. [ :html | self renderContentOn: html ] appendToJQuery: self wrapper asJQuery
  507. ! !
  508. !HLWidget class methodsFor: 'accessing'!
  509. openAsTab
  510. | instance |
  511. instance := self new.
  512. (HLTabWidget
  513. on: instance
  514. labelled: instance defaultTabLabel) add.
  515. ^ instance
  516. !
  517. tabClass
  518. ^ ''
  519. !
  520. tabLabel
  521. ^ 'Tab'
  522. !
  523. tabPriority
  524. ^ 500
  525. ! !
  526. !HLWidget class methodsFor: 'testing'!
  527. canBeOpenAsTab
  528. ^ false
  529. ! !
  530. HLWidget subclass: #HLFocusableWidget
  531. instanceVariableNames: ''
  532. package: 'Helios-Core'!
  533. !HLFocusableWidget commentStamp!
  534. I am a widget that can be focused.
  535. ## API
  536. Instead of overriding `#renderOn:` as with other `Widget` subclasses, my subclasses should override `#renderContentOn:`.
  537. To bring the focus to the widget, use the `#focus` method.!
  538. !HLFocusableWidget methodsFor: 'accessing'!
  539. focusClass
  540. ^ 'focused'
  541. ! !
  542. !HLFocusableWidget methodsFor: 'events'!
  543. blur
  544. self wrapper asJQuery blur
  545. !
  546. focus
  547. self wrapper asJQuery focus
  548. ! !
  549. !HLFocusableWidget methodsFor: 'rendering'!
  550. renderContentOn: html
  551. !
  552. renderOn: html
  553. wrapper := html div
  554. class: self cssClass;
  555. yourself.
  556. wrapper with: [ self renderContentOn: html ].
  557. wrapper
  558. at: 'tabindex' put: '0';
  559. onBlur: [ self wrapper asJQuery removeClass: self focusClass ];
  560. onFocus: [ self wrapper asJQuery addClass: self focusClass ]
  561. ! !
  562. !HLFocusableWidget methodsFor: 'testing'!
  563. canHaveFocus
  564. ^ true
  565. !
  566. hasFocus
  567. ^ self wrapper notNil and: [ self wrapper asJQuery hasClass: self focusClass ]
  568. ! !
  569. HLFocusableWidget subclass: #HLListWidget
  570. instanceVariableNames: 'items selectedItem'
  571. package: 'Helios-Core'!
  572. !HLListWidget methodsFor: 'accessing'!
  573. activeItemCssClass
  574. ^'active'
  575. !
  576. buttonsDivCssClass
  577. ^ 'pane_actions form-group'
  578. !
  579. cssClassForItem: anObject
  580. ^ ''
  581. !
  582. findListItemFor: anObject
  583. ^ (((wrapper asJQuery find: 'li')
  584. filter: [ :thisArg :otherArg | (thisArg asJQuery data: 'item') = anObject ] currySelf) eq: 0)
  585. !
  586. items
  587. ^ items ifNil: [ items := self defaultItems ]
  588. !
  589. items: aCollection
  590. items := aCollection
  591. !
  592. listCssClass
  593. ^'nav nav-pills nav-stacked'
  594. !
  595. listCssClassForItem: anObject
  596. ^ self selectedItem = anObject
  597. ifTrue: [ self activeItemCssClass ]
  598. ifFalse: [ 'inactive' ]
  599. !
  600. positionOf: aListItem
  601. <
  602. return aListItem.parent().children().get().indexOf(aListItem.get(0)) + 1
  603. >
  604. !
  605. selectedItem
  606. ^ selectedItem
  607. !
  608. selectedItem: anObject
  609. selectedItem := anObject
  610. ! !
  611. !HLListWidget methodsFor: 'actions'!
  612. activateFirstListItem
  613. self activateListItem: ((wrapper asJQuery find: 'li.inactive') eq: 0)
  614. !
  615. activateItem: anObject
  616. self activateListItem: (self findListItemFor: anObject)
  617. !
  618. activateListItem: aListItem
  619. | item |
  620. (aListItem get: 0) ifNil: [ ^ self ].
  621. aListItem parent children removeClass: self activeItemCssClass.
  622. aListItem addClass: self activeItemCssClass.
  623. self ensureVisible: aListItem.
  624. "Activate the corresponding item"
  625. item := aListItem data: 'item'.
  626. self selectedItem == item ifFalse: [
  627. self selectItem: item ]
  628. !
  629. activateNextListItem
  630. self activateListItem: (self wrapper asJQuery find: ('li.', self activeItemCssClass)) next.
  631. "select the first item if none is selected"
  632. (self wrapper asJQuery find: (' .', self activeItemCssClass)) get ifEmpty: [
  633. self activateFirstListItem ]
  634. !
  635. activatePreviousListItem
  636. self activateListItem: (self wrapper asJQuery find: ('li.', self activeItemCssClass)) prev
  637. !
  638. ensureVisible: aListItem
  639. "Move the scrollbar to show the active element"
  640. | parent position |
  641. (aListItem get: 0) ifNil: [ ^ self ].
  642. position := self positionOf: aListItem.
  643. parent := aListItem parent.
  644. aListItem position top < 0 ifTrue: [
  645. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem position top - 10) ].
  646. aListItem position top + aListItem height > parent height ifTrue: [
  647. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem height - (parent height - aListItem position top)) +10 ]
  648. !
  649. focus
  650. super focus.
  651. self items isEmpty ifFalse: [
  652. self selectedItem ifNil: [ self activateFirstListItem ] ]
  653. !
  654. reactivateListItem: aListItem
  655. self activateListItem: aListItem.
  656. self reselectItem: self selectedItem
  657. !
  658. refresh
  659. super refresh.
  660. self selectedItem ifNotNil: [self ensureVisible: (self findListItemFor: self selectedItem)].
  661. !
  662. reselectItem: anObject
  663. !
  664. selectItem: anObject
  665. self selectedItem: anObject
  666. ! !
  667. !HLListWidget methodsFor: 'defaults'!
  668. defaultItems
  669. ^ #()
  670. ! !
  671. !HLListWidget methodsFor: 'events'!
  672. setupKeyBindings
  673. (HLRepeatedKeyDownHandler on: self)
  674. whileKeyDown: 38 do: [ self activatePreviousListItem ];
  675. whileKeyDown: 40 do: [ self activateNextListItem ];
  676. rebindKeys.
  677. self wrapper asJQuery keydown: [ :e |
  678. e which = 13 ifTrue: [
  679. self reselectItem: self selectedItem ] ]
  680. ! !
  681. !HLListWidget methodsFor: 'rendering'!
  682. renderButtonsOn: html
  683. !
  684. renderContentOn: html
  685. html ul
  686. class: self listCssClass;
  687. with: [ self renderListOn: html ];
  688. onClick: [ self focus ].
  689. html div class: self buttonsDivCssClass; with: [
  690. self renderButtonsOn: html ].
  691. self setupKeyBindings
  692. !
  693. renderItem: anObject on: html
  694. | li |
  695. li := html li.
  696. li asJQuery data: 'item' put: anObject.
  697. li
  698. class: (self listCssClassForItem: anObject);
  699. with: [
  700. html a
  701. with: [
  702. (html tag: 'i') class: (self cssClassForItem: anObject).
  703. self renderItemLabel: anObject on: html ];
  704. onClick: [
  705. self reactivateListItem: li asJQuery ] ]
  706. !
  707. renderItemLabel: anObject on: html
  708. html with: anObject asString
  709. !
  710. renderListOn: html
  711. self items do: [ :each |
  712. self renderItem: each on: html ]
  713. ! !
  714. HLListWidget subclass: #HLNavigationListWidget
  715. instanceVariableNames: 'previous next'
  716. package: 'Helios-Core'!
  717. !HLNavigationListWidget methodsFor: 'accessing'!
  718. next
  719. ^ next
  720. !
  721. next: aWidget
  722. next := aWidget.
  723. aWidget previous = self ifFalse: [ aWidget previous: self ]
  724. !
  725. previous
  726. ^ previous
  727. !
  728. previous: aWidget
  729. previous := aWidget.
  730. aWidget next = self ifFalse: [ aWidget next: self ]
  731. ! !
  732. !HLNavigationListWidget methodsFor: 'actions'!
  733. nextFocus
  734. self next ifNotNil: [ self next focus ]
  735. !
  736. previousFocus
  737. self previous ifNotNil: [ self previous focus ]
  738. ! !
  739. !HLNavigationListWidget methodsFor: 'events'!
  740. setupKeyBindings
  741. super setupKeyBindings.
  742. self wrapper asJQuery keydown: [ :e |
  743. e which = 39 ifTrue: [
  744. self nextFocus ].
  745. e which = 37 ifTrue: [
  746. self previousFocus ] ]
  747. ! !
  748. HLNavigationListWidget subclass: #HLToolListWidget
  749. instanceVariableNames: 'model'
  750. package: 'Helios-Core'!
  751. !HLToolListWidget methodsFor: 'accessing'!
  752. commandCategory
  753. ^ self label
  754. !
  755. label
  756. ^ 'List'
  757. !
  758. menuCommands
  759. "Answer a collection of commands to be put in the cog menu"
  760. ^ ((HLToolCommand concreteClasses
  761. select: [ :each | each isValidFor: self model ])
  762. collect: [ :each | each for: self model ])
  763. select: [ :each |
  764. each category = self commandCategory and: [
  765. each isAction and: [ each isActive ] ] ]
  766. !
  767. model
  768. ^ model
  769. !
  770. model: aBrowserModel
  771. model := aBrowserModel.
  772. self
  773. observeSystem;
  774. observeModel
  775. !
  776. selectedItem: anItem
  777. "Selection changed, update the cog menu"
  778. super selectedItem: anItem.
  779. self updateMenu
  780. ! !
  781. !HLToolListWidget methodsFor: 'actions'!
  782. activateListItem: anItem
  783. self model withChangesDo: [ super activateListItem: anItem ]
  784. !
  785. activateNextListItem
  786. self model withChangesDo: [ super activateNextListItem ]
  787. !
  788. activatePreviousListItem
  789. self model withChangesDo: [ super activatePreviousListItem ]
  790. !
  791. observeModel
  792. !
  793. observeSystem
  794. !
  795. reactivateListItem: anItem
  796. self model withChangesDo: [ super reactivateListItem: anItem ]
  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 btn-default dropdown-toggle';
  824. at: 'data-toggle' put: 'dropdown';
  825. with: [ (html tag: 'i') class: 'glyphicon glyphicon-chevron-down' ].
  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. HLListWidget subclass: #HLTabListWidget
  847. instanceVariableNames: 'callback'
  848. package: 'Helios-Core'!
  849. !HLTabListWidget commentStamp!
  850. I am a widget used to display a list of helios tabs.
  851. When a tab is selected, `callback` is evaluated with the selected tab as argument.!
  852. !HLTabListWidget methodsFor: 'accessing'!
  853. callback
  854. ^ callback ifNil: [ [] ]
  855. !
  856. callback: aBlock
  857. callback := aBlock
  858. ! !
  859. !HLTabListWidget methodsFor: 'actions'!
  860. selectItem: aTab
  861. super selectItem: aTab.
  862. self callback value: aTab
  863. ! !
  864. !HLTabListWidget methodsFor: 'rendering'!
  865. renderItemLabel: aTab on: html
  866. html span
  867. class: aTab cssClass;
  868. with: aTab label
  869. ! !
  870. HLWidget subclass: #HLInformationWidget
  871. instanceVariableNames: 'informationString'
  872. package: 'Helios-Core'!
  873. !HLInformationWidget commentStamp!
  874. I display an information dialog.
  875. ## API
  876. `HLWidget >> #inform:` is a convenience method for creating information dialogs.!
  877. !HLInformationWidget methodsFor: 'accessing'!
  878. informationString
  879. ^ informationString ifNil: [ '' ]
  880. !
  881. informationString: anObject
  882. informationString := anObject
  883. ! !
  884. !HLInformationWidget methodsFor: 'actions'!
  885. remove
  886. [
  887. self wrapper asJQuery fadeOut: 100.
  888. [ self wrapper asJQuery remove ]
  889. valueWithTimeout: 400.
  890. ]
  891. valueWithTimeout: 1500
  892. !
  893. show
  894. self appendToJQuery: 'body' asJQuery
  895. ! !
  896. !HLInformationWidget methodsFor: 'rendering'!
  897. renderContentOn: html
  898. html div
  899. class: 'growl';
  900. with: self informationString.
  901. self remove
  902. ! !
  903. HLWidget subclass: #HLManager
  904. instanceVariableNames: 'tabsWidget environment history announcer rendered'
  905. package: 'Helios-Core'!
  906. !HLManager commentStamp!
  907. HLManager is the entry point Class of Helios.
  908. Its `singleton` instance is created on startup, and rendered on body.!
  909. !HLManager methodsFor: 'accessing'!
  910. activeTab
  911. ^ self tabsWidget activeTab
  912. !
  913. announcer
  914. ^ announcer ifNil: [ announcer := Announcer new ]
  915. !
  916. environment
  917. "The default environment used by all Helios objects"
  918. ^ environment ifNil: [ environment := self defaultEnvironment ]
  919. !
  920. environment: anEnvironment
  921. environment := anEnvironment
  922. !
  923. history
  924. ^ history ifNil: [ history := OrderedCollection new ]
  925. !
  926. history: aCollection
  927. history := aCollection
  928. !
  929. keyBinder
  930. ^ HLKeyBinder current
  931. !
  932. setEditorTheme: aTheme
  933. 'helios.editorTheme' asSetting value: aTheme
  934. !
  935. setTheme: aTheme
  936. | currentTheme |
  937. currentTheme := 'helios.theme' asSettingIfAbsent: 'default'.
  938. 'body' asJQuery
  939. removeClass: currentTheme value;
  940. addClass: aTheme.
  941. 'helios.theme' asSetting value: aTheme
  942. !
  943. tabWidth
  944. ^ (window asJQuery width - 90) / self tabs size
  945. !
  946. tabs
  947. ^ self tabsWidget tabs
  948. !
  949. tabsWidget
  950. ^ tabsWidget ifNil: [ tabsWidget := HLTabsWidget new ]
  951. ! !
  952. !HLManager methodsFor: 'actions'!
  953. activate: aTab
  954. self tabsWidget activate: aTab
  955. !
  956. addTab: aTab
  957. self tabsWidget addTab: aTab
  958. !
  959. confirm: aString ifFalse: aBlock
  960. self
  961. confirm: aString
  962. ifTrue: []
  963. ifFalse: aBlock
  964. !
  965. confirm: aString ifTrue: aBlock
  966. self
  967. confirm: aString
  968. ifTrue: aBlock
  969. ifFalse: []
  970. !
  971. confirm: aString ifTrue: aBlock ifFalse: anotherBlock
  972. HLConfirmationWidget new
  973. confirmationString: aString;
  974. actionBlock: aBlock;
  975. cancelBlock: anotherBlock;
  976. show
  977. !
  978. handleLossOfEnvironmentWithParent: parent
  979. parent at: 'onunload' put: [
  980. self removeBeforeUnloadMessage.
  981. window close ]
  982. !
  983. inform: aString
  984. HLInformationWidget new
  985. informationString: aString;
  986. show
  987. !
  988. removeActiveTab
  989. self tabsWidget removeActiveTab
  990. !
  991. removeBeforeUnloadMessage
  992. <window.onbeforeunload = null>
  993. !
  994. removeTabForWidget: aWidget
  995. self tabsWidget removeTabForWidget: aWidget
  996. !
  997. request: aString do: aBlock
  998. self
  999. request: aString
  1000. value: ''
  1001. do: aBlock
  1002. !
  1003. request: aString value: valueString do: aBlock
  1004. HLRequestWidget new
  1005. confirmationString: aString;
  1006. actionBlock: aBlock;
  1007. value: valueString;
  1008. show
  1009. ! !
  1010. !HLManager methodsFor: 'defaults'!
  1011. defaultEnvironment
  1012. "If helios is loaded from within a frame, answer the parent window environment"
  1013. | parent parentSmalltalkGlobals |
  1014. parent := window opener ifNil: [ window parent ].
  1015. parent ifNil: [ ^ Environment new ].
  1016. [ parentSmalltalkGlobals := ((parent at: 'requirejs') value: 'amber/boot') at: 'globals' ]
  1017. on: Error do: [ parentSmalltalkGlobals := (parent at: 'requirejs') value: 'amber_vm/globals' ].
  1018. parentSmalltalkGlobals ifNil: [ ^ Environment new ].
  1019. self handleLossOfEnvironmentWithParent: parent.
  1020. ^ (parentSmalltalkGlobals at: 'Environment') new
  1021. ! !
  1022. !HLManager methodsFor: 'initialization'!
  1023. initialize
  1024. super initialize.
  1025. rendered := false
  1026. !
  1027. setup
  1028. self
  1029. registerServices;
  1030. setupEvents.
  1031. self keyBinder setupEvents.
  1032. self tabsWidget setupEvents.
  1033. self setupTheme.
  1034. '#helper' asJQuery fadeOut
  1035. ! !
  1036. !HLManager methodsFor: 'private'!
  1037. registerServices
  1038. self
  1039. registerInspector;
  1040. registerErrorHandler;
  1041. registerProgressHandler;
  1042. registerTranscript;
  1043. registerFinder
  1044. !
  1045. setupEvents
  1046. 'body' asJQuery keydown: [ :event |
  1047. "On ctrl keydown, adds a 'navigation' css class to <body>
  1048. for the CodeMirror navigation links. See `HLCodeWidget`."
  1049. event ctrlKey ifTrue: [
  1050. 'body' asJQuery addClass: 'navigation' ] ].
  1051. 'body' asJQuery keyup: [ :event |
  1052. 'body' asJQuery removeClass: 'navigation' ].
  1053. window asJQuery resize: [ :event |
  1054. self refresh ]
  1055. !
  1056. setupTheme
  1057. "self
  1058. setTheme: 'niflheim';
  1059. setEditorTheme: 'niflheim'."
  1060. self
  1061. setTheme: 'default';
  1062. setEditorTheme: 'default'.
  1063. ! !
  1064. !HLManager methodsFor: 'rendering'!
  1065. renderContentOn: html
  1066. html with: self tabsWidget.
  1067. html with: HLWelcomeWidget new.
  1068. self renderDefaultTabs.
  1069. rendered := true
  1070. !
  1071. renderDefaultTabs
  1072. rendered ifFalse: [
  1073. HLWorkspace openAsTab.
  1074. HLBrowser openAsTab ]
  1075. ! !
  1076. !HLManager methodsFor: 'services'!
  1077. registerErrorHandler
  1078. self environment registerErrorHandler: HLErrorHandler new.
  1079. ErrorHandler register: HLErrorHandler new
  1080. !
  1081. registerFinder
  1082. self environment registerFinder: HLFinder new.
  1083. Finder register: HLFinder new
  1084. !
  1085. registerInspector
  1086. self environment registerInspector: HLInspector.
  1087. Inspector register: HLInspector
  1088. !
  1089. registerProgressHandler
  1090. self environment registerProgressHandler: HLProgressHandler new.
  1091. ProgressHandler register: HLProgressHandler new
  1092. !
  1093. registerTranscript
  1094. self environment registerTranscript: HLTranscriptHandler
  1095. ! !
  1096. HLManager class instanceVariableNames: 'current'!
  1097. !HLManager class methodsFor: 'accessing'!
  1098. current
  1099. ^ current ifNil: [ current := self basicNew initialize ]
  1100. ! !
  1101. !HLManager class methodsFor: 'initialization'!
  1102. setup
  1103. self current
  1104. setup;
  1105. appendToJQuery: 'body' asJQuery.
  1106. ('helios.confirmOnExit' settingValueIfAbsent: true) ifTrue: [
  1107. window onbeforeunload: [ 'Do you want to close Amber? All uncommitted changes will be lost.' ] ]
  1108. ! !
  1109. !HLManager class methodsFor: 'instance creation'!
  1110. new
  1111. "Use current instead"
  1112. self shouldNotImplement
  1113. ! !
  1114. HLWidget subclass: #HLModalWidget
  1115. instanceVariableNames: ''
  1116. package: 'Helios-Core'!
  1117. !HLModalWidget commentStamp!
  1118. I implement an abstract modal widget.!
  1119. !HLModalWidget methodsFor: 'actions'!
  1120. remove
  1121. '.dialog' asJQuery removeClass: 'active'.
  1122. [
  1123. '#overlay' asJQuery remove.
  1124. wrapper asJQuery remove
  1125. ] valueWithTimeout: 300
  1126. !
  1127. show
  1128. self appendToJQuery: 'body' asJQuery
  1129. ! !
  1130. !HLModalWidget methodsFor: 'private'!
  1131. giveFocusToButton: aButton
  1132. aButton asJQuery focus
  1133. ! !
  1134. !HLModalWidget methodsFor: 'rendering'!
  1135. hasButtons
  1136. ^ true
  1137. !
  1138. renderButtonsOn: html
  1139. !
  1140. renderContentOn: html
  1141. | confirmButton |
  1142. html div id: 'overlay'.
  1143. html div
  1144. class: 'dialog ', self cssClass;
  1145. with: [
  1146. self renderMainOn: html.
  1147. self hasButtons ifTrue: [
  1148. self renderButtonsOn: html ] ].
  1149. '.dialog' asJQuery addClass: 'active'.
  1150. self setupKeyBindings
  1151. !
  1152. renderMainOn: html
  1153. !
  1154. setupKeyBindings
  1155. '.dialog' asJQuery keyup: [ :e |
  1156. e keyCode = String esc asciiValue ifTrue: [ self cancel ] ]
  1157. ! !
  1158. HLModalWidget subclass: #HLConfirmationWidget
  1159. instanceVariableNames: 'cancelButtonLabel confirmButtonLabel confirmationString actionBlock cancelBlock'
  1160. package: 'Helios-Core'!
  1161. !HLConfirmationWidget commentStamp!
  1162. I display confirmation dialog.
  1163. ## API
  1164. HLWidget contains convenience methods like `HLWidget >> #confirm:ifTrue:` for creating confirmation dialogs.!
  1165. !HLConfirmationWidget methodsFor: 'accessing'!
  1166. actionBlock
  1167. ^ actionBlock ifNil: [ [] ]
  1168. !
  1169. actionBlock: aBlock
  1170. actionBlock := aBlock
  1171. !
  1172. cancelBlock
  1173. ^ cancelBlock ifNil: [ [] ]
  1174. !
  1175. cancelBlock: aBlock
  1176. cancelBlock := aBlock
  1177. !
  1178. cancelButtonLabel
  1179. ^ cancelButtonLabel ifNil: [ 'Cancel' ]
  1180. !
  1181. cancelButtonLabel: aString
  1182. ^ cancelButtonLabel := aString
  1183. !
  1184. confirmButtonLabel
  1185. ^ confirmButtonLabel ifNil: [ 'Confirm' ]
  1186. !
  1187. confirmButtonLabel: aString
  1188. ^ confirmButtonLabel := aString
  1189. !
  1190. confirmationString
  1191. ^ confirmationString ifNil: [ 'Confirm' ]
  1192. !
  1193. confirmationString: aString
  1194. confirmationString := aString
  1195. ! !
  1196. !HLConfirmationWidget methodsFor: 'actions'!
  1197. cancel
  1198. self cancelBlock value.
  1199. self remove
  1200. !
  1201. confirm
  1202. self remove.
  1203. self actionBlock value
  1204. ! !
  1205. !HLConfirmationWidget methodsFor: 'rendering'!
  1206. renderButtonsOn: html
  1207. | confirmButton |
  1208. html div
  1209. class: 'buttons';
  1210. with: [
  1211. html button
  1212. class: 'button';
  1213. with: self cancelButtonLabel;
  1214. onClick: [ self cancel ].
  1215. confirmButton := html button
  1216. class: 'button default';
  1217. with: self confirmButtonLabel;
  1218. onClick: [ self confirm ] ].
  1219. self giveFocusToButton:confirmButton
  1220. !
  1221. renderMainOn: html
  1222. html span
  1223. class: 'head';
  1224. with: self confirmationString
  1225. ! !
  1226. HLConfirmationWidget subclass: #HLRequestWidget
  1227. instanceVariableNames: 'input multiline value'
  1228. package: 'Helios-Core'!
  1229. !HLRequestWidget commentStamp!
  1230. I display a modal window requesting user input.
  1231. ## API
  1232. `HLWidget >> #request:do:` and `#request:value:do:` are convenience methods for creating modal request dialogs.!
  1233. !HLRequestWidget methodsFor: 'accessing'!
  1234. beMultiline
  1235. multiline := true
  1236. !
  1237. beSingleline
  1238. multiline := false
  1239. !
  1240. cssClass
  1241. ^ 'large'
  1242. !
  1243. value
  1244. ^ value ifNil: [ '' ]
  1245. !
  1246. value: aString
  1247. value := aString
  1248. ! !
  1249. !HLRequestWidget methodsFor: 'actions'!
  1250. confirm
  1251. | val |
  1252. val := input asJQuery val.
  1253. self remove.
  1254. self actionBlock value: val
  1255. ! !
  1256. !HLRequestWidget methodsFor: 'private'!
  1257. giveFocusToButton: aButton
  1258. ! !
  1259. !HLRequestWidget methodsFor: 'rendering'!
  1260. renderMainOn: html
  1261. super renderMainOn: html.
  1262. self isMultiline
  1263. ifTrue: [ input := html textarea ]
  1264. ifFalse: [ input := html input
  1265. type: 'text';
  1266. onKeyDown: [ :event |
  1267. event keyCode = 13 ifTrue: [
  1268. self confirm ] ];
  1269. yourself ].
  1270. input asJQuery
  1271. val: self value;
  1272. focus
  1273. ! !
  1274. !HLRequestWidget methodsFor: 'testing'!
  1275. isMultiline
  1276. ^ multiline ifNil: [ true ]
  1277. ! !
  1278. HLModalWidget subclass: #HLProgressWidget
  1279. instanceVariableNames: 'progressBars visible'
  1280. package: 'Helios-Core'!
  1281. !HLProgressWidget commentStamp!
  1282. I am a widget used to display progress modal dialogs.
  1283. My default instance is accessed with `HLProgressWidget class >> #default`.
  1284. See `HLProgressHandler` for usage.!
  1285. !HLProgressWidget methodsFor: 'accessing'!
  1286. progressBars
  1287. ^ progressBars ifNil: [ progressBars := OrderedCollection new ]
  1288. ! !
  1289. !HLProgressWidget methodsFor: 'actions'!
  1290. addProgressBar: aProgressBar
  1291. self show.
  1292. self progressBars add: aProgressBar.
  1293. aProgressBar appendToJQuery: (self wrapper asJQuery find: '.dialog')
  1294. !
  1295. do: aBlock on: aCollection displaying: aString
  1296. | progressBar |
  1297. progressBar := HLProgressBarWidget new
  1298. parent: self;
  1299. label: aString;
  1300. workBlock: aBlock;
  1301. collection: aCollection;
  1302. yourself.
  1303. self addProgressBar: progressBar.
  1304. progressBar start
  1305. !
  1306. flush
  1307. self progressBars do: [ :each |
  1308. self removeProgressBar: each ]
  1309. !
  1310. remove
  1311. self isVisible ifTrue: [
  1312. visible := false.
  1313. super remove ]
  1314. !
  1315. removeProgressBar: aProgressBar
  1316. self progressBars remove: aProgressBar ifAbsent: [].
  1317. aProgressBar wrapper asJQuery remove.
  1318. self progressBars ifEmpty: [ self remove ]
  1319. !
  1320. show
  1321. self isVisible ifFalse: [
  1322. visible := true.
  1323. super show ]
  1324. ! !
  1325. !HLProgressWidget methodsFor: 'rendering'!
  1326. renderMainOn: html
  1327. self progressBars do: [ :each |
  1328. html with: each ]
  1329. ! !
  1330. !HLProgressWidget methodsFor: 'testing'!
  1331. hasButtons
  1332. ^ false
  1333. !
  1334. isVisible
  1335. ^ visible ifNil: [ false ]
  1336. ! !
  1337. HLProgressWidget class instanceVariableNames: 'default'!
  1338. !HLProgressWidget class methodsFor: 'accessing'!
  1339. default
  1340. ^ default ifNil: [ default := self new ]
  1341. ! !
  1342. HLModalWidget subclass: #HLTabSelectionWidget
  1343. instanceVariableNames: 'tabs tabList selectedTab selectCallback cancelCallback confirmCallback'
  1344. package: 'Helios-Core'!
  1345. !HLTabSelectionWidget commentStamp!
  1346. I am a modal window used to select or create tabs.!
  1347. !HLTabSelectionWidget methodsFor: 'accessing'!
  1348. cancelCallback
  1349. ^ cancelCallback ifNil: [ [] ]
  1350. !
  1351. cancelCallback: aBlock
  1352. cancelCallback := aBlock
  1353. !
  1354. confirmCallback
  1355. ^ confirmCallback ifNil: [ [] ]
  1356. !
  1357. confirmCallback: aBlock
  1358. confirmCallback := aBlock
  1359. !
  1360. selectCallback
  1361. ^ selectCallback ifNil: [ [] ]
  1362. !
  1363. selectCallback: aBlock
  1364. selectCallback := aBlock
  1365. !
  1366. selectedTab
  1367. ^ selectedTab
  1368. !
  1369. selectedTab: aTab
  1370. selectedTab := aTab
  1371. !
  1372. tabs
  1373. ^ tabs ifNil: [ #() ]
  1374. !
  1375. tabs: aCollection
  1376. tabs := aCollection
  1377. ! !
  1378. !HLTabSelectionWidget methodsFor: 'actions'!
  1379. cancel
  1380. self remove.
  1381. self cancelCallback value
  1382. !
  1383. confirm
  1384. self remove.
  1385. self confirmCallback value: self selectedTab
  1386. !
  1387. selectTab: aTab
  1388. self selectedTab: aTab.
  1389. self selectCallback value: aTab
  1390. !
  1391. setupKeyBindings
  1392. super setupKeyBindings.
  1393. '.dialog' asJQuery keyup: [ :e |
  1394. e keyCode = String cr asciiValue ifTrue: [ self confirm ] ]
  1395. ! !
  1396. !HLTabSelectionWidget methodsFor: 'rendering'!
  1397. renderButtonsOn: html
  1398. | confirmButton |
  1399. html div
  1400. class: 'buttons';
  1401. with: [
  1402. html button
  1403. class: 'button';
  1404. with: 'Cancel';
  1405. onClick: [ self cancel ].
  1406. confirmButton := html button
  1407. class: 'button default';
  1408. with: 'Select tab';
  1409. onClick: [ self confirm ] ].
  1410. self giveFocusToButton:confirmButton
  1411. !
  1412. renderContentOn: html
  1413. super renderContentOn: html.
  1414. self tabList focus
  1415. !
  1416. renderMainOn: html
  1417. html div
  1418. class: 'title';
  1419. with: 'Tab selection'.
  1420. html with: self tabList
  1421. !
  1422. renderTab: aTab on: html
  1423. html
  1424. span
  1425. class: aTab cssClass;
  1426. with: aTab label
  1427. !
  1428. renderTabsOn: html
  1429. self tabs do: [ :each |
  1430. html li with: [
  1431. html a
  1432. with: [
  1433. self renderTab: each on: html ];
  1434. onClick: [ self selectTab: each ] ] ]
  1435. !
  1436. tabList
  1437. tabList ifNil: [
  1438. tabList := HLTabListWidget new.
  1439. tabList
  1440. callback: [ :tab | self selectTab: tab. tabList focus ];
  1441. selectedItem: self selectedTab;
  1442. items: self tabs ].
  1443. ^ tabList
  1444. ! !
  1445. HLWidget subclass: #HLProgressBarWidget
  1446. instanceVariableNames: 'label parent workBlock collection bar'
  1447. package: 'Helios-Core'!
  1448. !HLProgressBarWidget commentStamp!
  1449. I am a widget used to display a progress bar while iterating over a collection.!
  1450. !HLProgressBarWidget methodsFor: 'accessing'!
  1451. collection
  1452. ^ collection
  1453. !
  1454. collection: aCollection
  1455. collection := aCollection
  1456. !
  1457. label
  1458. ^ label
  1459. !
  1460. label: aString
  1461. label := aString
  1462. !
  1463. parent
  1464. ^ parent
  1465. !
  1466. parent: aProgress
  1467. parent := aProgress
  1468. !
  1469. workBlock
  1470. ^ workBlock
  1471. !
  1472. workBlock: aBlock
  1473. workBlock := aBlock
  1474. ! !
  1475. !HLProgressBarWidget methodsFor: 'actions'!
  1476. evaluateAt: anInteger
  1477. self updateProgress: (anInteger / self collection size) * 100.
  1478. anInteger <= self collection size
  1479. ifTrue: [
  1480. [
  1481. self workBlock value: (self collection at: anInteger).
  1482. self evaluateAt: anInteger + 1 ] valueWithTimeout: 10 ]
  1483. ifFalse: [ [ self remove ] valueWithTimeout: 500 ]
  1484. !
  1485. remove
  1486. self parent removeProgressBar: self
  1487. !
  1488. start
  1489. "Make sure the UI has some time to update itself between each iteration"
  1490. self evaluateAt: 1
  1491. !
  1492. updateProgress: anInteger
  1493. bar asJQuery css: 'width' put: anInteger asString, '%'
  1494. ! !
  1495. !HLProgressBarWidget methodsFor: 'rendering'!
  1496. renderContentOn: html
  1497. html span with: self label.
  1498. html div
  1499. class: 'progress';
  1500. with: [
  1501. bar := html div
  1502. class: 'bar';
  1503. style: 'width: 0%' ]
  1504. ! !
  1505. HLProgressBarWidget class instanceVariableNames: 'default'!
  1506. !HLProgressBarWidget class methodsFor: 'accessing'!
  1507. default
  1508. ^ default ifNil: [ default := self new ]
  1509. ! !
  1510. HLWidget subclass: #HLSpotlightWidget
  1511. instanceVariableNames: 'input'
  1512. package: 'Helios-Core'!
  1513. !HLSpotlightWidget methodsFor: 'accessing'!
  1514. ghostText
  1515. ^ 'Search... (Ctrl+F)'
  1516. !
  1517. inputCompletion
  1518. ^ self manager environment availableClassNames, self manager environment allSelectors
  1519. ! !
  1520. !HLSpotlightWidget methodsFor: 'actions'!
  1521. findMatches: aQueryString andRender: aRenderCallback
  1522. | matches |
  1523. matches := self inputCompletion select: [ :each | each match: aQueryString ].
  1524. aRenderCallback value: matches
  1525. !
  1526. search: aString
  1527. "open a new Browser pointing to aString"
  1528. aString ifNotEmpty: [
  1529. Finder findString: aString ]
  1530. ! !
  1531. !HLSpotlightWidget methodsFor: 'rendering'!
  1532. renderContentOn: html
  1533. input := html input
  1534. class: 'spotlight typeahead';
  1535. placeholder: self ghostText;
  1536. onKeyDown: [ :event |
  1537. event which = 13 ifTrue: [
  1538. self search: input asJQuery val ] ]
  1539. yourself.
  1540. input asJQuery
  1541. typeahead: #{ 'hint' -> true }
  1542. value: #{ 'name' -> 'classesAndSelectors'.
  1543. 'displayKey' -> [ :suggestion | suggestion asString ].
  1544. 'source' -> [ :query :callback | self findMatches: query andRender: callback ]}.
  1545. "use additional datasets for grouping into classes and selectors"
  1546. ! !
  1547. HLWidget subclass: #HLTabWidget
  1548. instanceVariableNames: 'widget label root'
  1549. package: 'Helios-Core'!
  1550. !HLTabWidget commentStamp!
  1551. I am a widget specialized into building another widget as an Helios tab.
  1552. I should not be used directly, `HLWidget class >> #openAsTab` should be used instead.
  1553. ## Example
  1554. HLWorkspace openAsTab!
  1555. !HLTabWidget methodsFor: 'accessing'!
  1556. activate
  1557. self manager activate: self
  1558. !
  1559. cssClass
  1560. ^ self widget tabClass
  1561. !
  1562. focus
  1563. self widget canHaveFocus ifTrue: [
  1564. self widget focus ]
  1565. !
  1566. label
  1567. ^ label ifNil: [ '' ]
  1568. !
  1569. label: aString
  1570. label := aString
  1571. !
  1572. manager
  1573. ^ HLManager current
  1574. !
  1575. widget
  1576. ^ widget
  1577. !
  1578. widget: aWidget
  1579. widget := aWidget
  1580. ! !
  1581. !HLTabWidget methodsFor: 'actions'!
  1582. add
  1583. self manager addTab: self.
  1584. self observeManager
  1585. !
  1586. hide
  1587. root ifNotNil: [ root asJQuery css: 'visibility' put: 'hidden' ]
  1588. !
  1589. observeManager
  1590. self manager announcer
  1591. on: HLTabLabelChanged
  1592. send: #onTabLabelChanged:
  1593. to: self
  1594. !
  1595. registerBindings
  1596. self widget registerBindings
  1597. !
  1598. remove
  1599. self unregister.
  1600. self widget unregister.
  1601. root ifNotNil: [ root asJQuery remove ]
  1602. !
  1603. show
  1604. root
  1605. ifNil: [ self appendToJQuery: 'body' asJQuery ]
  1606. ifNotNil: [ root asJQuery css: 'visibility' put: 'visible' ]
  1607. !
  1608. unregister
  1609. self manager announcer unsubscribe: self
  1610. ! !
  1611. !HLTabWidget methodsFor: 'reactions'!
  1612. onTabLabelChanged: anAnnouncement
  1613. anAnnouncement widget = self widget ifTrue: [
  1614. self label = anAnnouncement label ifFalse: [
  1615. self label: anAnnouncement label.
  1616. self manager refresh ] ]
  1617. ! !
  1618. !HLTabWidget methodsFor: 'rendering'!
  1619. renderOn: html
  1620. root := html div
  1621. class: 'tab';
  1622. yourself.
  1623. self renderTab
  1624. !
  1625. renderTab
  1626. root contents: [ :html |
  1627. html div
  1628. class: 'amber_box';
  1629. with: [ self widget renderOn: html ] ]
  1630. ! !
  1631. !HLTabWidget methodsFor: 'testing'!
  1632. isActive
  1633. ^ self manager activeTab = self
  1634. ! !
  1635. !HLTabWidget class methodsFor: 'instance creation'!
  1636. on: aWidget labelled: aString
  1637. ^ self new
  1638. widget: aWidget;
  1639. label: aString;
  1640. yourself
  1641. ! !
  1642. HLWidget subclass: #HLTabsWidget
  1643. instanceVariableNames: 'tabs activeTab history selectionDisabled spotlight'
  1644. package: 'Helios-Core'!
  1645. !HLTabsWidget methodsFor: 'accessing'!
  1646. activeTab
  1647. ^ activeTab
  1648. !
  1649. history
  1650. ^ history ifNil: [ history := OrderedCollection new ]
  1651. !
  1652. history: aCollection
  1653. history := aCollection
  1654. !
  1655. spotlight
  1656. ^ spotlight ifNil: [ spotlight := HLSpotlightWidget new ]
  1657. !
  1658. tabWidth
  1659. ^ (window asJQuery width - 250) / self tabs size
  1660. !
  1661. tabs
  1662. ^ tabs ifNil: [ tabs := OrderedCollection new ]
  1663. ! !
  1664. !HLTabsWidget methodsFor: 'actions'!
  1665. activate: aTab
  1666. self isSelectionDisabled ifTrue: [ ^ self ].
  1667. self manager keyBinder flushBindings.
  1668. aTab registerBindings.
  1669. activeTab := aTab.
  1670. self
  1671. refresh;
  1672. addToHistory: aTab;
  1673. show: aTab
  1674. !
  1675. activateNextTab
  1676. | nextTab |
  1677. self tabs ifEmpty: [ ^ self ].
  1678. nextTab := self tabs
  1679. at: (self tabs indexOf: self activeTab) + 1
  1680. ifAbsent: [ self tabs first ].
  1681. self activate: nextTab
  1682. !
  1683. activatePreviousTab
  1684. | previousTab |
  1685. self tabs ifEmpty: [ ^ self ].
  1686. previousTab := self tabs
  1687. at: (self tabs indexOf: self activeTab) - 1
  1688. ifAbsent: [ self tabs last ].
  1689. self activate: previousTab
  1690. !
  1691. addTab: aTab
  1692. self tabs add: aTab.
  1693. self activate: aTab
  1694. !
  1695. addToHistory: aTab
  1696. self removeFromHistory: aTab.
  1697. self history add: aTab
  1698. !
  1699. disableSelection
  1700. selectionDisabled := true
  1701. !
  1702. enableSelection
  1703. selectionDisabled := false
  1704. !
  1705. removeActiveTab
  1706. self removeTab: self activeTab
  1707. !
  1708. removeFromHistory: aTab
  1709. self history: (self history reject: [ :each | each == aTab ])
  1710. !
  1711. removeTab: aTab
  1712. (self tabs includes: aTab) ifFalse: [ ^ self ].
  1713. self removeFromHistory: aTab.
  1714. self tabs remove: aTab.
  1715. self manager keyBinder flushBindings.
  1716. aTab remove.
  1717. self refresh.
  1718. self history ifNotEmpty: [
  1719. self history last activate ]
  1720. !
  1721. removeTabForWidget: aWidget
  1722. self removeTab: (self tabs
  1723. detect: [ :each | each widget = aWidget ]
  1724. ifNone: [ ^ self ])
  1725. !
  1726. updateTabsOrder
  1727. tabs := '.nav-tabs li' asJQuery toArray
  1728. collect: [ :each | each at: 'tab-data' ]
  1729. ! !
  1730. !HLTabsWidget methodsFor: 'private'!
  1731. setupEvents
  1732. 'body' asJQuery keydown: [ :event |
  1733. "ctrl+> and ctrl+<"
  1734. (event ctrlKey and: [ event which = 188 ]) ifTrue: [
  1735. self activatePreviousTab.
  1736. event preventDefault ].
  1737. (event ctrlKey and: [ event which = 190 ]) ifTrue: [
  1738. self activateNextTab.
  1739. event preventDefault ] ]
  1740. ! !
  1741. !HLTabsWidget methodsFor: 'rendering'!
  1742. renderAddOn: html
  1743. html div
  1744. class: 'dropdown new_tab';
  1745. with: [
  1746. html a
  1747. class: 'dropdown-toggle';
  1748. at: 'data-toggle' put: 'dropdown';
  1749. with: [
  1750. (html tag: 'b') class: 'caret' ].
  1751. html ul
  1752. class: 'dropdown-menu';
  1753. with: [
  1754. ((HLWidget withAllSubclasses
  1755. select: [ :each | each canBeOpenAsTab ])
  1756. sorted: [ :a :b | a tabPriority < b tabPriority ])
  1757. do: [ :each |
  1758. html li with: [
  1759. html a
  1760. with: each tabLabel;
  1761. onClick: [ each openAsTab ] ] ] ] ]
  1762. !
  1763. renderContentOn: html
  1764. html div
  1765. class: 'navbar navbar-fixed-top';
  1766. with: [ html div
  1767. class: 'navbar-header';
  1768. at: 'role' put: 'tabpanel';
  1769. with: [ self renderTabsOn: html ] ].
  1770. html with: self spotlight.
  1771. self renderAddOn: html
  1772. !
  1773. renderTab: aTab on: html
  1774. | li |
  1775. li := html li
  1776. style: 'width: ', self tabWidth asString, 'px';
  1777. class: (aTab isActive ifTrue: [ 'tab active' ] ifFalse: [ 'tab inactive' ]);
  1778. with: [
  1779. html a
  1780. with: [
  1781. ((html tag: 'i') class: 'close')
  1782. onClick: [ self removeTab: aTab ].
  1783. html span
  1784. class: aTab cssClass;
  1785. title: aTab label;
  1786. with: aTab label ];
  1787. at: 'role' put: 'tab'];
  1788. onClick: [ aTab activate ].
  1789. (li asJQuery get: 0) at: 'tab-data' put: aTab
  1790. !
  1791. renderTabsOn: html
  1792. | ul |
  1793. ul := html ul
  1794. class: 'nav navbar-nav nav-tabs';
  1795. at: 'role' put: 'tablist';
  1796. with: [
  1797. self tabs do: [ :each |
  1798. self renderTab: each on: html ] ].
  1799. ul asJQuery sortable: #{
  1800. 'containment' -> 'parent'.
  1801. 'start' -> [ self disableSelection ].
  1802. 'stop' -> [ [ self enableSelection] valueWithTimeout: 300 ].
  1803. 'update' -> [ self updateTabsOrder ]
  1804. }
  1805. !
  1806. show: aTab
  1807. self tabs do: [ :each | each hide ].
  1808. aTab show; focus
  1809. ! !
  1810. !HLTabsWidget methodsFor: 'testing'!
  1811. isSelectionDisabled
  1812. ^ selectionDisabled ifNil: [ false ]
  1813. ! !
  1814. HLTabsWidget class instanceVariableNames: 'current'!
  1815. HLWidget subclass: #HLWelcomeWidget
  1816. instanceVariableNames: ''
  1817. package: 'Helios-Core'!
  1818. !HLWelcomeWidget methodsFor: 'accessing'!
  1819. cssClass
  1820. ^ 'welcome'
  1821. ! !
  1822. !HLWelcomeWidget methodsFor: 'actions'!
  1823. openClassBrowser
  1824. HLBrowser openAsTab
  1825. !
  1826. openHelp
  1827. !
  1828. openTestRunner
  1829. HLSUnit openAsTab
  1830. !
  1831. openWorkspace
  1832. HLWorkspace openAsTab
  1833. ! !
  1834. !HLWelcomeWidget methodsFor: 'rendering'!
  1835. renderButtonsOn: html
  1836. html button
  1837. class: 'button';
  1838. with: 'Class Browser';
  1839. onClick: [ self openClassBrowser ].
  1840. html button
  1841. class: 'button';
  1842. with: 'Workspace';
  1843. onClick: [ self openWorkspace ].
  1844. html button
  1845. class: 'button';
  1846. with: 'Test Runner';
  1847. onClick: [ self openTestRunner ].
  1848. "html button
  1849. class: 'button';
  1850. with: 'Help';
  1851. onClick: [ self openHelp ]"
  1852. !
  1853. renderContentOn: html
  1854. self
  1855. renderHelpOn: html;
  1856. renderButtonsOn: html
  1857. !
  1858. renderHelpOn: html
  1859. html h2 with: 'No tools are open'.
  1860. html ul with: [
  1861. html li with: 'Perform actions with ctrl + space'.
  1862. html li with: 'Open one of the common tools:' ]
  1863. ! !