[Pharo-project] An idea about Pluggable widgets

Igor Stasenko siguctua at gmail.com
Thu Apr 7 15:26:07 CEST 2011


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