The reason for this behavior is that the button
may not get a buttonRelease notification,
because at the time you release the mouse button, it is covered by the dialog
(which gets the release event, but does not care).
Thus, the button ``thinks'' that the mouse button is still pressed after the
dialog is closed - until you move the mouse.
This problem only appears if you have the button trigger-on-press or
use the pressAction (in contrast to the default behavior, where the action
is triggered in release).
The following example illustrates that behavior:
a solution to this problem is to either let the button trigger on release
(which is its default) or to manually turn the button back to the off-position
within the action:
|top b dialog|
top := HorizontalPanelView new.
top extent:300@300.
top horizontalLayout:#center; verticalLayout:#center.
dialog := WarningBox label:'some dialog'.
b := Button label:'press me' in:top.
b pressAction:[dialog open].
top open
Notice:
|top b dialog|
top := HorizontalPanelView new.
top extent:300@300.
top horizontalLayout:#center; verticalLayout:#center.
dialog := WarningBox label:'some dialog'.
b := Button label:'press me' in:top.
b pressAction:[dialog open. b turnOff. ].
top open
It is not a good idea to setup a button to perform its action on press -
the default behavior (i.e. action-on-release) allows the user of your
program to change her mind, by moving the mouse-pointer out of the button
before releasing the mouse-button. Action-on-press should only be
used for scrollBar-like positioning buttons.
A calculator-like arrangement is acomplished with:
well, almost - we need to specify a useful layout (by default, the panels
layout is #center).
|top vPanel hPanels buttons|
buttons := (1 to:16) collect:[:i | Button label:i printString].
top := StandardSystemView extent:300@300.
vPanel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
hPanels := (1 to:4) collect:[:i | HorizontalPanelView in:vPanel].
1 to:4 do:[:hPanelNr |
1 to:4 do:[:buttNr |
|butt|
butt := buttons at:((hPanelNr - 1 * 4) + buttNr).
(hPanels at:hPanelNr) addSubView:butt.
].
].
top open
Adding corresponding layout messages fixes this:
The same setup can (of course) be generated if you use the GUI painter.
However, you have to manually add those buttons (use copy-paste ... to create
all those buttons).
|top vPanel hPanels buttons|
buttons := (1 to:16) collect:[:i | Button label:i printString].
top := StandardSystemView extent:300@300.
vPanel := VerticalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.
vPanel horizontalLayout:#fit.
vPanel verticalLayout:#fitSpace.
hPanels := (1 to:4) collect:[:i |
|panel|
panel := HorizontalPanelView in:vPanel.
panel horizontalLayout:#fitSpace.
panel verticalLayout:#fit.
].
1 to:4 do:[:hPanelNr |
1 to:4 do:[:buttNr |
|butt|
butt := buttons at:((hPanelNr - 1 * 4) + buttNr).
(hPanels at:hPanelNr) addSubView:butt.
].
].
top open
constructMyDynamicMenu
|myMenu item|
myMenu := Menu new.
item := MenuItem label:'action1'.
item value:#theActionSelector1.
myMenu addItem:item.
item := MenuItem label:'action2'.
item value:#theActionSelector2.
myMenu addItem:item.
^ myMenu
and an aspect method, which returns a block to construct the menu
whenever invoked:
myDynamicMenu
^ [ self constructMyDynamicMenu]
In the GUI-painter, set the menu-aspect to #myDynamicMenu
.
The above menu will invoke the methods named theActionSelector1
and
theActionSelector2
in the application.
Sometimes, it is useful to invoke the same method, but pass different arguments.
This can be done by giving each menuItem the same selector, and an
additional argument, as in:
FInally, it may be useful to combine some fix menu (as constructed in the
MenuEditor) with a variable part (as constructed above).
constructMyDynamicMenu
|myMenu item|
myMenu := Menu new.
item := MenuItem label:'action1'.
item value:#'theActionSelector:'.
item argument:'Argument 1'.
myMenu addItem:item.
item := MenuItem label:'action2'.
item value:#'theActionSelector:'.
item argument:'Argument 2'.
myMenu addItem:item.
^ myMenu
To do this, define the menu as usual (in the MenuEditor), and save it.
Here, we assume that the menu was saved under the
name baseMenu
.
On the instance side, define a method as above, which should look like:
Notice the
constructMyDynamicMenu
|myMenu item|
myMenu := self class baseMenu decodeAsLiteralArray.
item := MenuItem label:'-'. "/ separator
myMenu addItem:item.
item := MenuItem label:'action1'.
item value:#'theActionSelector:'.
item argument:'Argument 1'.
myMenu addItem:item.
item := MenuItem label:'action2'.
item value:#'theActionSelector:'.
item argument:'Argument 2'.
myMenu addItem:item.
^ myMenu
decodeAsLiteralArray
;
this is needed since the menuSpec methods as generated by the MenuEditor
return a literal-encoded specification (i.e. an encoded Array).
In order to append more items, a real Menu instance is needed.
Copyright © 1995 Claus Gittinger, all rights reserved
<cg at exept.de>