Let’s star the exploration with a few components to ease the creation of a GUI.
Sometime, label may have a tendency to occupy more place than available. It becomes particularly true when you do not control the content of a label when an application is translated in other languages, with a more or less verbose way to express messages or concepts.
The SqueezeLabelMorph tries its best to contract a message in a given amount of character. It is part of the UI-Core.pck.st package, in a Workspace install it by executing the appropriate command:
Feature require: 'UI-Core'
This kind of label is set with a minimum number of characters it is willing to display. If that minimum number is lower than the label’s content, it will contract the text:
(SqueezeLabelMorph contents: 'I am a very looong label with maybe not enought place for me' minCharsToShow: 20) openInWorld.
Example 6.1: Label that squeezes
The content of the label is very long, particularly as we inform the label accepts to be squeezed to a minimum of 20 characters. Observe how such squeezed label reveals its complete content in a balloon text when the pointer is hovering it.
Figure 6.1: A label squeezed to 20 characters
When more space is made available to the label, more text of its content is revealed:
Figure 6.2: A squeezed label given some more space
When packing a SqueezeLabel in a layout morph with other morph, it will have consequence on the minimal width of the owner layout.
Compare the two examples with a squeezed and regular label:
| label row | label := SqueezeLabelMorph contents: 'I am a very long label' minCharsToShow: 15. row := LayoutMorph newRow. row addMorph: label; addMorph: (TextModelMorph withText: 'some input' :: morphExtent: 100@0). row openInWorld
Example 6.2: Squeezed label for a text entry
The whole layout is contracted to a smaller width
Figure 6.3: A text entry with a squeezed label
when comparing to a regular label use case
Figure 6.4: A text entry with a regular label
| row | row := LayoutMorph newRow. row addMorph: (LabelMorph contents: 'I am a very long label'); addMorph: (TextModelMorph withText: 'some input' :: morphExtent: 100@0). row openInWorld
Example 6.3: Regular label for a text entry
It is up to you to decide between the compactness of the GUI and the readability of the labels.
In Text entry, we presented a quite complex and feature complete class to handle multiple line of text editing. When only one line editing is needed it is a bit overkill, in that circumstance you can alternatively use the TextEntryMorph, part of the UI-Entry package:
Feature require: 'UI-Entry'
This class is quite simple and contrary to the TextModelMorph it does need a text model. Therefore there is no such things as changed and update mechanism involved, it is a passive morph.
However, it offers two options to interact with other objects:
Let’s experiment with the associated object answering to the #accept and #cancel messages. We need a TextEntryDemo class:
Object subclass: #TextEntryDemo instanceVariableNames: 'value entry' classVariableNames: '' poolDictionaries: '' category: 'DesignGUI-Booklet'
At initialize time we create all the needed objects:
initialize
value := '42'. entry := TextEntryMorph contents: value. entry acceptCancelReceiver: self. entry openInWorld
Now we make our TextEntryDemo to respond to the #accept and #cancel messages.
When pressing Enter, we update our value attribute
accept
value := entry contents. 'I accepted the input ' print. ('value = ', value) print
but when pressing Esc we just delete the morph
cancel
'I discarded the input' print. entry delete
Observe we only need the accessors #contents/#contents: to set and retrieve its content. It is a very simple class to use. If dependency mechanism where to be needed, an intermediate object as the TextEntryDemo can still be used with the observer pattern.
In example of text entry, we use layout to associate a text entry with a label. It is something very common when building a GUI, the LabelGroup does exactly that for an arbitrary number of morphs.
Feature require: 'UI-Widgets'
When creating a LabelGroup, we associate labels and widgets/controls in an unique group. In return the user gets a layout to be inserted in a dialog or a window.
(LabelGroup with: { 'First Name:' -> TextEntryMorph new. 'Last Name:' -> TextEntryMorph new}) openInWorld
Example 6.4: Labelling a group of morphs
Figure 6.5: Text entries associated with labels
The group also gives access to the controls, although it is not a very efficient way to access the input widgets used in the group, it is handy:
Figure 6.6: Access to the controls of a label group
A label group is useful when constructing small dialog, in the next section we build one with the morphs we learnt in this section and the previous ones.
Small window the user interact with are called dialog or panel, Cuis-Smalltalk-UI offers several alternatives to use.
Feature require: 'UI-Panel'
Let’s rewrite the example of text entry with what we just learnt. The end result will look like this:
Figure 6.7: A greeting dialog
In the hierarchy provided by the UI-Panel package, we use the DialogPanel class. It offers both an area to plug our interactive components and an area for our button.
DialogPanel subclass: #GreetingPanel instanceVariableNames: 'firstName lastName greetLabel' classVariableNames: '' poolDictionaries: '' category: 'DesignGUI-Booklet'
We set the default color of the dialog
defaultColor
^ `Color lightOrange`
then install the iconic buttons for its title
initialize
super initialize. self showButtonsNamed: #(close expand)
To know about the available buttons for the title bar of a panel, read the class WindowTitleMorph. The expand action needs a rewrite of its associated action
expandButtonClicked
self fullScreen
We set our components in the dedicated newPane method of the Dialog hierarchy:
newPane
| column group | column := LayoutMorph newColumn :: color: Color transparent; gap: 10 . group := LabelGroup with: { 'First Name: ' -> (firstName := TextEntryMorph contents: '') . 'Last Name: ' -> (lastName := TextEntryMorph contents: '') }. greetLabel := LabelMorph contents: '' font: nil emphasis: 1. column addMorph: group layoutSpec: (LayoutSpec fixedWidth: 300); addMorph: greetLabel layoutSpec: ( LayoutSpec proportionalWidth: 0 fixedHeight: 20 offAxisEdgeWeight: #center). ^ column
The button has its own method too for installation:
newButtonArea
^ PluggableButtonMorph model: self action: #greet label: 'Greet' :: color: self widgetsColor
Finally we implement the greet action of the button to update the greetLabel:
greet
greetLabel contents: ( 'Hello {1} {2}!' format: {firstName contents. lastName contents})