Glenn asks how we hide (or abstract, if you want to be more computer-science
like) the behavior of gadgets the way Fields and other built-in interface
elements do it. It's not magic, that's for sure ;)

The answer is, use the same technique that Palm does. If that's enough for
you, stop here. Long and detailed explanation below:

When you use forms, you register a form event handler by calling 

FrmSetEventHandler(frmP, MyFormHandleEvent);

Then you open the form...

Looking at a typical event loop, we see:

do {    
        EvtGetEvent(&event, evtWaitForever); 
        if (! SysHandleEvent(&event))
                if (! MenuHandleEvent(0, &event, &error))
                        if (! ApplicationHandleEvent(&event))
                                FrmDispatchEvent(&event);
} while (event.eType != appStopEvent);

So for each event, lots of functions may take a look and see if they are
interested. The FrmDispatchEvent() function is where low-level pen taps
within interface elements are handled or translated into higher-level
events. Here's what's happening (in general based on my understanding of How
Computers Work, not from Palm OS source code -- you'll need to get the Palm
OS source to see *exactly* what's happening!)

Boolean FrmDispatchEvent(EventTypePtr eventP) {
        // based on event x & y location, figure out which, if any,
interface element the event belongs to

        Word numObjs = FrmGetNumberOfObjects (frm);
        for (i = 0; i < numObjs; i++) {
                // check each object until one is found where event x/y is
inside bounds
                if (CheckBounds(frm, i, eventP)) { // OK, I made this
function up, but you get the idea 
                        // call the object event handler, based on its type
                        switch (FrmGetObjectType (frm, i) {
                        case frmFieldObj:
                                return FldHandleEvent(FrmGetObjectPtr
(frm,i), eventP)
                        case frmControlObj:
                                return CtlHandleEvent(FrmGetObjectPtr
(frm,i), eventP)
                        case // you get the idea!
                        }
                }
        }
        // no match? call the registered form handler fn
        return frm->handler(event);
}

All the "dispatch" fn does is check every object in the form to see if it is
interested in the event. If it is, then the event is handled and processing
of that event stops. Check the docs, and you'll see for every interface
object there is a xxxHandleEvent() function. That's what they're for.
Sometimes these handlers will handle one or more low-level event (pen
down/pen up) and create a new event (CtlSelectEvent) which is inserted into
the event queue. This is from the PalmOS docs:

"ctlSelectEvent
The control routine CtlHandleEvent sends this event. When
CtlHandleEvent receives a ctlEnterEvent, it tracks the pen
until the pen is lifted. If the pen is lifted within the bounds of the
same control it went down in, a cltSelectEvent is added to the
event queue; if not, a ctlExitEvent is added to the event queue."

Reading the ctlEnterEvent description, we see that the CtlHandleEvent sends
this one when it handles a penDownEvent in its bounds. So, the user puts the
pen down in a control, and this happens:

penDownEvent is received in the event loop, and eventually passed to
FrmDispatchEvent(), where it is sent to the correct control's
CtlHandleEvent(). That handler creates a new event named ctlEnterEvent and
adds it to the event queue. 

ctlEnterEvent is received in the event loop, and eventually passed to
FrmDispatchEvent(), where it is sent to the correct control's
CtlHandleEvent(). That handler tracks events until a penUpEvent is received,
and then it either adds a ctlSelectEvent or ctlExitEvent to the event queue.
let's say it adds a ctlSelectEvent.

ctlSelectEvent is received in the event loop, and eventually passed to
FrmDispatchEvent(), where it is not handled by any of the object handlers...
so your registered form event handler is finally called with it!

That's not exactly magic... but it sure does hide a lot of complexity from
you when you're writing apps.


If you want to mimic this behavior in your form handler, you'll need to
write a myGadgetHandleEvent() fn. That function would do whatever processing
you want, including possibly generating new user-defined events for you.
Your form event handler would then look something like this:

Boolean myFormEventHandler(EventPtr eventp) {
Boolean handled;

        handled = myGadgetDispatchEvent(eventp);

        if (handled) return handled;

        switch (eventP->eType) {
        case MyGadgetSelect:    // or some other user-defined event type...
                // handle high-level event here
        case // etc.
        }
        return false;
}

Your myGadgetDispatchEvent() fn would look in the current form for gadgets,
and for each gadget found would check to see if the event is in its bounds.
If it is, then you'd process the event (possibly adding new events to the
event queue), and return true/false as appropriate. You could do that in the
myGadgetHandleEvent() function, if you really want to mimic the PalmOS
design.

As they say, it's all in the book ;)

NOTE: In PalmOS 3.5, you can register a callback for a gadget. That callback
is called during the FrmDispatchEvent() processing if an event falls inside
the bounds of a gadget, so you no longer have to figure out *which* gadget
got tapped. In essence, you just need to write a myGadgetHandleEvent() for
your gadget, and you get rid of the myGadgetDispatchEvent() function I
describe above.

 ==-
John Schettino author of
Palm OS Programming For Dummies, http://schettino.tripod.com


-----Original Message-----
Subject: Gadgets and events - followup question
From: Glenn Bachmann
A followup to Pete Angiuoli's question on gadgets:

I think I understand that if I put an input field on a form, that the form
handler does not "see" low-level events such as pen up/down. Through some
magic, they get routed directly to the input field, and handled by code in
the Palm OS handler for input fields. There is no special routing that the
form handler has to do to say to the input field that a pen was tapped.

I think I also understand that if I write a gadget, that it does not
magically receive events that happen within its client area rectangle, it
is the responsibility of the parent form to figure out that a pen down that
happened at coordinates 100,50 should be handled by the gadget at that
location. So I wind up with code in my form handler that looks like this:

-Get a pen event
-Determine the location of the pen tap
-Figure out if there is a custom gadget that is "underneath" that x/y
location
-Hand the event to some piece of code that is responsible for handling a
pendown in the context of the gadget (ex. change the cursor position).

You can see where this becomes unwieldy if, say, I've written a "super
input field" gadget, and I've got 16 of them on my form. 

So my question is, how is it that pen events and such are automatically
routed to the "built-in" UI elements? Is there a way for my own "custom
gadgets" to participate in this mechanism, such that my parent form handler
logic does not have to manually route these events? Under windows, because
of the parent-child relationship of windows, this is automatically handled
by the UI element that "has the focus". 

Assuming I'm not just hopelessly confused, any insight on this would be
helpful to us.

Thanks!


Glenn Bachmann
Bachmann Software and Services, LLC
http://www.bachmannsoftware.com
Software for Handheld & Wireless Computing, Windows and the Internet
Authors of "Palm Programming", published by Macmillan/Sams, and home of
Bachmann Print Manager, the only graphical printing solution for the Palm
Computing Platform

Reply via email to