[Pharo-project] An idea about Pluggable widgets

Esteban Lorenzano estebanlm at gmail.com
Thu Apr 7 16:04:51 CEST 2011


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




More information about the Pharo-project mailing list