4.3: Declaring a Setting
- Page ID
- 43808
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)All global preferences of Pharo can be viewed or changed using the Settings Browser. A preference is typically a class variable or an instance variable of a singleton. If one wants to be able to change a value from the SettingsBrowser, then a setting must be declared for it. A setting is declared by a particular class method that should be implemented as follows: it takes a builder as argument and it is tagged with the <systemsettings> pragma.
The argument, aBuilder
, serves as an API or facade for building setting declarations. The pragma allows the Settings Browser to dynamically discover current setting declarations.
The important point is that a setting declaration should be package specific. It means that each package is responsible for the declaring of its own settings. For a particular package, specific settings are declared by one or several of its classes or a companion package. There is no global setting defining class or package (as was the case in Pharo1.0). The direct benefit is that when the package is loaded, then its settings are automatically loaded. When a package is unloaded, then its settings are automatically unloaded. In addition, a Setting declaration should not refer to any Setting class but to the builder argument. This assures that your application is not dependent on Settings and that you will be able to remove Setting if you want to define extremely small footprint applications.
Let’s take the example of the caseSensitiveFinds
preference. It is a boolean preference which is used for text searching. If it is true
, then text finding is case sensitive. This preference is stored in the CaseSensitiveFinds
class variable of the class TextEditor
. Its value can be queried and changed by, respectively, TextEditor class>>caseSensitiveFinds
and TextEditor class>>caseSensitiveFinds:
given below:
TextEditor class>>caseSensitiveFinds ^ CaseSensitiveFinds ifNil: [CaseSensitiveFinds := false] TextEditor class>>caseSensitiveFinds: aBoolean CaseSensitiveFinds := aBoolean
To define a setting for this preference (i.e., for the CaseSensitiveFinds
class variable) and be able to see it and change it from the Settings Browser, the method below is implemented. The result is shown in the screenshot of the Figure \(\PageIndex{1}\).
CodeHolderSystemSettings class>>caseSensitiveFindsSettingsOn: aBuilder <systemsettings> (aBuilder setting: #caseSensitiveFinds) target: TextEditor; label: 'Case sensitive search' translated; description: 'If true, then the "find" command in text will always make its searches in a case-sensitive fashion' translated; parent: #codeEditing.

Now, let’s study this setting declaration in details.
The header
CodeHolderSystemSettings class>>caseSensitiveFindsSettingsOn: aBuilder ...
This class method is declared in the class CodeHolderSystemSettings
. This class is dedicated to settings and contains nothing but setting declarations. Defining such a class is not mandatory; in fact, any class can define setting declarations. We define it that way to make sure that the setting declaration is packaged in a different package than the one of the preference definition – for layering purposes.
This method takes a builder as argument. This object serves as a facade API for setting buildings: the contents of the method essentially consists in sending messages to the builder to declare and organize a sub-tree of settings.
The pragma
A setting declaration is tagged with the <systemsettings>
pragma.
CodeHolderSystemSettings class>>caseSensitiveFindsSettingsOn: aBuilder <systemsettings> ...
In fact, when the settings browser is opened, it first collects all settings declarations by searching all methods with the <systemsettings>
pragma. In addition, if you compile a setting declaration method while a Settings Browser is opened then it is automatically updated with the new setting.
The setting configuration
A setting is declared by sending the message setting:
to the builder with an identifier passed as argument. Here is an example where the identifier is #caseSensitiveFinds
:
CodeHolderSystemSettings class>>caseSensitiveFindsSettingsOn: aBuilder <systemsettings> (aBuilder setting: #caseSensitiveFinds) ...
Sending the message setting:
to a builder creates a setting node builder which itself is a wrapper for a setting node. By default, the symbol passed as argument is considered as the selector used by the Settings Browser to get the preference value. The selector for changing the preference value is by default built by adding a colon to the getter selector (i.e., it is caseSensitiveFinds:
here). These selectors are sent to a target which is by default the class in which the method is implemented (i.e., CodeHolderSystemSettings
). Thus, this one line setting declaration is sufficient if caseSensitiveFinds
and caseSensitiveFinds:
accessors are implemented in CodeHolderSystemSettings
.
In fact, very often, these default initializations will not fit your need. Of course you can adapt the setting node configuration to take into account your specific situation. For example, the corresponding getter and setter accessors for the caseSensitiveFinds
setting are implemented in the class TextEditor
. Then, we should explicitly set that the target is TextEditor
. This is done by sending the message target:
to the setting node with the target class TextEditor
passed as argument as shown by the updated definition:
CodeHolderSystemSettings class>>caseSensitiveFindsSettingsOn: aBuilder <systemsettings> (aBuilder setting: #caseSensitiveFinds) target: TextEditor
This very short version is fully functional and enough to be compiled and taken into account by the Settings Browser as shown by Figure \(\PageIndex{2}\).

Unfortunately, the presentation is not really user-friendly because:
- the label shown in the settings browser is the identifier (the symbol used to build accessors to access it),
- there is no description or explanation available for this setting, and
- the new setting is simply added at the root of the setting tree.
To address such shortcomings, you can better configure your setting node with a label and a description respectively with the label:
and description:
messages which take a string as argument.
CodeHolderSystemSettings class>>caseSensitiveFindsSettingsOn: aBuilder <systemsettings> (aBuilder setting: #caseSensitiveFinds) target: TextEditor; label: 'Case sensitive search' translated; description: 'If true, then the "find" command in text will always make its searches in a case-sensitive fashion' translated; parent: #codeEditing.
Don’t forget to send translated
to the label and the description strings, it will greatly facilitate the translation into other languages.
Concerning the classification and the settings tree organization, there are several ways to improve it. This point is fully detailed in the next section.
More about the target
The target of a setting is the receiver for getting and changing the preference value. Most of the time it is a class. Typically, a preference value is stored in a class variable. Thus, class side methods are used as accessors for accessing the setting.
But the receiver can also be a singleton object. This is currently the case for many preferences. As an example, the Free Type fonts preferences, they are all stored in the instance variables of a FreeTypeSettings
singleton. Thus, here, the receiver is the FreeTypeSettings
instance that you can get by evaluating the following expression:
FreeTypeSettings current
One can use this expression to configure the target of a corresponding setting. As an example the #glyphContrast
preference could be declared as follow:
(aBuilder setting: #glyphContrast) target: FreeTypeSettings current; label: 'Glyph contrast' translated; ...
This is simple, but unfortunately, declaring such a singleton target like this is not a good idea. This declaration is not compatible with the Setting style functionalities. In such a case, one would have to separately indicate the target class and the message selector to send to the target class to get the singleton. Thus, as shown in the example below, you should use the targetSelector:
message:
(aBuilder setting: #glyphContrast) target: FreeTypeSettings; targetSelector: #current; label: 'Glyph contrast' translated; ...
More about default values
The way the Settings Browser builds a setting input widget depends on the actual value type of a preference. Having nil as a value for a preference is a problem for the Settings Browser because it can’t figure out which input widget to use. So basically, to be properly shown with the good input widget, a preference must always be set with a non nil value. You can set a default value to a preference by initializing it as usual, with a #initialize method or with a lazy initialization programed in the accessor method of the preference.
Regarding the Settings Browser, the best way is the lazy initialization (see the example of the #caseSensitiveFinds preference given in Section 5.3). Indeed, as explained in Section 5.2, from the Settings Browser contextual menu, you can reset a preference value to its default or globally reset all preference values. In fact, it is done by setting the preference value to reset to nil. As a consequence, the preference is automatically set to its default value as soon as it is get by using its dedicated accessor.
It is not always possible to change the way an accessor is implemented. A reason for that could be that the preference accessor is maintained within another package which you are not allowed to change. As shown in the example below, as a workaround, you can indicate a default value from the declaration of the setting by sending the message default: to the setting node:
CodeHolderSystemSettings class>>caseSensitiveFindsSettingsOn: aBuilder <systemsettings> (aBuilder setting: #caseSensitiveFinds) default: true; ...