II. Switch to the section Misc of the gallery, then drag a Thumb Wheel Widget and drop it into the canvas. The same process is done for a Slider Widget and a Progress Indicator Widget.
III. Arrange components as you like, change the applications name and resize the canvas.
Now, our canvas should now look more or less like:
IV. Now, we want that all three widgets operate on the same value holder (i.e. changes in any of the widgets shall immediately update thei visual stage of the percentage shown in all others). To do this, you have to specify the same aspect selector for all three widgets by switching to the section Basics of the control window of the GUI Painter, and entering the same name (say "percentageValue") into the entry field Model:

percentageValue
|holder|
holder := builder bindingAt: #percentageValue.
holder isNil ifTrue:[
builder
aspectAt:#percentageValue
put:(ValueHolder with:0)
].
^ holder
percentageValue
percentageValue isNil ifTrue:[
percentageValue := (ValueHolder with:0)
].
^ percentageValue
(assuming, that the application contains an instance variable
named "percentageValue")
The GUI Painter can automatically generate aspect methods like the above (via the 'Generate aspect methods' menu item).
Via the Settings menu, you can control if the aspect method
should use the builders bindings dictionary or use an instance variable
to hold the aspect.
Note: This function will not overwrite existing aspect accessor methods
- your changed methods are safe from being overwritten when this function
is called. This also means, that obsolete aspect methods have to be removed
manually.
initialize
super initialize.
aspects := IdentityDictionary new.
aspects
at:#percentageValue
put:(ValueHolder with:0).
and provide access to this value by implementing:
aspectFor:aKey
^ aspects
at:aKey
ifAbsent:[super aspectFor:aKey].
If you want an update message to be sent to your application (whenever an aspect is changed), you can extent the above method as in:
initialize
|holder|
super initialize.
aspects := IdentityDictionary new.
aspects
at:#percentageValue
put:(holder := ValueHolder with:0).
holder addDependent:self
This will arrange for the #update:with:from:
method to be invoked, whenever a new value is stored into the
valueHolder.
update:something with:aParameter from:someObject
super update:something with:aParameter from:someObject.
...
your update code
...
where
something indicates what
has changed (for example #value, #list,
etc.),
aParameter can be an argument
passed with,
someObject is the value holder
that has invoked the update message.
The advantages of using a dictionary for the aspects are:
The application can also be started in a Workspace by evaluating
"NewApplication open", by the SystemBrowser (double-click on
the classes name), or
by writing a program containing the open-message.
While playing with
the demo, you may notice that the range of the sliders value is 0..100,
while the thumb wheel has a default range of 0..360. To change this, select
the thumb wheel in the GUI Painter, switch to the section Details,
and enter 0, 100, 1 into the entry fields Min:, Max:, and
Step: respectively.

II. Switch to the section Lists of the widget gallery and drag a List Widget and drop it into the canvas.
Align the top border of the widget's view to the top border of the canvas,
by pressing the Align/Top button:
and the left and right borders by pressing the Align/Left & Right
button:
now, your canvas will look somewhat like:


You will be asked for a class and method name, when doing the for the first time. You may want to change the class name to some more useful name. The name of the window spec method usually needs no renaming. If your application consists of multiple interfaces, then different names for the window spec methods are required.
If you start the application, you will recognize that the view of the file list is still empty. Of course, since the model SelectionInList is never filled with a list of file names, the view still displays an empty list. To pass a list of file names, we can set the list in the program either in the access method, or alternatively redefine one of the applications startup methods.
Here, let's redefine the postBuildWith: method, which is invoked during application startup (and provided to concrete subclasses of ApplicationModel as a hook for specific setup).
Open a System Browser and add following method codes to the application class in the instance method protocol. For the first method create a new method category named startup / release:
postBuildWith: aBuilder
The next method does set the list of files. A convenient method category could be private:
setFileList
"get a list of file names contained in your current directory"
self fileListHolder list: Filename homeDirectory directoryContents.
"reject all sub directories"
listOfFiles := listOfFiles reject: [:name| name asFilename isDirectory].
"put the list into the value holder"
self fileListHolder list: listOfFiles

Here, a callback selector in your application needs to be defined, which is invoked whenever the selection changes. Let's enter a name for the selector (say fileNameSelected) into the entry field Select::

"get the selected file name"
selectedFileNameString := self fileListHolder selection.
"put the file contents into the text view"
self fileContentsHolder value: contents
After starting the application, you can now select the file names in order to get the file contents:

II. Define the entries of the popup menu. By pressing the button
Menu: at the left side of the menu entry field a Menu Editor appears.
Remove the default first entry, then add two menu items by pressing the
button
.
Enter for the labels of the menu items Rename and Remove
into the entry fields Label:. For the action selectors enter renameFile
and removeFile into the entry fields Action:. After
each edit action accept your modifications. If you have completed your
work with the Menu Editor, you can save the menu spec by pressing the button
,
then close the Menu Editor.
To get more information about the Menu Editor open the document "Using the Menu Editor".
III. The last step is to implement the action methods. Open a System Browser and add following method codes to the method category user actions:
removeFile
"get the selected file name"
selectedFileNameString := self fileListHolder selection.
"open a confirmation dialog"
(self confirm:('Remove ' , selectedFileNameString , '?'))
ifTrue:
[
"update the fileList"
self setFileList
renameFile
"get the selected file name"
selectedFileNameString := self fileListHolder selection.
"put the data into value holders"
oldFileNameHolder := selectedFileNameString
asValue.
newFileNameHolder := '' asValue.
"create the bindings for builder of the dialog"
bindings := IdentityDictionary new.
bindings at: #oldFileName put: oldFileNameHolder.
bindings at: #newFileName put: newFileNameHolder.
"open the dialog"
(self openDialogInterface: #dialogSpecForRenamingFiles withBindings:
bindings)
ifTrue:
[
"update the file list"
self setFileList
III. Define the aspect selectors of the EntryFields as oldFileName and newFileName. Both the Cancel- and the OK-button already have default aspects, which are correct for our need. Finally, make the oldFileName entry field a read-only field, by selecting the Read Only attribute.
If you like to play with the previous example, the class of this example
can be found as "SimpleGUIDemoApplication" in the "CodingExamples_GUI"
namespace (autoloaded). Also, the corresponding source code can be found
in "doc/coding/SimpleGUIDemoExample.st".
The following startup and release messages are of particular interest:
A redefinition of this method is a good place to confirm close requests of the user to the running application. For example, there can be opened a dialog asking the user whether the contents of the application.should be saved or wasted, before the application is going to shut down. If the user does not confirm, the super send is simply not performed.
Copyright © 1998 eXept Software AG, all rights reserved