NS BASIC Tech Note January 18, 1997 35. Using PICKER widgets to create buttons that display menus ----------------------------------------------------------------- This technote shows you how to create a popup menu for the i button in the APP widget, as well as a TEXTBUTTON that displays a menu when tapped. These two behaviors let you create applications with very standard Newton 2.0 interfaces! LetÕs create a simple program that displays an APP widget. The user taps the close box to exit the program, and can tap the button to get information, help, or set preferences. There is also a Show button that is used to select a task. WeÕll fill in some task layouts in the next section. 0010 REM Widget Example 0020 appSize := getappparams().appAreaBounds 0030 w1Spec := {Title: "Widget Example",  GOTO:'appDone, GOSUBinfo: 'showInfo} 0040 w2Spec:={text:"\uFC01\u Show", GOSUB:'showButtonTap, viewBounds: SETBOUNDS(appSize.left+26, appSize.bottom-19, appSize.left+70, appSize.bottom-6), viewFont:  {family:'espy, face:1, size:9}, viewFormat:67109457} 0050 WINDOW w1, w1Spec, "APP" 0060 WINDOW w2, w2Spec, "TEXTBUTTON" 0070 SHOW w1, w2 0080 WAIT -1 0090 appDone: REM tapped close box 0100 HIDE 0110 PRINT "Closed." 0120 END 0130 showInfo: REM Info button tapped 0140 popBounds :=  U.w1Spec.base.appInfo:GLOBALBOX() 0150 popBounds.left = popBounds.right+2 0160 iPopSpec := {GOSUB:'pickiChosen,  pickItems:["About","Help","Prefs"], Bounds:popBounds} 0170 WINDOW wInfo, iPopSpec, "PICKER" 0180 SHOW wInfo 0190 RETURN 0200 pickiChosen: REM Picked 0210 ON (iPopSpec.viewValue + 1)  GOTO iAbout,iHelp,iPrefs 0220 iAbout: Notify("About",  "This is an example.") 0230 RETURN 0240 iHelp: Notify("Help",  "There is no Help.") 0250 RETURN 0260 iPrefs: Notify("Prefs",  "There are no Prefs.") 0270 RETURN 0280 showButtonTap: REM 0290 popBounds :=  U.w2Spec:GLOBALBOX() 0300 popBounds.left = popBounds.right+2 0310 showSpec := {GOSUB:'pickShowChosen,  pickItems:["Some","Most","All",  'PICKSOLIDSEPARATOR, "Less", "More"], Bounds:popBounds} 0320 WINDOW wShow, showSpec, "PICKER" 0330 SHOW wShow 0340 RETURN 0350 pickShowChosen: REM 0360 Notify("Show", "Option \"" &  showSpec.pickItems[showSpec.viewValue] & "\" is not implemented!") 0370 RETURN There are actually four widgets created in this program. The first two are the APP and TEXTBUTTON widgets that make up the application. The windowSpecs for these are defined in lines 30 and 40. They are created using WINDOW Statements in lines 50 and 60, and are displayed with the SHOW Statement in line 70. Line 80 is our event loop. The APP has a GOTO element in its windowSpec so we can exit if the user taps the close box. The APP also has a GOSUBinfo element so we can process a tap on the button. The subroutine that processes that tap is in lines 130-190. It finds the coordinates of the button in line 140 and uses that to place a PICKER widget right next to the button. The PICKER widget must be created each time it is used, so this is one case where you must use the WINDOW Statement. Once the PICKER is SHOWn the subroutine returns to our event loop in line 80. If the user selects from the menu then the subroutine in line 200-270 is called. We access the viewValue element of the PICKER widget to get the offset of the selected menu item, and use that to display a message. The Show TEXTBUTTON works is the same way as the i button does. Using WINDOW to create widgets gives you complete control over the settings used. In our example we were able to compute the viewBounds for the TEXTBUTTON and both of our PICKER widgets and then use the computed values to place these widgets exactly where we wanted. We also had to do quite a bit of work to create all the widgets. The next section uses the WIDGETDEF Statement and the Visual Designer to add one of the five layouts that can be displayed by tapping the Show or i buttons and selecting from the resulting popup menu. In our example program we created the application portion of the interface one widget at a time. ThatÕs fine up to now, but suppose we had five separate layouts as the example implies by the Show button. If we had to create each layout one widget at a time weÕd have a lot of work on our hands. Instead, we can use the WIDGETDEF Statement and the Visual Designer to create each layout right on the Newton. To use the Visual Designer you begin by adding as many WIDGETDEF Statements as you have unique layouts. In our example weÕll need five WIDGETDEF Statements. Next, you used the EDIT Command on each WIDGETDEF Statement to actually create the layout. Finally, you add WINDOW, SHOW, and HIDE Statements, and probably some additional event loops, to complete the interface. Begin by adding a Statement defining the first layout in the program: 62 WIDGETDEF layout_some The name you use after WIDGETDEF is the name of the variable that will hold all the windowSpecs for the widgets in the layout. Be sure not to use the variable for some other value or youÕll overwrite the windowSpecs. Open the Visual Designer by using the EDIT Command: * EDIT 62 The Visual Designer opens and displays a new, empty layout. I Added several widgets to this layout and then closed the Visual Designer. To actually display a layout created by a WIDGETDEF Statement you still need to use the WINDOW and SHOW Statements. The WINDOW Statement is used in a simplified form: 64 WINDOW win_some, layout_some Notice that we donÕt specify a widget. ThatÕs because each windowSpec in layout_some includes a widgetType element. Since a layout contains several widgets, win_some contains an array of window numbers after this Statement executes. You can SHOW and HIDE all the widgets in a layout by using the array of window numbers in a SHOW or HIDE Statement. You can also SHOW or HIDE a specific widget if you use the desired array element in a SHOW or HIDE Statement. We show this in the example in the next section. Once youÕve shown a layout, you still need to use an event loop to wait for the user to finish interacting with the layout. This means there must be some way for the user to exit the layout. When they do you typically will want to access the values the user entered into the layout and then HIDE it. The program below is an expanded version of the example presented earlier. There is only one layout implemented here (the Show Some layout) but you could easily add more. 0010 REM Visual Designer Example 0020 appSize := getappparams().appAreaBounds 0030 w1Spec := {Title: "Widget Example",  GOTO:'appDone, GOSUBinfo: 'showInfo} 0040 w2Spec:={text:"\uFC01\u Show", GOSUB:'showButtonTap, viewBounds: SETBOUNDS(appSize.left+26, appSize.bottom-19, appSize.left+70,  appSize.bottom-6), viewFont: {family:'espy, face:1, size:9}, viewFormat:67109457} 0050 WINDOW w1, w1Spec, "APP" 0060 WINDOW w2, w2Spec, "TEXTBUTTON" 0062 WIDGETDEF layout_some :={DatePicker:{widgetType:"datePicker" ,order:0,viewBounds:{top:55,bottom:135,left:4,right:124},vi ewFlags:512,selectedDates:[48864486]},Widget_0:{widgetType: "title",order:1,viewBounds:{left:4,top:28,right:111,bottom: 45},viewJustify:0,viewFont:{family:'espy,face:1,size:12},vi ewFlags:1,text:"Show Some!",viewFormat:0},checkBox:{widgetT ype:"checkbox",order:2,viewValue:NIL,text:"Some Option",vie wBounds:{left:8,top:147,right:108,bottom:167},viewFlags:512 ,viewFont:{family:'espy,face:0,size:10},viewJustify:4},Widg et_3:{widgetType:"newSetClock",order:3,viewBounds:{left:15, top:173,right:78,bottom:236},viewFlags:512,minutes:6,hours: 16},dateToggle:{widgetType:"textButton",order:4,GOSUB:'togg leDate,viewBounds:{left:141,top:88,right:200,bottom:103},vi ewFlags:514,text:"Hide Date",viewFont:{family:'espy,face:1, size:9},viewFormat:67109456}} 0064 WINDOW win_some, layout_some 0070 SHOW w1, w2 0075 layout_visible = NIL 0080 WAIT -1 0090 appDone: REM tapped close box 0100 HIDE 0110 PRINT "Closed." 0120 END 0130 showInfo: REM Info button tapped 0140 popBounds :=  U.w1Spec.base.appInfo:GLOBALBOX() 0150 popBounds.left = popBounds.right+2 0160 iPopSpec := {GOSUB:'pickiChosen,  pickItems: ["About","Help", "Prefs"], Bounds:popBounds} 0170 WINDOW wInfo, iPopSpec, "PICKER" 0180 SHOW wInfo 0190 RETURN 0200 pickiChosen: REM Picked 0210 ON (iPopSpec.viewValue + 1)  GOTO iAbout,iHelp,iPrefs 0220 iAbout: Notify("About",  "This is an example.") 0230 RETURN 0240 iHelp: Notify("Help",  "There is no Help.") 0250 RETURN 0260 iPrefs: Notify("Prefs",  "There are no Prefs.") 0270 RETURN 0280 showButtonTap: REM 0290 popBounds :=  U.w2Spec:GLOBALBOX() 0300 popBounds.left = popBounds.right+2 0310 showSpec := {GOSUB:'pickShowChosen, pickItems: ["Some","Most","All",  'PICKSOLIDSEPARATOR, "Less",  "More"], Bounds:popBounds} 0315 IF layout_visible <> NIL THEN  showSpec.pickItems[layout_visible-1]:= {item:showSpec.pickItems[ layout_visible-1], pickable: TRUE,mark: CHR(8730)} 0320 WINDOW wShow, showSpec, "PICKER" 0330 SHOW wShow 0340 RETURN 0350 pickShowChosen: REM 0355 IF layout_visible <> NIL THEN  ON layout_visible  GOSUB save_some, save_most 0357 layout_visible = showSpec.viewValue + 1 0358 IF showSpec.viewValue = 0 THEN 0360 SHOW win_some 0361 ELSE 0362 Notify("Show", "Option \"" & showSpec.pickItems[ showSpec.viewValue] &  "\" is not implemented!") 0363 END IF 0370 RETURN 1000 save_some: REM 1010 PRINT "Values from layout layout_some:" 1020 PRINT "Date: " & DATENTIME( layout_some.DatePicker.selectedDates[0]) 1030 IF layout_some.checkbox.viewValue  THEN PRINT "Checkbox is checked"  ELSE PRINT "Checkbox is NOT checked" 1040 PRINT "Hours: ";  layout_some.Widget_3.hours; ",  Minutes: ";  layout_some.Widget_3.minutes 1050 HIDE win_some 1060 RETURN 2000 toggleDate: REM 2010 IF layout_some.dateToggle.text =  "Hide Date" THEN 2020 SETVALUE(layout_some.dateToggle,  'text, "Show Date") 2030 HIDE win_some[  layout_some.DatePicker.order] 2040 ELSE 2050 SETVALUE(layout_some.dateToggle,  'text, "Hide Date") 2060 SHOW win_some[  layout_some.DatePicker.order] 2070 END IF 2080 RETURN 3000 save_most: REM 3010 RETURN Lines 62 and 64 define and create the layout for "Show Some". We donÕt SHOW it until the user selects Show, Some. This happens in lines 358 and 360. When the user selects another view to Show the program "saves" the data in the current view. For the Show Some view this is lines 1000-1060. Here you can see how each widget in a layout is addressed by the name you give it in the Visual Designer (lines 1020 and 1030) or by itÕs default name in line 1040. When the Some layout is visible the user can show or hide the DATEPICKER by tapping the Hide Date TEXTBUTTON. The subroutine in lines 2000-2080 is executed when the user taps this button. Based on the name of the button (the text element of its windowSpec) the DATEPICKER widget is either hidden (line 2030) or shown (line 2060). The program uses the order element of the DATEPICKER widget windowSpec to select the correct window number from the win_some array of window numbers. This is better than hard coding a number (in this case, the expression win_some[1] would work) in the SHOW or HIDE Statement. That way if you edit the layout in the Visual Designer and change the ordering of the widgets your code will not need to change.