I'm using a similar approach for Mars.
Views in Mars always consume from a similar structure and we have the concept
of "model adaptor" when you need to adapt from your model to the views. We also
have a couple of generic adaptors who adapts nicely to your model.
Let's say we need to place a string into a MRLabel, and model for label is
prepared to understand #content message.
so you say something like this:
MRLabel new
model: ('My label' adaptAccessor: #yourself);
...etc...
and that way you have "cleanness" in both sides :)
other problems is when widgets triggers events (like your example, a button
click, named performAction).
I think here you should not trigger anything but an announcement.
Your performAction should look more like:
performAction
self announce: ButtonPressed
and your "client code" should look:
MRButton new
on: ButtonPressed do: [ ... whatever... ]
So, summarizing.... I "more or less" agree with your approach. Or better I
mean: I agree in the "accessing" part, but I would like using announcements to
trigger events.
cheers,
Esteban
El 07/04/2011, a las 10:26a.m., Igor Stasenko escribió:
> I have an idea how to refactor the pluggable morphs mess.
>
> The main principle behind all pluggables, that you have a model (which
> is held in instance variable),
> and then morph having additional ivars to speak with this model, which
> the selector names you should use
> to access certain state from model.
>
> For example:
>
> AlignmentMorph subclass: #PluggableButtonMorph
> instanceVariableNames: 'model label getStateSelector actionSelector
> getLabelSelector getMenuSelector shortcutCharacter askBeforeChanging
> triggerOnMouseDown offColor onColor feedbackColor
> showSelectionFeedback allButtons arguments argumentsProvider
> argumentsSelector gradientLook enabled actionBlock getColorSelector
> getEnabledSelector'
> classVariableNames: 'UseGradientLook'
> poolDictionaries: ''
> category: 'Morphic-Pluggable Widgets'
>
>
> as you can see it is crowded with all those ivars, where part of them
> used to speak with model,
> and part of them used for keeping flags and additional parameters
> which you can customize when creating a button.
> getStateSelector actionSelector getLabelSelector getMenuSelector
> arguments argumentsProvider argumentsSelector gradientLook
> actionBlock getColorSelector getEnabledSelector
>
> and this is really messy..
> Take a look at code:
>
> performAction
> "Inform the model that this button has been pressed. Sent by the
> controller when this button is pressed. If the button's actionSelector
> takes any arguments, they are obtained dynamically by sending the
> argumentSelector to the argumentsProvider"
>
> enabled ifFalse: [^self].
> askBeforeChanging ifTrue: [model okToChange ifFalse: [^ self]].
> self actionBlock ifNotNil: [ ^ self actionBlock value].
> actionSelector ifNotNil:
> [actionSelector numArgs == 0
> ifTrue: [model perform: actionSelector]
> ifFalse:
> [argumentsProvider ifNotNil:
> [arguments := argumentsProvider
> perform: argumentsSelector].
> model perform: actionSelector
> withArguments: arguments]]
>
>
> Now think if we could replace this with simple:
>
>
> performAction
> "Inform the model that this button has been pressed. Sent by the
> controller when this button is pressed. If the button's actionSelector
> takes any arguments, they are obtained dynamically by sending the
> argumentSelector to the argumentsProvider"
>
> enabled ifFalse: [^self].
> model performAction.
>
>
> so, in this case, we just put an obligation onto model to perform any
> action in response to button clicked,
> and button no longer cares about crappy logic how it should send a
> message to model
> (via action block, via actionSelector ... and whether it should pass
> any additional arguments or not..)
>
> So, in all places which using things in a way like:
>
> getListItems
> ^ model perform: getListItemsSelector
>
> now will be replaced with clean:
>
> getListItems
> ^ model getListItems
>
> So, a pluggable morph no longer cares about keeping all those
> selectors and maintain an obscure logic whether selector is nil or not
> nil etc etc..
> Let model handle it!
>
> Then you may ask, what if i have a model, which doesn't implements
> such protocol (required for specific pluggable widget).
> The answer is simple:
> - create a pluggable model class for given widget, which wraps around
> your object and can keeps all those messy 'getLabelSelector'
> 'getMenuSelector' ,
> inside and mediates between your model and widget.
>
> The benefit of it that widgets code will be much more cleaner.
> And another benefit is that you know what exact protocol is needed if
> you want to use your object as a model to given widget (list, button
> etc).
> And in most of the cases, you don't need to use this messy
> 'getXYZSelector', because you can simply answer to messages sent by
> widget and provide
> necessary data.
> But if you wanna go messy, you just use wrapper model, which contains
> all those 'getXYZSelectors' which you can set to whatever you like.
>
> --
> Best regards,
> Igor Stasenko AKA sig.
>