This chapter defines a number of NewBASIC's technical concepts, including:
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.
Between the time it is loaded and unloaded an application will go through the following stages:
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.
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.
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.
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.
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.)
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
NewBASIC uses the parent/child hierarchy to make sure that certain things that happen to a parent will also happen to its children.
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.)
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.
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.
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
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.
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
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.
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.
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.
There are a number of ways that a program can detect mouse activity:
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.
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.
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:
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.
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 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:
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
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:
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 |
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:
For example, the constant LIGHT_GREEN breaks down into the following parts:
You may wonder what the opacity or opaqueness of a color means.
Opacity determines how much this color will obscure what's beneath it.
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