[Pharo-project] An idea about Pluggable widgets

Igor Stasenko siguctua at gmail.com
Thu Apr 7 19:23:57 CEST 2011


On 7 April 2011 18:59, Alain Plantec <alain.plantec at yahoo.com> wrote:
> Hi Igor,
> I'm not sure to understand.
> As an example:
> TrafficLight is my model with an action for the red light.
> If I build a view for it, I can use a Button with #performAction:
>
> Button>> performAction
>    ^ model performAction
>
> So, my TrafficLight must implement a #performAction method.
> Now, if I want also an action for the green light performed when another
> button is pressed.
> It means that I've to implement a specific model class for the red and
> another for the green ?

For that case you use model adapters.
So, then you do something like:

button1 model: (self for: #performAction adapt: #turnRed)
button2 model: (self for: #performAction adapt: #turnGreen)

and so, a receiver (which is a model), will receive #turnRed, or
#turnGreen messages,
while button will still use same message for model - #performAction

> cheers
> Alain
>
>
> for example, I have a simple UI with two buttons
>
> Le 07/04/2011 15:26, Igor Stasenko a écrit :
>>
>> 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.



More information about the Pharo-project mailing list