123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- Smalltalk createPackage: 'Moka-Core'!
- Object subclass: #MKController
- instanceVariableNames: 'view model'
- package: 'Moka-Core'!
- !MKController commentStamp!
- I implement the Controller part of the MVC pattern in Moka.
- I hold onto my `model` and `view`, set with `MKView >> controller:`.!
- !MKController methodsFor: 'accessing'!
- model
- ^ model
- !
- model: aModel
- model := aModel
- !
- view
- ^ view
- !
- view: aView
- view := aView
- ! !
- !MKController methodsFor: 'actions'!
- 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'!
- !MKAspectsController commentStamp!
- I am an abstract controller for performing one action using an `aspect` on a model.
- ## API
- - 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.!
- !MKAspectsController methodsFor: 'actions'!
- performAspectAction: aSelector
- self model perform: aSelector
- !
- performAspectAction: aSelector with: anObject
- self model
- perform: aSelector asMutator
- withArguments: { anObject }
- ! !
- MKAspectsController subclass: #MKSingleAspectController
- instanceVariableNames: ''
- package: 'Moka-Core'!
- !MKSingleAspectController commentStamp!
- I am an abstract controller used with single aspect views.
- My view must hold onto one aspect accessed with `#aspect`.!
- !MKSingleAspectController methodsFor: 'actions'!
- performAspectAction
- ^ self performAspectAction: self view aspect
- !
- performAspectActionWith: anObject
- ^ self
- performAspectAction: self view aspect
- with: anObject
- ! !
- Object subclass: #MKObservable
- instanceVariableNames: 'announcer'
- package: 'Moka-Core'!
- !MKObservable commentStamp!
- View models are typically subclasses of me.
- I implement the Observable part of the Observer pattern in Moka.
- The observer pattern is implemented through an `announcer` object.
- ## API
- - Listening
- Use `#on:do:` or `#on:send:to:` to listen to receiver changes
- - Triggering
- `#changed:` is the builtin method used to trigger `#update:` in views.
- Use `#announce:` in subclasses to trigger announcements to listeners.!
- !MKObservable methodsFor: 'announcements'!
- announce: anAnnouncement
- announcer announce: anAnnouncement
- !
- changed: aSelector
- "Trigger `#update:` to all listening aspect views"
-
- self announce: (MKAspectChanged aspect: aSelector)
- !
- on: anAnnouncement do: aBlock
- announcer on: anAnnouncement do: aBlock
- !
- on: anAnnouncement send: aSelector to: anObject
- announcer on: anAnnouncement send: aSelector to: anObject
- ! !
- !MKObservable methodsFor: 'initialization'!
- initialize
- super initialize.
- announcer := Announcer new
- ! !
- MKObservable subclass: #MKView
- instanceVariableNames: 'controller model root extraCssClass'
- package: 'Moka-Core'!
- !MKView commentStamp!
- I implement the View part of the MVC pattern in Moka.
- ## API
- - 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:`.!
- !MKView methodsFor: 'accessing'!
- children
- "Answer all the sub-views of the receiver"
- ^ #()
- !
- 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 ] ]
- !
- cssStyle
- ^ ''
- !
- extraCssClass
- ^ extraCssClass ifNil: [ '' ]
- !
- extraCssClass: aString
- extraCssClass := aString
- !
- model
- ^ model
- !
- model: aModel
- model := aModel.
- self observeModel
- !
- tag
- ^ 'div'
- ! !
- !MKView methodsFor: 'actions'!
- blur
- root ifNotNil: [ root asJQuery blur ]
- !
- focus
- root ifNotNil: [ root asJQuery focus ]
- !
- remove
- "Removes the receiver from the DOM"
- root ifNotNil: [ root asJQuery remove ].
-
- self announce: (MKViewRemoved view: self)
- !
- resized
- "Action triggered when the view has been resized from the outside"
-
- self children do: [ :each | each resized ]
- ! !
- !MKView methodsFor: 'adding'!
- appendToBrush: aTagBrush
- self appendToJQuery: aTagBrush asJQuery
- !
- appendToJQuery: aJQuery
- self renderOn: (HTMLCanvas onJQuery: aJQuery)
- ! !
- !MKView methodsFor: 'converting'!
- asJQuery
- ^ root asJQuery
- ! !
- !MKView methodsFor: 'defaults'!
- defaultControllerClass
- ^ MKController
- ! !
- !MKView methodsFor: 'dom'!
- domPosition
- "Answer the position of the reciever in the page"
-
- | offset |
- offset := self asJQuery offset.
- ^ offset left @ offset top
- !
- domSize
- ^ self asJQuery width @ self asJQuery height
- ! !
- !MKView methodsFor: 'factory'!
- defaultController
- ^ self defaultControllerClass new
- ! !
- !MKView methodsFor: 'observing'!
- observeModel
- "No op. Override in subclasses"
- ! !
- !MKView methodsFor: 'private'!
- 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 ]
- ! !
- !MKView methodsFor: 'rendering'!
- 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 cssStyle;
- yourself.
- root with: [ self renderContentOn: html ].
-
- self setupEventHandlers
- ! !
- !MKView methodsFor: 'updating'!
- update
- "Update the view's content. Override in subclasses to fine-tune updating"
-
- root ifNil: [ ^ self ].
-
- root asJQuery empty.
- [ :html | self renderContentOn: html ]
- appendToJQuery: root asJQuery
- ! !
- !MKView class methodsFor: 'instance creation'!
- model: aModel
- ^ self new
- model: aModel;
- yourself
- !
- model: aModel controller: aController
- ^ (self model: aModel)
- controller: aController;
- yourself
- ! !
- MKView subclass: #MKLayoutView
- instanceVariableNames: 'layout'
- package: 'Moka-Core'!
- !MKLayoutView commentStamp!
- I implement the View part of the MVC pattern in Moka.
- ## API
- - 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:`.!
- !MKLayoutView methodsFor: 'accessing'!
- cssStyle
- ^ self layout asCssString
- !
- layout
- ^ layout ifNil: [ layout := self defaultLayout ]
- ! !
- !MKLayoutView methodsFor: 'defaults'!
- defaultLayout
- ^ MKLayout new
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- yourself
- ! !
- !MKLayoutView methodsFor: 'layout'!
- bottom
- ^ self layout bottom
- !
- bottom: aNumber
- self layout bottom: aNumber
- !
- centerX
- ^ self layout centerX
- !
- centerX: aNumber
- self layout centerX: aNumber
- !
- centerY
- ^ self layout centerY
- !
- centerY: aNumber
- self layout centerY: aNumber
- !
- height
- ^ self layout height
- !
- height: aNumber
- self layout height: aNumber
- !
- left
- ^ self layout left
- !
- left: aNumber
- self layout left: aNumber
- !
- right
- ^ self layout right
- !
- right: aNumber
- self layout right: aNumber
- !
- top
- ^ self layout top
- !
- top: aNumber
- self layout top: aNumber
- !
- width
- ^ self layout width
- !
- width: aNumber
- self layout width: aNumber
- ! !
- !MKLayoutView class methodsFor: 'instance creation'!
- model: aModel
- ^ self new
- model: aModel;
- yourself
- !
- model: aModel controller: aController
- ^ (self model: aModel)
- controller: aController;
- yourself
- ! !
- MKLayoutView subclass: #MKAspectsView
- instanceVariableNames: ''
- package: 'Moka-Core'!
- !MKAspectsView commentStamp!
- I am an abstract view which state depend on aspects of a model.!
- !MKAspectsView methodsFor: 'accessing'!
- valueForAspect: aSelector
- ^ self model perform: aSelector
- ! !
- !MKAspectsView methodsFor: 'defaults'!
- defaultControllerClass
- ^ MKAspectController
- ! !
- !MKAspectsView methodsFor: 'observing'!
- observeModel
- super observeModel.
-
- self model
- on: MKAspectChanged
- send: 'update:'
- to: self
- ! !
- !MKAspectsView methodsFor: 'updating'!
- update: anAnnouncement
- "Override in subclasses to match the view's aspect(s)"
- ! !
- MKAspectsView subclass: #MKSingleAspectView
- instanceVariableNames: 'aspect'
- package: 'Moka-Core'!
- !MKSingleAspectView commentStamp!
- I am an abstract view which state depend on an `aspect` of a model.
- ##API
- - Use the `#aspect:` to listen to a specific aspect of a model. Changes will then trigger `#update`.!
- !MKSingleAspectView methodsFor: 'accessing'!
- aspect
- ^ aspect
- !
- aspect: aSelector
- aspect := aSelector
- !
- aspectValue
- ^ self valueForAspect: self aspect
- ! !
- !MKSingleAspectView methodsFor: 'defaults'!
- defaultControllerClass
- ^ MKSingleAspectController
- ! !
- !MKSingleAspectView methodsFor: 'updating'!
- update: anAnnouncement
- anAnnouncement aspect = self aspect ifTrue: [
- self update ]
- ! !
- !MKSingleAspectView class methodsFor: 'instance creation'!
- model: aModel aspect: aSelector
- ^ (self model: aModel)
- aspect: aSelector;
- yourself
- ! !
- MKLayoutView subclass: #MKDecorator
- instanceVariableNames: 'decorated'
- package: 'Moka-Core'!
- !MKDecorator commentStamp!
- I am root class of the decorator pattern in Moka.
- I am used to add rendering and/or behavior to other views.
- ## API
- To decorate a view, use the class-side `#decorate:` method.!
- !MKDecorator methodsFor: 'accessing'!
- children
- ^ { self decorated }
- !
- decorated
- ^ decorated
- !
- decorated: aView
- decorated := aView.
- self observeDecorated
- ! !
- !MKDecorator methodsFor: 'observing'!
- observeDecorated
- "Override in subclasses"
- ! !
- !MKDecorator methodsFor: 'rendering'!
- renderContentOn: html
- html with: self decorated
- ! !
- !MKDecorator class methodsFor: 'instance creation'!
- decorate: aView
- ^ self new
- decorated: aView;
- yourself
- ! !
|