Moka-Core.st 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. Smalltalk current createPackage: 'Moka-Core'!
  2. Object subclass: #MKController
  3. instanceVariableNames: 'view model'
  4. package: 'Moka-Core'!
  5. !MKController commentStamp!
  6. I implement the Controller part of the MVC pattern in Moka.
  7. I hold onto my `model` and `view`, set with `MKView >> controller:`.!
  8. !MKController methodsFor: 'accessing'!
  9. model
  10. ^ model
  11. !
  12. model: aModel
  13. model := aModel
  14. !
  15. view
  16. ^ view
  17. !
  18. view: aView
  19. view := aView
  20. ! !
  21. MKController subclass: #MKAspectController
  22. instanceVariableNames: 'aspect'
  23. package: 'Moka-Core'!
  24. !MKAspectController commentStamp!
  25. I am an abstract controller for performing one action using an `aspect` on a model.
  26. ## API
  27. - Use `#aspect:` to plug a selector to be performed on the model
  28. - Subclasses can either use `#performActionWith:` or `#performAction` to evaluate the `aspect` selector on the model with one or no argument.!
  29. !MKAspectController methodsFor: 'accessing'!
  30. aspect
  31. ^ aspect
  32. !
  33. aspect: aSelector
  34. aspect := aSelector
  35. ! !
  36. !MKAspectController methodsFor: 'actions'!
  37. performAction
  38. self aspect ifNotNil: [
  39. self model
  40. perform: self aspect ]
  41. !
  42. performActionWith: anObject
  43. self aspect ifNil: [ ^ self ].
  44. self model
  45. perform: self aspect asMutator
  46. withArguments: { anObject }
  47. ! !
  48. Object subclass: #MKModel
  49. instanceVariableNames: 'announcer'
  50. package: 'Moka-Core'!
  51. !MKModel commentStamp!
  52. I implement the Model part of the MVC pattern in Moka.
  53. I am the abstract superclass of all Moka model. The observer pattern is implemented through an `announcer` object.
  54. ## API
  55. - Listening
  56. Use `#on:do:` or `#on:send:to:` to listen to model changes
  57. - Triggering
  58. `#changed:` is the builtin method used to trigger `#update:` in views.
  59. Use `#announce:` in subclasses to trigger announcements to listeners.!
  60. !MKModel methodsFor: 'announcements'!
  61. announce: anAnnouncement
  62. announcer announce: anAnnouncement
  63. !
  64. changed: aSelector
  65. "Trigger `#update:` to all listening aspect views"
  66. self announce: (MKModelChanged aspect: aSelector)
  67. !
  68. on: anAnnouncement do: aBlock
  69. announcer on: anAnnouncement do: aBlock
  70. !
  71. on: anAnnouncement send: aSelector to: anObject
  72. announcer on: anAnnouncement send: aSelector to: anObject
  73. ! !
  74. !MKModel methodsFor: 'initialization'!
  75. initialize
  76. super initialize.
  77. announcer := Announcer new
  78. ! !
  79. Object subclass: #MKModelChanged
  80. instanceVariableNames: 'aspect'
  81. package: 'Moka-Core'!
  82. !MKModelChanged commentStamp!
  83. I am an announcement announced when a model is changed.!
  84. !MKModelChanged methodsFor: 'accessing'!
  85. aspect
  86. ^ aspect
  87. !
  88. aspect: aSelector
  89. aspect := aSelector
  90. ! !
  91. !MKModelChanged class methodsFor: 'instance creation'!
  92. aspect: aSelector
  93. ^ self new
  94. aspect: aSelector;
  95. yourself
  96. ! !
  97. Widget subclass: #MKView
  98. instanceVariableNames: 'controller model wrapper'
  99. package: 'Moka-Core'!
  100. !MKView commentStamp!
  101. I implement the View part of the MVC pattern in Moka.
  102. ## API
  103. - Instance can be created with the `MKView class >> model:*` convenience methods
  104. - rendering is done through `#renderContentOn:`, to be overridden in concrete view classes
  105. - `#update` provide updating facility, refreshing the entire view
  106. - subclasses can override `#defaultControllerClass` to provide a default controller specific to a view
  107. - subclasses can override `#observeModel`.!
  108. !MKView methodsFor: 'accessing'!
  109. controller
  110. "Answer the current receiver's controller.
  111. If no controller is installed yet, install the `defaultController`
  112. of the receiver and answer it."
  113. controller ifNil: [
  114. self controller: self defaultController ].
  115. ^ controller
  116. !
  117. controller: aController
  118. "Install `aController` to be the receiver's controller"
  119. controller := aController.
  120. aController
  121. view: self;
  122. model: self model
  123. !
  124. model
  125. ^ model
  126. !
  127. model: aModel
  128. model := aModel.
  129. self observeModel
  130. ! !
  131. !MKView methodsFor: 'defaults'!
  132. defaultControllerClass
  133. ^ MKController
  134. ! !
  135. !MKView methodsFor: 'factory'!
  136. defaultController
  137. ^ self defaultControllerClass new
  138. ! !
  139. !MKView methodsFor: 'observing'!
  140. observeModel
  141. "No op. Override in subclasses"
  142. ! !
  143. !MKView methodsFor: 'rendering'!
  144. render
  145. "Append the receiver to the BODY element"
  146. self appendToJQuery: 'body' asJQuery
  147. !
  148. renderContentOn: html
  149. "Main rendering method, override in subclasses."
  150. !
  151. renderOn: html
  152. "Basic rendering method.
  153. Wraps the content with a `wrapper` div for updating the receiver.
  154. Do not override this method, but `#renderContentOn:`"
  155. wrapper := html div
  156. class: 'moka_view';
  157. yourself.
  158. wrapper with: [ self renderContentOn: html ]
  159. ! !
  160. !MKView methodsFor: 'updating'!
  161. update
  162. "Update the view's content."
  163. wrapper ifNil: [ self error: 'The view has not been rendered yet' ].
  164. wrapper asJQuery empty.
  165. [ :html | self renderContentOn: html ]
  166. appendToJQuery: wrapper asJQuery
  167. ! !
  168. !MKView class methodsFor: 'instance creation'!
  169. model: aModel
  170. ^ self new
  171. model: aModel;
  172. yourself
  173. !
  174. model: aModel controller: aController
  175. ^ (self model: aModel)
  176. controller: aController;
  177. yourself
  178. ! !
  179. MKView subclass: #MKAspectView
  180. instanceVariableNames: 'aspect label'
  181. package: 'Moka-Core'!
  182. !MKAspectView commentStamp!
  183. I am an abstract view which state depend on an `aspect` of a model.
  184. ##API
  185. - Use the `#aspect:` to listen to a specific aspect of a model. Changes will then trigger `#update`.!
  186. !MKAspectView methodsFor: 'accessing'!
  187. aspect
  188. ^ aspect
  189. !
  190. aspect: aSelector
  191. aspect := aSelector.
  192. self controller aspect: aSelector
  193. !
  194. aspectValue
  195. ^ self model perform: self aspect
  196. ! !
  197. !MKAspectView methodsFor: 'defaults'!
  198. defaultControllerClass
  199. ^ MKAspectController
  200. ! !
  201. !MKAspectView methodsFor: 'observing'!
  202. observeModel
  203. super observeModel.
  204. self model
  205. on: MKModelChanged
  206. send: 'update:'
  207. to: self
  208. ! !
  209. !MKAspectView methodsFor: 'updating'!
  210. update: anAnnouncement
  211. anAnnouncement aspect = self aspect
  212. ifTrue: [ self update ]
  213. ! !
  214. !MKAspectView class methodsFor: 'instance creation'!
  215. model: aModel aspect: aSelector
  216. ^ (self model: aModel)
  217. aspect: aSelector;
  218. yourself
  219. ! !