Events

Summary:
  • Applets written in the Curl® language use the technique of firing an event at a target to send a message to a program.
  • Events are the key to making Curl applets interactive.
  • An event handler refers to an object of the EventHandler class.
  • A dynamic event handler is an EventHandler object that attaches to a specific EventTarget object.
Curl applets use the technique of firing an event at a target to send a message between components of a program or to notify the program about a user's action affecting a certain target object on the screen, such as typing into a text field or moving the mouse over an object. When the target is notified that an event has occurred, it calls one or more event handlers in response.
This chapter describes events and event targets, how events are created and fired, and how different kinds of event handlers run in response to the firing of events. The events generated when using the keyboard and the mouse to interact with applet windows are described here in detail.
Selection, drag, and drop events are discussed separately in the Selection and Drag and Drop chapters. Dialog events are explained in the Event Firing at Controls section. TimerEvents and the events used in producer-consumer relationships, which are essential for animation, are discussed in the Animation chapter.
Events are the key to making Curl applets and applications interactive. Events are represented by Event subclasses. Each Event subclass represents a specific type of event that can be fired. For example, clicking on an object displayed on your screen causes a PointerPress and a PointerRelease event, and dragging the cursor over a text segment causes a DragPointerEvent.
Events are fired at objects that act as event targets. The object you click on and the text you select by dragging are examples of event targets.
An object can be a target of an Event only if it is a subclass of EventTarget. All graphical objects are potential targets because they inherit from the EventTarget class. EventTarget objects have a list of dynamic event handlers associated with them that are called when matching events are fired at them.
When a particular event activity occurs, such as clicking the mouse button when the pointer is over a graphical object, an event object representing that type of activity is created, and the event is fired with that graphical object as its target. In the following example, the event target is a CommandButton, and clicking on it fires an Action event at the CommandButton.

Example: A Simple Action Event Handler
{CommandButton
    label="Click me!",
    {on Action at btn:CommandButton do
        set btn.label = "Thank you."
    }
}
The source code starting with the word on creates an event handler that gets attached to the CommandButton in this example. The event handler makes the CommandButton change the button's label in response to PointerRelease events, which are a type of Action event.
In general, any event can be used to signal any series of operations or changes to the state of the applet, including firing other events. The source code for such action is contained in event handlers. A target object can have multiple event handlers, each prepared to execute its source code when an event of a certain type is fired at the target. The source code in an event handler can refire the event at further targets.
A simplified summary of the event mechanism is as follows:
Event Mechanism:
  • A user performs some gesture with the mouse or the keyboard that is directed toward some graphical object on the screen.
  • An event representing that activity is instantiated and fired at an appropriate target object.
  • One or more event handlers attached to the target object and corresponding to this event now run.
  • Any event handler may fire the same event (or new events) at other target objects, probably triggering other event handlers.
You need to be careful to avoid calling event handlers recursively, which sometimes happens if an event handler performs an action that causes the same kind of event to be fired again. For example, if the handler for a ViewResizeEvent changes the view size, or if a FocusIn event handler changes the keyboard focus to some other object. The easiest way to avoid this situation is to use the {after 0s do ...} idiom to execute the code that triggers new events. Consider the following code fragment.
{on FocusIn do
    {after 0s do
        || code-that-changes-keyboard-focus
    }
} 
The code that changes keyboard focus is run only after the current event is fully processed, which avoids possible recursion. Wrapping an expression in {after 0s do body} has the effect of adding body to the end of the event queue because timer events are processed only if there are no other events.

Event Queues

Each Curl applet has an event queue. Each node on an event queue consists of an event and an event target at which the event is to be fired.
When the user interface is active, the Curl® runtime repeatedly executes a standard sequence of operations related to events called the event loop. The event loop requires the process to wait for user interactions to generate new event nodes for the event queue whenever the event queue is empty. When the event queue is not empty, the event node at the front of the queue is removed, the event is fired at its designated target, and the event is handled as required.

Firing Events

Although you don't often need to, you can add events to the event queue by calling EventTarget.enqueue-event. This is called asynchronous event handling, because the handler for the enqueued event won't run until after the current event (and any others that may be in the queue before the new event) are handled. If you want to fire an event at a target immediately, without going through the event queue, you can call EventTarget.handle-event. This is called synchronous event handling because the new event is handled immediately, before returning from the current event handler (if any).
For example, when a CommandButton detects a PointerPress followed by a PointerRelease within the same button, it enqueues an Action event on itself. In a similar way, if the button has the keyboard focus, the KeyPress event handler enqueues an Action event when the space bar is pressed and released. In both cases the Action event handler performs the same action associated with the button.

Event Handlers

The term event handler properly refers to an object of the EventHandler class. Such an object contains both an event type (one of the event subclasses) that it responds to and a procedure that it executes in response to that type of event.

Because an event handler's procedure is the active part of the object that can be run in response to an event, it is easy to overlook the distinction between the event handler object and the procedure it contains. However, it is important to keep in mind that an event handler's code is only executed in response to events of the designated class.
There are different types of event handlers, Those event handlers that are attached to one instance of an object are known as dynamic event handlers. They are the most widely used event handlers and can be attached to any EventTarget object.
Each EventTarget object maintains a list of dynamic EventHandlers. The EventTarget class has methods to add and remove event handlers from its list of EventHandlers. When an event is fired at a target, the target compares the data type of the event with the event class of every event handler on its list, walking up the list starting with the last handler added, and invoking each handler whose event class matches. Multiple event handlers may apply to a given event.
The next section discusses how to write a dynamic event handler. but the most commonly used type is created with the help of the special on expression. (See the Using the on Expression section below.)
Events are often reused as an applet runs. If an Event to which you have made a reference is reused, the information that you want to store will be incorrect. Event handlers should not keep a reference to the event after the handler has returned; for example, by assigning the Event to a global variable or storing it in a field.

Dynamic Event Handlers

A dynamic event handler is an EventHandler object that is attached to a specific EventTarget object.
The primary steps in creating a dynamic event handler for a particular object are:
  1. Writing the code for the event handler procedure
  2. Specifying the event class to which it applies
  3. Attaching the code to a particular object (the event target)
You can carry out Steps 1 and 2 at the same time by using the on expression, which has several variants. In addition, there are two ways to attach the event handler to a target object, for Step 3.
First, by including the event handler with the other arguments when creating an event target object:

Example: Including Event Handler in Target
{CommandButton
    label = "Do not press!",
    || create an event handler for Action events on this button
    {on Action do
        {popup-message "Why did you do that?"}
    }
}
Or by invoking the add-event-handler method on the target object after it has been created:

Example: Using EventTarget.add-event-handler Method
{value
    let target:CommandButton =
        {CommandButton
            label = "Do not press!"
        }
    || create an event handler and add it to the button already created
    {target.add-event-handler
        {on Action do
            {popup-message "Why did you do that?"}
        }
    }
    target
}

Using the on Expression

There are several styles of the special on expression. The following shows the syntax of the simplest form: {on event-class do body}
You can attach this event handler to a graphical object as shown below.

Example: Simplest Form of the on Expression
{CommandButton
    label={center {bold Invokes an event handler when clicked}},
    || Attach following event handler to this CommandButton
    {on Action do
        {popup-message
            title="Your Message",
            "This is a user message dialog."
        }
    }
} ||CommandButton
Other styles of the on expression allow you to bind a variable name to the specific event that the event handler is responding to, so that you can call methods or look up properties of that event. Similarly, you can bind a variable name to the event target and have access to its methods and properties.
StyleDescription
{on event-type do body} Specifies the event type only.
{on event-var:event-type do body} Binds a variable to the event.
{on event-type at target-var[:target-type]} do body} Binds a variable to the target. If target-type is not specified, it defaults to any.
{on event-var:event-type at target-var[:target-type] do body} Binds variables to both the event and the target.
In all styles:
Note: In writing a dynamic event handler, you can refer to any variable that has been defined in the lexical context in which the on expression appears.
The following example displays a rectangle that changes color when the pointer enters and leaves it. There are two dynamic event handlers attached to the Frame, each of which changes the color by setting the background of the target Frame. To be able to refer to this option, the event handlers have to declare a variable (rect) to represent their target.

Example: Declaring a Target Variable in the on Expression
{Frame 
    width=100pt, 
    height=50pt,
    background = "orange",
    || attach the first dynamic event handler
    {on PointerEnter at rect:Frame do
        set rect.background = "cyan"
    },
    || attach the second dynamic event handler
    {on PointerLeave at rect:Frame do
        set rect.background = "magenta"
    }
}

Explicitly Adding a Dynamic Event Handler

Instead of implicitly attaching an event handler to a GuiEventTarget by placing it within the target's curly braces, you can explicitly attach an event handler, created by the on expression or otherwise, by calling the GuiEventTarget.add-event-handler method on the target.
In the following example, an HBox contains two Frames. The variables left and right identify the left and right frames. An event handler explicitly attached to the left frame responds to pointer enter and leave events by changing the color of the right frame. The target is the left frame, but the event handler acts on the background color of the right frame.

Example: Explicitly Adding a Dynamic Event Handler
{value
    let left = 
        {Frame
            background = "orange",
            width=100pt, 
            height=50pt
        }
    let right = 
        {Frame 
            background = "orange",
            width=100pt, 
            height=50pt
        }
    let my-hbox = 
        {spaced-hbox 
            left, 
            right
        }

    || attach first event handler to left frame
    {left.add-event-handler
        {on PointerEnter do
            set right.background = "aqua"
        }
    }
    || attach second event handler to left frame
    {left.add-event-handler
        {on PointerLeave do
            set right.background = "magenta"
        }
    }
    my-hbox
}

Removing a Dynamic Event Handler

To remove a dynamic event handler, invoke EventTarget.remove-event-handler on the target to which the event handler is attached, supplying the event handler to be removed as its argument. You need to assign the event handler returned by the on expression to a variable of the EventHandler class so that you can specify which handler is to be removed.
The following example adds event handlers to a frame when you set a checkbox to true, and removes them when you set the check box to false.

Example: Removing a Dynamic Event Handler
{value
    let rectangle = 
        {Frame 
            width={make-elastic}, 
            height=50pt,
            background = {FillPattern.get-orange}
        }
    || create a handler for PointerEnter event
    let turn-me-aqua:EventHandler =
        {on PointerEnter do
            set rectangle.background = {FillPattern.get-aqua}
        }
    || create a handler for PointerLeave event
    let turn-me-magenta:EventHandler =
        {on PointerLeave do
            set rectangle.background = {FillPattern.get-magenta}
        }
    || create a checkbutton that will alternately add or remove
    || the color changing event handlers
    let checkbox:CheckButton =
        {CheckButton
            label = "Add color changers",
            {on ValueChanged do
                {if checkbox.value == true then
                    || add color changers when the box is checked
                    {rectangle.add-event-handler turn-me-magenta}
                    {rectangle.add-event-handler turn-me-aqua}
                 else
                    || remove color changers when it is unchecked
                    {rectangle.remove-event-handler turn-me-magenta}
                    {rectangle.remove-event-handler turn-me-aqua}
                }
            }
        }
    {VBox
        checkbox,
        rectangle
    }
}

Event and Target Class Hierarchies

To understand more about how events are handled and how to change that behavior, you need to know more about the Event and the EventTarget classes and what kinds of methods are available for these classes and their major subclasses.
Here is a more detailed view of part of the event class hierarchy. The most commonly used event classes are GuiEvent, which includes events relating to graphical user interfaces, its subclass GuiWindowEvent, and its subclass GuiInputEvent, which includes all mouse and keyboard events. They are the focus of the rest of this chapter.
Event
GuiEvent
CurrentNodeChanged
CurrentRecordChanged
StopEvent
DetachEvent
AttachEvent
CommandChanged
RenderFailed
StartEvent
TabPaneShown
SelectionEvent
LoadingFinished
RepaintNotify
DialogEvent
GuiWindowEvent
GrabRelease
Action
TabPaneHidden
CurrentRecordChangeRequest
AsyncWorkEvent


AsyncStreamWriteEvent
AsyncStreamReadEvent
AsyncSocketEvent
AsyncFileOpenEvent


RecordSetEvent
RecordSetLoadStateChanged
RecordsChanged


ChartAxesChanged
ChartLayoutChanged


SceneEvent
IntersectionSceneEvent


EmbeddedBrowserEvent


DocumentFinishEvent
StatusTextChangeEvent
BrowseStartEvent
TitleTextChangeEvent
BrowseFinishEvent
DownloadStartEvent
DownloadFinishEvent
BrowseErrorEvent


WindowEvent


UpdateWindowEvent
RepaintWindowEvent
DestroyWindowEvent
TimedWindowEvent
PointerCaptureEndWindowEvent
HostSettingChangedWindowEvent
DestroyRequestedWindowEvent


IOEvent
StreamEvent
IOTimeoutEvent
SocketEvent
TimerEvent
Note: Other event classes are discussed elsewhere, primarily in the chapters Selection, Drag and Drop, Dialogs and Controls, and Animation.
Similarly, the main event target class is EventTarget, whose most important subtype is GuiEventTarget. The following is a partial class hierarchy for event targets:
EventTarget
AsyncStreamReaderEventTarget
RecordSet


EmptyRecordSet
EventManagingRecordSet
RecordView
ConnectedRecordSet


SceneObject


TextSceneObject
LightableSceneObject
SceneGroup
Camera
SceneLight


Window
IOEventTarget


DefaultIOEventTarget
DataSocketStream


AsyncFileOpenerEventTarget
GuiEventTarget
SkinnableControlUI
ControlFeel
SelectionContext
ContainerDragScroller
GuiManager
Selectable
Visual
CommandBinding
KeyAccel
ConsoleInput
ControlContainer
ControlUI
Timer
Events are often but not necessarily related to the graphical interface; they also include I/O events and socket events as well as timer events. See the GUI and Window Events section for more information about GUI Events.