[squeak-dev] Re: [ANN] Preference pragmas

Alain Plantec alain.plantec at univ-brest.fr
Fri Mar 6 09:30:03 UTC 2009


Hi Andreas,

Andreas Raab a écrit :
> Alain's implementation of preferences is an improvement on the current 
> preference system but it is effectively replacing one set of 
> dependencies with a different set of dependencies. Where previously 
> Preferences would be registered and stored via Preferences 
> addPreference:... in Alain's approach preferences get created via (the 
> old version):
...
> or, with the latest:
>
> gradientButtonLook
>     <preference>
>     ^ GradientButtonLook
>         ifNil: [GradientButtonLook := PreferenceValue
>                         name: 'Gradient look for buttons'
>                         description: 'Gradient look for buttons'
>                         parent: #uiPreferenceNode
>                         type: #Boolean
>                         default: false]
>
> In other words, a dependency (to either PreferenceNode, 
> PreferenceValue, RangePreferenceValue, MultiplePreferenceValue etc) is 
> created and stored client-side.
With Pharo approach, your code depends on a unique small hierarchy of 
classes exactly as it can depend on Collection one.
It does not depend on a global object (Preferences) as it is now.
For me, it is not a problem since preferences are declared locally in 
packages (exactly as in your pragma approach)
>
> My approach differs in such that it is aimed at being discoverable 
> without introducing any dependencies. 
I don't think so, you are introducing a dependency on a particular 
syntax wich is <preference: something: somethingelse: >
I think it is better to rely on classes and not to depend on a flat syntax.

> defaultPollPeriod
>     "Answer the number of milliseconds between interrupts for spyOn: 
> and friends.
>     This should be faster for faster machines."
>     <preference: 'Default Poll Period'
>         category: 'Profiling'
>         description: 'The default poll period (msecs) MessageTally uses'
>         type: #Number>
>     ^DefaultPollPeriod ifNil: [ DefaultPollPeriod _ 1 ]
>
You also need
defaultPollPeriod: aNumber
    DefaultPollPeriod := aNumber

and maybe another method for the default and maybe something else for 
system level notification.

Only a detail maybe but In our implementation, for one preference, you 
need to set only one declaration:
and you use the preference like this:
v := UIPreferences gradientButtonLook value.
UIPreferences gradientButtonLook value: false.

It allows  system level notification because #PreferenceValue>>value: 
triggers a #PreferenceChanged events.
An  object can be declared as listener to a particular preference value 
like this:
MyObject>>initialize
    super initialize.
    UIPreferences gradientButtonLook whenChangedSend: 
#gradientButtonLookIsNow: to: self.

the opposite is #forget:
MyObject>>release
    UIPreferences gradientButtonLook forget: self

You can see it as a replacement for changeInformee: informeeSymbol  
changeSelector: aChangeSelector.

> However, since this was discovered via pragmas, no dependency between 
> Preferences and MessageTally has been created. 
again, the dependency is on the syntax of the pragma
> You can remove or replace the preferences implementation in the image 
> without any side effects whatsoever on MessageTally or its code. A 
> different preference implementation can discover the same preference 
> and present it accordingly. This allows adding preferences wherever is 
> convenient without needlessly introducing dependencies on a particular 
> preference implementation or UI.
Pharo preference do not depends on a particular UI. PreferenceSupport is 
removeable without any side effect on the system.
But I agree that it depends on the PreferenceNode hierarchy.
>
>
> In Alain's version this would not be possible without actually 
> changing code since it is directly coupled to a particular preference 
> class and API.
ok but the pragma-only declaration is poor: only flat syntax which 
allows only literals as parameters.
the consequence is that you can't deal with one-to-many or range 
preference or something else you can discover later.
Two examples:
here the value domain is explicitly given with the help of a 
MultiplePreferenceValue. Each possible value of the preference is given 
with FixedPreferenceValue instances.
themePreference
    <preference>
    ^ ThemePreference
        ifNil: [ThemePreference := MultiplePreferenceValue
                        name: 'UITheme'
                        description: 'The theme to use for UI look and feel'
                        parent: #uiPreferenceNode       
                        type: #UITheme   
                        default: UIThemeWatery2
                        values: {
                            FixedPreferenceValue
                                name: 'Standard Squeak'
                                description: 'Standard Squeak style'
                                type: #UITheme                        
       
                                value: UIThemeStandardSqueak.
                            FixedPreferenceValue
                                name: 'Watery 2'
                                description: 'Similar to a nice OS'
                                type: #UITheme
                                value: UIThemeWatery2}]

here a range preference value, again, the value domain is explicitly given.
glyphContrast
    <preference>
    ^ GlyphContrast
        ifNil: [GlyphContrast := RangePreferenceValue
                        name: 'Glyph contrast'
                        description: 'Change the contrast level for 
glyphs. This is an integer between 1 and 100. (the default value is 50)'
                        parent:  #freeTypePreferenceNode
                        type: #Integer
                        default: 50
                        range: (1 to: 100)]

If you only make use of a poor preference declaration (I consider 
preference pragma flat declaration as a poor flat textual declaration)
it can have bad effects on the quality of the code which is using
the preference.
See how freetype hinting preference are currently handled.
You have 4 boolean preferences wheras following declaration better fits:

hintingPreference
    <preference>
    ^ HintingPreference
        ifNil: [HintingPreference := MultiplePreferenceValue
                        name: 'Hinting'
                        description: 'Changes the glyph shapes'
                        parent: #freeTypePreferenceNode       
                        type: #Symbol   
                        default: #Light
                        values: {
                            FixedPreferenceValue
                                name: 'Light'
                                description: 'Light hinting, bla bla bla 
....'
                                type: #Symbol                            
   
                                value: #Light.
                            FixedPreferenceValue
                                name: 'Full'
                                description: 'Full hinting bla bla bla ....'
                                type: #Symbol
                                value: #Full.
                            FixedPreferenceValue
                                name: 'None'
                                description: 'None hinting bla bla bla ....'
                                type: #Symbol                            
   
                                value: #None.
                            FixedPreferenceValue
                                name: 'Normal'
                                description: 'Normal hinting bla bla bla 
....'
                                type: #Symbol
                                value: #Normal}]

FixedPreferenceValue instance can be also bound to more complex values 
than simple symbol (#Light, #Full ...)
            FixedPreferenceValue
                                name: 'Light'
                                description: 'Light hinting, bla bla bla 
....'
                                type: #FreeTypeHinting                
              
                                value: [FreeTypeLightHinting new].
or
            FixedPreferenceValue
                                name: 'Light'
                                description: 'Light hinting, bla bla bla 
....'
                                type: #FreeTypeHinting                
              
                                value: (MessageSend receiver: 
FreeTypeLightHinting selector: #new).


Thus, the declaration is more rich. As a consequence, also with the help 
of the system level notification,
a client code can be much more simple and well designed.
I mean avoiding code like:

FreeTypeSettings>>hintingFullPreferenceChanged
    Preferences HintingFull
        ifTrue:[Preferences disable: #HintingNone; disable: 
#HintingLight; disable: #HintingNormal]
        ifFalse:[
            (Preferences HintingNone or:[Preferences HintingLight 
or:[Preferences HintingNormal]])
                ifFalse:[
                    "turn it back on again"
                    ^Preferences enable: #HintingFull]].
    monoHinting := Preferences HintingFull.
    lightHinting := Preferences HintingLight.
    hinting := monoHinting or:[lightHinting or:[Preferences HintingNormal]].
    FreeTypeCache current removeAll.
    FreeTypeFont allSubInstances do:[:each | each clearCachedMetrics].
    NewParagraph allSubInstances do:[:each | each composeAll].
    World restoreMorphicDisplay.

The last point you maybe missed (which is maybe not so important) is 
that what we are declaring is a tree of preferences.
See Snapshot attached (For now, only a poor tool version, 
"PreferenceTree open" to see it in action), it  ilustrates well the point.
An again, this kind of tool is NOT mandatory. Related package can be 
removed without side effect on the system because
it also makes use of dynamic preference discovering with the help of Pragma.

ah, the real last point is the default value: in your point of view, 
default values depend on value type.
I can't agree with that point of view. It would mean, as an example, 
that every boolean preferences have false, or true as the default.
The problem with non-literals then arises if you want to declare default 
value with pragma...

ah, again. If your system relies on a <preference:truc:...> syntax. What 
about evolution of the system ?
If you want to change all  declarations, you have to find them all. As 
far as I know, you can't rely on standard code browser for it.
And what about external tools and user application ?

Gary Chambers quote: -------------------------
> The simple pragma approach I described would make it easier for the 
> tools though since they wouldn't need a separate model (or hang onto 
> the pragma) in order to work.
>
> Also, for user applications. Our ReportBuilder, for instance, also has 
> an end-user ui for preferences local to itself. Having acccess to the 
> default value/description withough having to find the pragma would be 
> easier. (Just an example, the ReportBuilder actually uses xml config 
> files for its prefs).
------------------------- End of Gary quote
>
> Bottom line: I think my approach is a necessity before Alain's 
> preference implementation can be usefully deployed. It allows us to 
> define preferences without introducing dependencies to specific 
> implementations, while allowing different implementations to discover 
> the same preferences.
I do not really understand.
>
> I hope this makes the conceptual difference clear.
Thanks for it.
...and remarks, critics, idea, improvements are welcomed.

Cheers
alain
>
> Cheers,
>   - Andreas
>
>
>

-------------- next part --------------
A non-text attachment was scrubbed...
Name: Preference tree.png
Type: image/png
Size: 43018 bytes
Desc: not available
Url : http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20090306/e5bf0bfc/Preferencetree.png


More information about the Squeak-dev mailing list