parent previous next question (Smalltalk Textbook 05)

EngiMenuMaker

In this section we will study the 'PopUpMenu' class. Program 5-1 shows how to make an instance of 'PopUpMenu' and present a menu on the screen. If you select one of items of the menu, the menu disappears.


Program-5-1: (PopUpMenu; startUp)
----------------------------------------------------------------------
| aPopUpMenu |
aPopUpMenu := PopUpMenu labelArray: #('copy' 'cut' 'paste').
aPopUpMenu startUp
----------------------------------------------------------------------

So you see how easy it is to pop up a menu. Next you'll want to know how to get a return value that indicates which item of the menu is selected. To do this, you send a 'startUp' message to 'aPopUpMenu' like Program 5-4. You see the return value is 1 if you select 'copy', 2 if you select 'cut' and 3 if you select 'paste'. The value returned is just the offset of the menu item chosen.


Program-5-4: (PopUpMenu; startUp)
----------------------------------------------------------------------
| aPopUpMenu aResult |
aPopUpMenu := PopUpMenu labelArray: #('copy' 'cut' 'paste').
aResult := aPopUpMenu startUp.
Transcript cr; show: aResult printString
----------------------------------------------------------------------

You can return something besides a number by using the message 'labelArray:values:' to create an instance. See Program 5-5. This program returns the string 'COPY' if you select 'copy', the string 'CUT' if you select 'cut' and returns the string 'PASTE' if you select 'paste' in the menu, otherwise it returns 0.


Program-5-5: (PopUpMenu; labelArray:values:, startUp)
----------------------------------------------------------------------
| aPopUpMenu aResult |
aPopUpMenu := PopUpMenu
                  labelArray: #('copy' 'cut' 'paste')
                  values: #('COPY' 'CUT' 'PASTE').
aResult := aPopUpMenu startUp.
Transcript cr; show: aResult printString
----------------------------------------------------------------------

Next is an example of how to use the 'labelArray:lines:values:' message to create a PopUpMenu instance. You should see a divider line between 'paste' and ' do it' in the menu.


Program-5-5: (PopUpMenu; labelArray:lines:values:, startUp)
----------------------------------------------------------------------
| labelArray valueArray aPopUpMenu aResult |
labelArray := #('copy' 'cut' 'paste' 'do it' 'print it' 'inspect').
valueArray := #('COPY' 'CUT' 'PASTE' 'DO IT' 'PRINT IT' 'INSPECT').
aPopUpMenu := PopUpMenu
                  labelArray: labelArray
                  lines: #(3)
                  values: valueArray.
aResult := aPopUpMenu startUp.
Transcript cr; show: aResult printString
----------------------------------------------------------------------

Try changing 'lines: #(3)' to 'lines: #(2 4)'. What happens?

Next is a more complicated example of a structured menu.


Program-5-6: (PopUpMenu, Array; submenu, labelArray:values:, startUp)
----------------------------------------------------------------------
| editingMenu evaluatingMenu
  labelArray valueArray aPopUpMenu aResult |
editingMenu := PopUpMenu
                  labelArray: #('copy' 'cut' 'paste')
                  values: #('COPY' 'CUT' 'PASTE').
evaluatingMenu := PopUpMenu
                  labelArray: #('do it' 'print it' 'inspect')
                  values: #('DO IT' 'PRINT IT' 'INSPECT').
labelArray := #('editing' 'evaluating').
valueArray := Array with: editingMenu with: evaluatingMenu.
aPopUpMenu := PopUpMenu
                  labelArray: labelArray
                  lines: #(1)
                  values: valueArray.
aResult := aPopUpMenu startUp.
Transcript cr; show: aResult printString
----------------------------------------------------------------------

You see a main-menu with two sub-menus. The 'editing' sub-menu contains 'copy','cut','paste' and the 'evaluation' sub-menu contains 'do it', 'print it','inspect'.

Let me give you an applied question. Display a pop up menu which contains all the instance messages that the 'PopUpMenu' class has. The answer is Program 5-7.


Program-5-7: (PopUpMenu, Array; labelArray:values:, startUp, selectors)
----------------------------------------------------------------------
| instanceMessages aPopUpMenu aResult |
instanceMessages := PopUpMenu selectors asArray.
aPopUpMenu := PopUpMenu
                  labelArray: instanceMessages
                  values: instanceMessages.
aResult := aPopUpMenu startUp.
Transcript cr; show: aResult printString
----------------------------------------------------------------------

Now change 'PopUpMenu selectors asArray' to 'PopUpMenu selectors asSortedCollection asArray'. What happens?

How about another applied question. Display a pop up menu which contains all instance messages and also all class messages 'Menu' class has. Also, add a line to the menu to separate instance messages and class messages. Program 5-8 is the answer.


Program-5-8: (PopUpMenu; labelArray:lines:values:, startUp, selectors)
----------------------------------------------------------------------
| aClass instanceMessages classMessages allMessages aPopUpMenu aResult |
aClass := Menu.
instanceMessages := aClass selectors asSortedCollection asArray.
classMessages := aClass class selectors asSortedCollection asArray.
allMessages := instanceMessages , classMessages.
aPopUpMenu := PopUpMenu
                  labelArray: allMessages
                  lines: (Array with: instanceMessages size)
                  values: allMessages.
aResult := aPopUpMenu startUp.
Transcript cr; show: aResult printString
----------------------------------------------------------------------

Do you feel like you're getting to know pop up menus? Finally let me give you a practical 'EngiMenuMaker' class to create pop up menus. Appendix 03 contains the 'EngiMenuMaker' source. You specify a collection of pairs of a menu item and a return value to 'EngiMenuMaker' as shown in Program 5-9. Specify 'nil' to get a separator line.


Program-5-9: (EngiMenuMaker, OrderedCollection; menu)
----------------------------------------------------------------------
| collection menu result |
collection := OrderedCollection new.
collection add: 'again' -> 'AGAIN'.
collection add: 'undo' -> 'UNDO'.
collection add: nil.
collection add: 'copy' -> 'COPY'.
collection add: 'cut' -> 'CUT'.
collection add: 'paste' -> 'PASTE'.
collection add: nil.
collection add: 'do it' -> 'DO IT'.
collection add: 'print it' -> 'PRINT IT'.
collection add: 'inspect' -> 'INSPECT'.
menu := EngiMenuMaker fromCollection: collection.
result := menu startUp.
Transcript cr; show: result printString
----------------------------------------------------------------------

Program 5-10 creates a nested menu.


Program-5-10: (EngiMenuMaker, OrderedCollection; nested menu)
----------------------------------------------------------------------
| collection menu result |
collection := OrderedCollection new.
collection add: 'editing' -> 
            [| editing |
            editing := OrderedCollection new.
            editing add: 'again' -> 'AGAIN'.
            editing add: 'undo' -> 'UNDO'.
            editing add: nil.
            editing add: 'copy' -> 'COPY'.
            editing add: 'cut' -> 'CUT'.
            editing add: 'paste' -> 'PASTE'.
            EngiMenuMaker fromCollection: editing] value.
collection add: 'execution' -> 
            [| execution |
            execution := OrderedCollection new.
            execution add: 'do it' -> 'DO IT'.
            execution add: 'print it' -> 'PRINT IT'.
            execution add: 'inspect' -> 'INSPECT'.
            EngiMenuMaker fromCollection: execution] value.
menu := EngiMenuMaker fromCollection: collection.
result := menu startUp.
Transcript cr; show: result printString
----------------------------------------------------------------------

Appendix 03 contains example1 and example2 which use block closures. These are similar programs to the above two. Please 'file in' Appendix 03 and try the example class methods.


parent previous next question
Copyright (C) 1994-1996 by Atsushi Aoki
Translated by Kaoru Rin Hayashi & Brent N. Reeves