[Pharo-project] An idea about Pluggable widgets

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


On 7 April 2011 19:23, Igor Stasenko <siguctua at gmail.com> wrote:
> 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
>

or alternatively, we could ask a widget to create and install an
instance of model adaptor
(so it will construct a properly initialized model adaptor, when you
only need to fill the data in it):

(button1 installModelAdaptorFor: self)
   actionSelector: #turnRed.

(button2 installModelAdaptorFor: self)
   actionSelector: #turnGreen.


>> 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.
>



-- 
Best regards,
Igor Stasenko AKA sig.



More information about the Pharo-project mailing list