In this section we will create a useful new class which has features of both 'ScheduledWindow' and 'CompositePart'. An instance of 'ScheduledWindow' can only have one display component. And an instance of 'CompositePart' can group several display components together. But an instance of 'CompositePart' cannot handle size, display position, label, and the blue button menu of a window of type 'ScheduledWindow'.
So, we will create a new class named 'EngiTopView', which has both of these features. Program 3-1 shows the use of 'EngiTopView'. (Before you can execute Program 3-1, you must 'file in' Appendix 01 and also see the Patch for VisualWorks 2.0)
Program-3-1: (EngiTopView, LookPreferences, View; scrollbar) --------------------------------------------------------------------- | topView edgeDecorator1 edgeDecorator2 | topView := EngiTopView model: nil label: 'EngiTopView' minimumSize: 250 @ 250. edgeDecorator1 := LookPreferences edgeDecorator on: View new. edgeDecorator1 noMenuBar. edgeDecorator1 noVerticalScrollBar. edgeDecorator1 noHorizontalScrollBar. edgeDecorator2 := LookPreferences edgeDecorator on: View new. edgeDecorator2 noMenuBar. edgeDecorator2 noVerticalScrollBar. edgeDecorator2 noHorizontalScrollBar. topView add: edgeDecorator1 in: (0 @ 0 corner: 1 @ 0.5). topView add: edgeDecorator2 in: (0 @ 0.5 corner: 1 @ 1). topView open ---------------------------------------------------------------------
'EngiTopView' is used just like 'ScheduledWindow' but may have more than one display component as 'CompositePart'. You can send 'add:in:' message to 'topView' which is an instance of 'EngiTopView'. Program 3-2 shows that 'EngiTopView' can accept a 'popUp' message to open a window, in which case it returns a Boolean value indicating whether the user clicked on 'accept' or 'cancel'. ?? [default buttons don't work on the Mac. They highlight, but when the user presses Enter, nothing happens]
Program-3-2: (EngiTopView, LookPreferences, View; scrollbar; popUp) --------------------------------------------------------------------- | topView edgeDecorator1 edgeDecorator2 aBoolan | topView := EngiTopView new. topView minimumSize: 250 @ 290. edgeDecorator1 := LookPreferences edgeDecorator on: View new. edgeDecorator1 noMenuBar. edgeDecorator1 noVerticalScrollBar. edgeDecorator1 noHorizontalScrollBar. edgeDecorator2 := LookPreferences edgeDecorator on: View new. edgeDecorator2 noMenuBar. edgeDecorator2 noVerticalScrollBar. edgeDecorator2 noHorizontalScrollBar. topView add: edgeDecorator1 in: (0 @ 0 corner: 1 @ 0.5). topView add: edgeDecorator2 in: (0 @ 0.5 corner: 1 @ 1). aBoolan := topView popUp. ^aBoolan ---------------------------------------------------------------------
This program opens a dialog-type-window which has two buttons in the lower part. One is an 'accept' button which is highlighted to indicate it is the default button and the other is a 'cancel' button.
'EngiTopView' accepts various messages as follows.
Open a window with 'accept' as a default button and 'cancel' as a normal button.
--------------------------------------------------------------------- EngiTopView new popUpAcceptCancel: true ---------------------------------------------------------------------
Open a window with 'accept' and 'cancel' buttons, but no default.
--------------------------------------------------------------------- EngiTopView new popUpAcceptCancel: nil ---------------------------------------------------------------------
Open a window with 'cancel' as a default button and 'accept' as a normal button.
--------------------------------------------------------------------- EngiTopView new popUpAcceptCancel: false ---------------------------------------------------------------------
Open a window with 'yes' as a default button and 'no' as a normal button.
--------------------------------------------------------------------- EngiTopView new popUpYesNo: true ---------------------------------------------------------------------
Open a window with both 'yes' and 'no' as normal buttons.
--------------------------------------------------------------------- EngiTopView new popUpYesNo: nil ---------------------------------------------------------------------
Open a window with 'no' as a default button and 'yes' as a normal button.
--------------------------------------------------------------------- EngiTopView new popUpYesNo: false ---------------------------------------------------------------------
Open a window with 'agree' as a default button and 'retract' as a normal button.
--------------------------------------------------------------------- EngiTopView new popUp: true trueLabel: 'agree' falseLabel: 'retract' ---------------------------------------------------------------------
Open a window with 'okay' as the only button, and also as the default.
--------------------------------------------------------------------- EngiTopView new popUpOkay: true ---------------------------------------------------------------------
Open a window with 'okay' as a normal button.
--------------------------------------------------------------------- EngiTopView new popUpOkay: nil ---------------------------------------------------------------------
Open a window with 'I see' as default button.
--------------------------------------------------------------------- EngiTopView new popUp: true label: 'I see' ---------------------------------------------------------------------
Let us design and implement 'EngiTopView' given the requirements illustrated above. First we make 'EngiTopView' a sub class of 'CompositePart'. This provides us with a CompositePart to group display components together into one object. Next, consider how we can make 'EngiTopView' behave like 'ScheduledWindow'. To do this, 'EngiTopView' must have 3 instance variables (model,label,extent). Program 3-3 shows the resulting class definition of 'EngiTopView'.
Program-3-3: (EngiTopView, CompositePart; model, label, extent) ---------------------------------------------------------------------- CompositePart subclass: #EngiTopView instanceVariableNames: 'model label extent' classVariableNames: '' poolDictionaries: '' category: 'Engi-Interface' ----------------------------------------------------------------------
A brief description of Class Methods follows.
(To create instances) model: aModel label: labelString minimumSize: minimumSize new (Each of these methods returns an instance of 'LayoutFrame') frameFraction: fractionRectangle frameFraction: fractionRectangle offset: offsetRectangle frameOffset: offsetRectangle A brief description of Instance Methods follows. (To access window labels) label label: aString (To access the minimum size of the window) minimumSize minimumSize: aPoint (To access the model) model model: aModel (Open/Close windows) close open popUp popUp: aBooleanOrNil label: labelString popUp: aBooleanOrNil trueLabel: trueLabel falseLabel: falseLabel popUpAcceptCancel: aBooleanOrNil popUpOkay: aBooleanOrNil popUpYesNo: aBooleanOrNil (To return an instance of 'LayoutFrame') frameFraction: fractionRectangle frameFraction: fractionRectangle offset: offsetRectangle frameOffset: offsetRectangle (Minimum window size of a pop up dialog) defaultPopUpMinimumSize (To set the model, label and minimum size of a window) model: aModel label: labelText minimumSize: minimumSize
The most difficult part of this program is 'popUp:trueLabel:falseLabel:'. Here is the method.
Program-3-4: (EngiTopView, CompositePart, ScheduledWindow, Button, ValueHolder, PluggableAdaptor, Rectangle, InputState; popUp) ---------------------------------------------------------------------- popUp: aBooleanOrNil trueLabel: trueString falseLabel: falseString | topWindow compositePart aModel trueButton falseButton aRectangle | topWindow := ScheduledWindow model: model label: label minimumSize: extent. topWindow controller: EngiTopPreemptor new. compositePart := CompositePart new. aModel := ValueHolder new. trueButton := Button trigger. aBooleanOrNil = true ifTrue: [trueButton beDefault]. trueButton label: trueString. trueButton model: ((PluggableAdaptor on: aModel) getBlock: [:m | false] putBlock: [:m :v | aModel value: true. self close] updateBlock: [:m :a :v | false]). falseButton := Button trigger. aBooleanOrNil = false ifTrue: [falseButton beDefault]. falseButton label: falseString. falseButton model: ((PluggableAdaptor on: aModel) getBlock: [:m | false] putBlock: [:m :v | aModel value: false. self close] updateBlock: [:m :a :v | false]). compositePart add: self in: (self frameFraction: (0 @ 0 corner: 1 @ 1) offset: (0 @ 0 corner: 0 @ -40)). compositePart add: trueButton in: (self frameFraction: (0.3 @ 1 corner: 0.3 @ 1) offset: (-40 @ -35 corner: 40 @ -5)). compositePart add: falseButton in: (self frameFraction: (0.7 @ 1 corner: 0.7 @ 1) offset: (-40 @ -35 corner: 40 @ -5)). topWindow component: compositePart. aRectangle := Point zero extent: (topWindow minimumSize max: self defaultPopUpMinimumSize). aRectangle := aRectangle align: aRectangle center with: InputState default mousePoint. topWindow openDialogIn: aRectangle. ^aModel value ----------------------------------------------------------------------
At first glance, this method is might be difficult to understand. However, if you master 'ScheduledWindow' and 'CompositePart', you'll be able to understand 'EngiTopView' also.
The 4th step is expressed as a block closure of a button model's 'putBlock'. This block closure is evaluated when the button is pressed. Please notice line 8 which specifies an instance of 'EngiTopPreemptor' as the controller of the 'ScheduledWindow'. This controller handles all events (keyboard, mouse, etc). It has the highest processing priority and once it gets control, it does not relenquish it until it completes.
You might recall how a dialog or pop-up window disappears when you click the mouse outside of the window. In that case 'StandardSystemController' is used as the controller which releases control from the window when the mouse cursor is moved out of the window. Thus, when you click the mouse on another window, that window is raised. When 'EngiTopPreemptor' is used to open a 'EngiTopView' as a dialog or pop-up, once it gets control it does not relenquish it until a button is clicked. 'EngiTopPreemptor' is a specialized version of 'StandardSystemController'.