123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- Smalltalk current createPackage: 'Moka-Core'!
- Object subclass: #MKController
- instanceVariableNames: 'view model'
- package: 'Moka-Core'!
- I implement the Controller part of the MVC pattern in Moka.
- I hold onto my `model` and `view`, set with `MKView >> controller:`.!
- model
- ^ model
- model: aModel
- model := aModel
- view
- ^ view
- view: aView
- view := aView
- onChange: anEvent
- onClick: anEvent
- onDblClick: anEvent
- onKeyDown: anEvent
- onKeyPress: anEvent
- onKeyUp: anEvent
- onMouseEnter: anEvent
- onMouseLeave: anEvent
- onMouseMove: anEvent
- onMouseOut: anEvent
- onMouseOver: anEvent
- MKController subclass: #MKAspectsController
- instanceVariableNames: ''
- package: 'Moka-Core'!
- I am an abstract controller for performing one action using an `aspect` on a model.
- - Use `#aspect:` to plug a selector to be performed on the model
- - Subclasses can either use `#performActionWith:` or `#performAction` to evaluate the `aspect` selector on the model with one or no argument.!
- performAspectAction: aSelector
- self model perform: aSelector
- performAspectAction: aSelector with: anObject
- self model
- perform: aSelector asMutator
- withArguments: { anObject }
- MKAspectsController subclass: #MKSingleAspectController
- instanceVariableNames: ''
- package: 'Moka-Core'!
- I am an abstract controller used with single aspect views.
- My view must hold onto one aspect accessed with `#aspect`.!
- performAspectAction
- ^ self performAspectAction: self view aspect
- performAspectActionWith: anObject
- ^ self
- performAspectAction: self view aspect
- with: anObject
- Object subclass: #MKModel
- instanceVariableNames: 'announcer'
- package: 'Moka-Core'!
- I implement the Model part of the MVC pattern in Moka.
- I am the abstract superclass of all Moka model. The observer pattern is implemented through an `announcer` object.
- - Listening
- Use `#on:do:` or `#on:send:to:` to listen to model changes
- - Triggering
- `#changed:` is the builtin method used to trigger `#update:` in views.
- Use `#announce:` in subclasses to trigger announcements to listeners.!
- announce: anAnnouncement
- announcer announce: anAnnouncement
- changed: aSelector
- "Trigger `#update:` to all listening aspect views"
-
- self announce: (MKModelChanged aspect: aSelector)
- on: anAnnouncement do: aBlock
- announcer on: anAnnouncement do: aBlock
- on: anAnnouncement send: aSelector to: anObject
- announcer on: anAnnouncement send: aSelector to: anObject
- initialize
- super initialize.
- announcer := Announcer new
- Object subclass: #MKModelChanged
- instanceVariableNames: 'aspect'
- package: 'Moka-Core'!
- I am an announcement announced when a model is changed.!
- aspect
- ^ aspect
- aspect: aSelector
- aspect := aSelector
- aspect: aSelector
- ^ self new
- aspect: aSelector;
- yourself
- Widget subclass: #MKView
- instanceVariableNames: 'controller model root layout extraCssClass'
- package: 'Moka-Core'!
- I implement the View part of the MVC pattern in Moka.
- - Instance can be created with the `MKView class >> model:*` convenience methods
- - rendering is done through `#renderContentOn:`, to be overridden in concrete view classes
- - `#update` provide updating facility, refreshing the entire view
- - subclasses can override `#defaultControllerClass` to provide a default controller specific to a view
- - subclasses can override `#observeModel`
- - Extra css classes can be added with `#extraCssClass:`.!
- controller
- "Answer the current receiver's controller.
- If no controller is installed yet, install the `defaultController`
- of the receiver and answer it."
-
- controller ifNil: [
- self controller: self defaultController ].
- ^ controller
- controller: aController
- "Install `aController` to be the receiver's controller"
-
- controller := aController.
- aController
- view: self;
- model: self model
- cssClass
- ^ String streamContents: [ :stream |
- stream << 'moka_view'.
- self extraCssClass ifNotEmpty: [
- stream << ' ' << self extraCssClass ] ]
- extraCssClass
- ^ extraCssClass ifNil: [ '' ]
- extraCssClass: aString
- extraCssClass := aString
- layout
- ^ layout ifNil: [ layout := self defaultLayout ]
- model
- ^ model
- model: aModel
- model := aModel.
- self observeModel
- position
- "Answer the position of the reciever in the page"
-
- ^ root ifNotNil: [
- | offset |
- offset := root asJQuery offset.
- offset left @ offset top ]
- tag
- ^ 'div'
- blur
- root ifNotNil: [ root asJQuery blur ]
- focus
- root ifNotNil: [ root asJQuery focus ]
- remove
- "Removes the receiver from the DOM"
- root ifNotNil: [ root asJQuery remove ]
- defaultControllerClass
- ^ MKController
- defaultLayout
- ^ MKLayout new
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- yourself
- defaultController
- ^ self defaultControllerClass new
- bottom: aNumber
- self layout bottom: aNumber
- centerX: aNumber
- self layout centerX: aNumber
- centerY: aNumber
- self layout centerY: aNumber
- height: aNumber
- self layout height: aNumber
- left: aNumber
- self layout left: aNumber
- right: aNumber
- self layout right: aNumber
- top: aNumber
- self layout top: aNumber
- width: aNumber
- self layout width: aNumber
- observeModel
- "No op. Override in subclasses"
- setupEventHandlers
- root
- onClick: [ :event | self controller onClick: event ];
- onDblClick: [ :event | self controller onDblClick: event ];
- onMouseEnter: [ :event | self controller onMouseEnter: event ];
- onMouseLeave: [ :event | self controller onMouseLeave: event ];
- onMouseOver: [ :event | self controller onMouseOver: event ];
- onMouseOut: [ :event | self controller onMouseOut: event ];
- onMouseMove: [ :event | self controller onMouseMove: event ];
- onKeyDown: [ :event | self controller onKeyDown: event ];
- onKeyUp: [ :event | self controller onKeyUp: event ];
- onKeyPress: [ :event | self controller onKeyPress: event ];
- onChange: [ :event | self controller onChange: event ]
- render
- "Append the receiver to the BODY element"
-
- self appendToJQuery: 'body' asJQuery
- renderContentOn: html
- "Main rendering method, override in subclasses."
- renderOn: html
- "Basic rendering method.
- Do not override this method, but `#renderContentOn:`"
-
- root := (html tag: self tag)
- class: self cssClass;
- style: self layout asCssString;
- yourself.
- root with: [ self renderContentOn: html ].
-
- self setupEventHandlers
- update
- "Update the view's content. Override in subclasses to fine-tune updating"
-
- root ifNil: [ self error: 'The view has not been rendered yet' ].
-
- root asJQuery empty.
- [ :html | self renderContentOn: html ]
- appendToJQuery: root asJQuery
- model: aModel
- ^ self new
- model: aModel;
- yourself
- model: aModel controller: aController
- ^ (self model: aModel)
- controller: aController;
- yourself
- MKView subclass: #MKAspectsView
- instanceVariableNames: ''
- package: 'Moka-Core'!
- I am an abstract view which state depend on aspects of a model.!
- valueForAspect: aSelector
- ^ self model perform: aSelector
- defaultControllerClass
- ^ MKAspectController
- observeModel
- super observeModel.
-
- self model
- on: MKModelChanged
- send: 'update:'
- to: self
- update: anAnnouncement
- "Override in subclasses to match the view's aspect(s)"
- MKAspectsView subclass: #MKSingleAspectView
- instanceVariableNames: 'aspect'
- package: 'Moka-Core'!
- I am an abstract view which state depend on an `aspect` of a model.
- - Use the `#aspect:` to listen to a specific aspect of a model. Changes will then trigger `#update`.!
- aspect
- ^ aspect
- aspect: aSelector
- aspect := aSelector
- aspectValue
- ^ self valueForAspect: self aspect
- defaultControllerClass
- ^ MKSingleAspectController
- update: anAnnouncement
- anAnnouncement aspect = self aspect ifTrue: [
- self update ]
- model: aModel aspect: aSelector
- ^ (self model: aModel)
- aspect: aSelector;
- yourself
|