This section explains how to program a window that replaces its subview with a different subview. On the top of the window we place two buttons to select the subview to be displayed. When the switch Drawing is pressed, the two subviews of a simple graphics demo are shown:
When the switch Text is pressed, the text style demo is shown:
The example reuses code from two previous examples.
The change set for this example is VariableViewDemo.1.cs. This change set is complete and independent from other change sets. You do not need to file in change sets of other examples.
For this example we need two auxiliary classes:
ActivationSwitch is a subclass of Switch that cannot be switched off by its controller.
Switch subclass: #ActivationSwitch instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'MVCTutorial-AdvancedExamples
okToChange " a change is only allowed when the switch is currently off. " ^on not
The controller will send this message when you set the instance variable askBeforeChanging to true.
ContainerView is a view that has protocol to
The design of the subview follows the pattern of pluggable views: The view keeps a method selector which it uses
View subclass: #ContainerView instanceVariableNames: 'subviewSelector' classVariableNames: '' poolDictionaries: '' category: 'MVCTutorial-AdvancedExamples'
on: aModel subviewSelector: aSelector window: aRectangle ^self new window: aRectangle; setModel: aModel subviewSelector: aSelector
setModel: aModel subviewSelector: aSelector self model: aModel. subviewSelector := aSelector. aModel perform: aSelector with: self.
update: aParam aParam = subviewSelector ifTrue: [self releaseSubViews. model perform: subviewSelector with: self. self display; emphasize ]
The example itself uses the model
Model subclass: #VariableSubViewExample instanceVariableNames: 'switch1 switch2 currentAspect' classVariableNames: '' poolDictionaries: '' category: 'MVCTutorial-AdvancedExamples'
Variables switch1 and switch2 keep two instances of ActivationSwitch, the models of the two PluggableSwitchViews. The variable currentAspect keeps a message selector of a message that is sent to provide the subviews for a ContainerView.
An auxiliary method is used to create switch labels in a suitable style and size:
labelFor: aString " create a DisplayText for the given string. Change this method to display the switch labels with a different text style or in a different size. " | text textStyle | text := Text string: aString attribute: TextEmphasis normal. textStyle := TextStyle default copy. textStyle defaultFontIndex: (textStyle fontIndexOfPointSize: 18). ^DisplayText text: text textStyle: textStyle.
This method is used twice in the open method:
open "VariableSubViewExample open" | topView model switch1 switch2 subviewContainer | model := self new. topView := ColorSystemView new. topView label: 'Variable Subview Demo'; model: model; borderWidth: 1. switch1 := PluggableButtonView on: model switch1 getState: #isOn action: #switch. switch1 label: (self labelFor: 'Drawing'); borderWidth: 1; askBeforeChanging: true; window: (0 @ 0 extent: 100 @ 15). topView addSubView: switch1. switch2 := PluggableButtonView on: model switch2 getState: #isOn action: #switch. switch2 label: (self labelFor: 'Text'); borderWidth: 1; askBeforeChanging: true; window: (0 @ 0 extent: 100 @ 15). topView addSubView: switch2 toRightOf: switch1. subviewContainer := ContainerView on: model subviewSelector: #subViewAccessor: window: (0 @ 0 extent: 200 @ 300). topView addSubView: subviewContainer below: switch1. topView controller open
Here we create the models for the two switches and initialize the variable currentAspect. With the initial values that are used here, the window will be opened with the drawing.
initialize switch1 := ActivationSwitch newOn onAction: [self button1IsOn]. switch2 := ActivationSwitch newOff onAction: [self button2IsOn]. currentAspect := #addGraphicView:
initialExtent ^500 @ 340
When a switch is turned on, it
button1IsOn switch2 turnOff. currentAspect := #addGraphicView:. self changed: #subViewAccessor:
button2IsOn switch1 turnOff. currentAspect := #addTextView:. self changed: #subViewAccessor:
These methods are needed to fetch the models for the two PluggableButtonViews.
switch1 ^switch1
switch2 ^switch2
Method subViewAccessor delegates the task to provide subviews for the requestor to one of the methods addGraphicView, addTextView:.
subViewAccessor: aViewContainer self perform: currentAspect with: aViewContainer
addGraphicView: subviewContainer GraphicalDemo2 addSubviewsFor: subviewContainer.
addTextView: subviewContainer TextStyleDemo addSubviewsFor: subviewContainer.
In TextStyleDemo, we have to add this class method:
addSubviewsFor: container | model frame textView fontList fontSizesList attributesList | frame := container window. model := self new. textView := PluggableTextView on: model text: #getText accept: nil readSelection: nil menu: #getTextMenu:. textView borderWidth: 1. textView window: (frame width *4 /20 @ 0 extent: frame width * 16/20 @ frame height). container addSubView: textView.< fontList := PluggableListView on: model list: #fontFamilyList selected: #currentFontFamily changeSelected: #selectFontFamily: menu: nil keystroke: nil. fontList controller: (fontList defaultController terminateDuringSelect: true); autoDeselect: false. fontList borderWidth: 1. fontList window: (0 @ 0 extent: (frame width * 4/20) @ (frame height / 3)). container addSubView: fontList toLeftOf: textView. fontSizesList := PluggableListView on: model list: #fontSizesList selected: #currentFontSize changeSelected: #selectFontSize: menu: nil keystroke: nil. fontSizesList autoDeselect: false. fontSizesList borderWidth: 1. fontSizesList window: (0 @ 0 extent: (frame width * 4 / 20) @ (frame height / 3)). container addSubView: fontSizesList below: fontList. attributesList := PluggableListViewOfMany on: model list: #textAttributesList primarySelection: #listIndex changePrimarySelection: #toggleListIndex: listSelection: #listSelectionAt: changeListSelection: nil menu: nil keystroke: nil. attributesList borderWidth: 1. attributesList window: (0 @ 0 extent: (frame width * 4 / 20) @ (frame height/3)). container addSubView: attributesList below: fontSizesList.
As far as it is now developed, this example has an annoying deficiency: A subview that is again selected, comes up with some initial settings, not with the settings that it had when is was replaced with another subview. It turns out that is deficiency can be fixed easily. This is what we will discuss in the next section.