NewBASIC Concepts

This chapter defines a number of NewBASIC's technical concepts, including:


Applications

From NewBASIC's point of view, an application is a program module. A program module roughly corresponds to the file that results when you use the Builder's build option.

A module may load another module. Not all modules have been set up to act as applications. What makes an application module special? From a programming point of view, there's nothing special. The Program Launcher application needs to know about the application module. How exactly it will manage this, depends upon who wrote the Program Launcher.

Application Life Cycle

Between the time it is loaded and unloaded an application will go through the following stages:

  1. The application module is loaded into memory. The application module's module_init( ) routine is called.
  2. The Program Launcher program calls the application module's module_goTo(  ) routine--specifying what "context" the application should start in. The Program Launcher calls the application module's module_show( ) routine--this routine is responsible for bringing up the module's starting UI (user interface).
  3. If the user decides to start using some other application, then the Program Launcher will tell the application module to hide its windows, calling the module_hide( ) routine. If the user switches back to your application, the Program Launcher will call the application module's module_show( ) routine.
  4. At some point, the Program Launcher may need to shut the application down: perhaps the user hasn't used the application in a while and the device needs the memory; perhaps the user has pressed the power button and all applications must shut down. The Program Launcher program calls the application's module_getContext( ) routine. This routine should return a "context" string. (The Program Launcher will store this context string away; the next time the Program Launcher loads the application, it will pass this string to the module_goTo( ) routine.) The application module's module_exit( ) routine will be called last, giving the program a chance to release any resources it may have grabbed.

Context

When the Program Launcher is about to unload an application, it will call that application's module_getContext( ) routine to get a text string. When the Program Launcher loads the application, it will call the application's module_goTo( ) routine, passing that text string. (When the application is loaded for the very first time, the Program Launcher will pass the empty string to module_goTo( ).)

There is no set API (application programming interface) for context strings; it need make sense only to your program. The program uses the string so that it may, when loaded, restore itself to its previous state when it was shut down.

Preventing Unloading

When the device is running out of free memory, the Program Launcher might unload a hidden (non-active) application to free up its memory. If an application computes even while hidden, it wishes to avoid being unloaded as long as possible. To do this, the application module should define and export an integer variable called preventUnload and set it non-zero. For more information, see preventUnload.


Component Tree: Parents & Children

Your program's components are arranged in a hierarchy. In general, this hierarchy is used to keep track of which components should appear inside other components.

Dialog Box Children For example, the children of a dialog box are those components which appear inside the dialog. When the dialog box becomes visible, NewBASIC knows that its children components should become visible. If the dialog is moved a few pixels to the left, the children components should move with it. If the dialog becomes invisible, so should its children components.

Consider the dialog box pictured to the right. When NewBASIC stores the various components that make up this dialog's gadgetry, it will keep track of them using a sort of hierarchy. A part of the hierarchy for the above dialog is shown below.

Parent Child HierarchyParent Child If component A is directly above component B in the hierarchy, we say that component A is component B's parent. We say that component B is component A's child, or one of component A's children.
While those components that can have children may have more than one each, a component may have only one parent.

There are properties corresponding to these ideas. To access a component's parent, use its parent property. To access a component's children, use its numChildren integer (0-). This read-only property is the component's number of children. And its children[] component. This is a read-only array of components, the component's children.

Not all components may have children. Only those components with children listed among their standard properties may have children. Some components may have a strange parent, or a null parent. Specifically, window components (Forms, Dialogs, and Floaters) have a strange parent; so do service components, which are components that never appear on-screen (some examples: Alarms, Busies, Databases, Timers.)

How To Specify A Component's Parent

In the Builder, if you move a component into another component's visual bounds, the Builder will try to make the component you're moving a child of the other. If that doesn't work, the component you're moving may become the other component's sibling--the Builder may assign it the same parent.

In BASIC code, you specify a component's parent by means of its parent property:

choice1.parent = PrefixGroup

Specify the parent of a newly created component, by means of the second argument to the MakeComponent( ) routine, the component's parent component :

MyChoice = MakeComponent("choice", group1)

The MakeComponent( ) routine can take a special keyword to specify a special parent. If the parent is "app," that means that the new component should have no real parent; its only parent will be the application itself--which is not a component.

To find out if component A can be a parent of component B, use the ValidParent( ) built-in BASIC function. You may make one component the child of another in NewBuild by placing the child component within the parent component:

IF ValidParent( compA, compB ) THEN
compB.parent = compA
END IF

Why This Matters

NewBASIC uses the parent/child hierarchy to make sure that certain things that happen to a parent will also happen to its children.


Focus and Active Windows

Comics Database To the user, it appears that when they click in a window, any text they enter interacts with that window, perhaps showing up in a text entry area in the window. By keeping track of the focus and the active windows, you may monitor and specify which component should receive text input.

The "focus" is the single component that will receive text input from the keyboard. To find out (or change) which component is the focus, use the system module's keyboard component focus property:

DIM f AS component
f = system:keyboard.focus


Each window component (each Form, Dialog, and Floater) has a focus property. This property keeps track of the focus within that window. When that window becomes the active window, its focus will become the system focus. If you want a Text/Entry component to receive text directed to a window, make it the window's focus. (You may do this using the Builder by entering the Text/Entry component's name in the Focus area of the window's Properties Box, the "Specific" area.)

Focus Objects To differentiate between the focus and a component which is a window's focus, we sometimes call the focus "the system focus." Windows are most likely to become the active window when they first appear or when the user clicks them.

Windows find out when they start and stop being active windows by receiving _activeChanged( ) events. Handling this event is an opportunity to update the window's focus.

The situation is complicated by the fact that there may be more than one active window. There may be an active form, an active non-modal window, and an active modal window. Whichever of these most recently became active is the true active window.

Tool Windows

There is a type of dialog window that never becomes the active window: the Tool type. This type is useful for allowing the user to interact with a dialog without changing the focus. The tools palette in a graphic building application is an example of this.

Example

Suppose ComicDialog has two children, ComicTitle (an Entry) and ComicDesc (a Text). Each time the users interacts with ComicDialog, they will change its focus property.

If they click in ComicTitle, it becomes the focus; if they click in ComicDesc, ComicDesc becomes the focus.
The following code would ensure that ComicTitle was the default focus when the ComicDialog window becomes active:

SUB ComicDialog_activeChanged(self AS dialog, gain AS integer)
REM This event occurs whenever this window becomes the active
REM non-modal window or stops being the active non-modal
REM window.
REM
REM When it becomes the active window, make the ComicTitle
REM Entry the default focus.
IF gain THEN
self.focus = ComicTitle
END IF
END SUB


Text Input

The user is able to enter text by means of the keyboard.

Keep the following things in mind if your application accepts text input:

It is also possible to generate simulated keyboard events.

The system keeps track of which component should receive text. This component is known as the focus. For more information about the focus, see Focus and Active Windows.

Scrolling Text Fields

To create a vertically scrolling text field, you need a Text component and a Scrollbar component. Set up the following:

For example:

SUB scrollbar1_changed(self AS scrollbar,scrollType AS integer)
text1.firstVisibleLine = self.value
END SUB
SUB text1_numLinesChanged( self AS text )
scrollbar1.maximum = self.numLines
END SUB
SUB text1_scrolled( self AS text )
scrollbar1.value = self.firstVisibleLine
END SUB

Accepting Characters / Rejecting Others

Often, when the users enter text, you'll want to restrict their input to certain characters. For instance, if they are entering a postal code, you might want to only allow numbers and hyphens. Entry and Text components have a filter property which you may use to restrict which characters the component accepts. If the user enters an unacceptable character, the filter rejects the character and sounds an error beep.

There are standard filters which are set up for common situations--accepting only those characters used in filenames, dates, numbers, etc. You may also set up a custom filter, as described in the next section.

Checking Every Character

To allow a Text or Entry to provide a custom reaction to each character as it is entered, set the component's filter to the custom filter and intercept the _filterChar( ) event. You may use this event to reject unwanted characters, thus providing a sort of custom filter. You may also specify that a character other than that received be entered in the text.

Gadget_char( ) Event

Gadgets and Tables can detect text input by means of the _char( ) event. The Gadget/Table receives text only if it has the focus. When handling the _ char( ) event, not that there will be more than one event for each keypress: there is a key-down event, a key-up event, and there may be several key-repeat events.


Mouse Input

There are a number of ways that a program can detect mouse activity:

Which Text Entry Was Most Recently Clicked?

The user may click a text entry area to signal that they wish to enter text there. Any text entered by keyboard will then go to that component. To find out which component is the current recipient of text input, check the system focus. For more information about the focus, see Focus and Active Windows.

Mouse Clicks and Dialog Boxes

There are some dialog boxes that prevent the user from clicking outside their boundaries; which perhaps don't even allow the user to work with windows of other applications. If you have a Dialog or Floater component that should prevent the user from working with other windows, change its type property.

By means of this property, you may make a dialog box modal or system-modal. If the user attempts to click outside of the dialog box on another window, the system ignores the click and sounds an error beep.


Time

You may create various applications such as stopwatches, appointment calendars, and action games that need to keep track of time. There are three component types to make this possible. You will use one or more of them depending on what you need:

  1. Alarm provides a real-time alarm timer. Applications that act like alarm clocks will use this component. The timer is usually only accurate to the minute.
  2. TimeDate keeps track of the system time. At any time, you may query a component of this type to find out the current system date or time. You may ask the component to let you know when the system time or date changes. Applications that need to keep track of the current time or date, will use this component.
  3. Timer generates periodic events at short intervals. They are useful when polling. Applications that need a clock with granularity finer than one minute will use this component type.

STRUCT TimeOfDay and STRUCT Date

Structures are types made from other types. The following structures are used to represent dates and times of day:

STRUCT TimeOfDay
DIM hour AS integer
DIM minute AS integer
DIM second AS integer
END STRUCT
STRUCT Date
DIM year AS integer
DIM months AS integer
DIM day AS integer
END STRUCT

The components that use these structures require that they represent valid times/dates. For example, if you tell an Alarm to set its alarm for 25:00, it raises an error.

Utility Actions

Use a TimeDate component for utility actions when dealing with dates. The GetDaysInMonth( ) action is useful for validating dates. The GetDayOfWeek( ) action returns a number corresponding to the day of week for a date.

The CALENDAR.BAS sample application shows how you might use these utility actions to complete a simple calendar application.

A Clock with Second Accuracy

A TimeDate component returns the system time whenever you ask; and that time will be accurate to the second. It lets you know when the system time changes, but the most often it does this is once a minute. If you wish to provide a clock that is accurate to the second, you need two components:

a TimeDate to provide the system time
a Timer to generate events once a second.

For an example of this, see the ACLOCK.BAS sample application. This application is a clock with a second hand, minute hand, and hour hand. The minute and hour hands are re-drawn in handlers of a TimeDate's _timeChanged( ) event, while the second hand is re-drawn in the Timer's _ring( ) event handler.
Excerpts from the program are shown here.

sub timedate1_timeChanged(self as timedate)
gadget1!Redraw( ) REM Redraw hour, minute hand--
REM this is handled by the Gadget's
REM _draw( ) event handler.
end sub
sub timer1_ring(self as timer)
DIM angle AS float
DIM x1 AS integer
DIM x2 AS integer
DIM y1 AS integer
DIM y2 AS integer
REM Not shown in this code excerpt: we erase
REM the old second hand.

REM Draw the second hand.
SecondsCount = SecondsCount + 1
angle = ( 15- SecondsCount) * 0.104720
x1 = 72 + ( 52 * Cos( angle ))
y1 = 80 - (52 * Sin( angle ))
x2 = 72 + ( 57 * Cos( angle ))
y2 = 80 - (57 * Sin( angle ))
gadget1!DrawLine(x1, y1, x2, y2, BLACK)
end sub


Color

Certain actions and properties make use of color values. Most programmers use the pre-defined color constants to specify a desired color.

You may specify a color in the following places:

Available color constants

The following color constants are available so you can easily specify system colors:
CONST WHITE &Hffffffff
CONST BLACK &Hff000000
CONST GRAY_50 &Hff808080
CONST GREY_50 &Hff808080
CONST DARK_GRAY &Hff555555
CONST LIGHT_GRAY &Hffaaaaaa
CONST DARK_GREY &Hff555555
CONST LIGHT_GREY &Hffaaaaaa
CONST DARK_GREEN &Hff00aa00
CONST LIGHT_GREEN &Hff55ff55
CONST DARK_BLUE &Hff0000aa
CONST LIGHT_BLUE &Hff5555ff
CONST DARK_CYAN &Hff00aaaa
CONST LIGHT_CYAN &Hff55ffff
CONST DARK_PURPLE &Hffaa00aa
CONST LIGHT_PURPLE &Hffff55ff
CONST DARK_ORANGE &Hffaa5500
CONST LIGHT_ORANGE &Hffff5555
CONST YELLOW &Hffffff55
CONST RED &Hffaa0000

Custom Colors

You may specify a color that is not one of the pre-defined constant values. Colors consist of four byte values (numbers 0-255) which have been combined into a long number:

Opacity (0-255) x &H1000000 +
Red (0-255) x &H10000 +
Green (0-255) x &H100 +
Blue (0-255)

For example, the constant LIGHT_GREEN breaks down into the following parts:

&HFF x &H1000000 + (100% opaque)
&H55 x &H10000 + (33% red)
&HFF x &H100 + (100% green)
&H55 (33% blue)

Opacity

You may wonder what the opacity or opaqueness of a color means.
Opacity determines how much this color will obscure what's beneath it.

Color Opacity

Here we see six horizontal rectangles with different colors and opacities. They have been drawn over four vertical rectangles.

The horizontal rectangles that are 100% opaque totally cover what is underneath. Those that are 50% opaque allow some color from below to show through.

Drawing with 0% opacity is effectively not drawing at all.

To create a new color based on one of the existing color constants, but using a lower opacity, use bitwise arithmetic; for example:

CONST RED_TRANSPARENT = RED BITAND &H7FFFFFFF