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.