Event handling

PGUI: Event handling

Modern grapical environments use event model for communicating between interactive objects and the input/output system.

The event model was developed to support direct manipulation interfaces. Direct manipulation is when the user is presented with concrete objects on the display and then manipulates those objects with interactive devices.

Event model makes it possible to support a large number of simultaneously available commands and actions. For example, think about the possible actions available in a file manager application (Windows explorer).

Windowing system

A windowing system makes it possible for several interactive applications to run simultaneously. In a windowing system a user interface of an application is built of

  • top level windows, and
  • controls (ui components, child windows, widgets, ...).

The software view of a windowing system is a hierarchy of (usually) rectangular windows and child windows. An application usually has one or more top level windows (root windows). A root window is managed by the windowing system or a separate process called the window manager.

Window events

User actions with the input devices are translated into software events (messages) and distributed to the appropriate window. Events (or messages) are identified by an event type.

Input events

Mouse events:

  • mouse-down and mouse-up events for each mouse button,
  • mouse-click (and mouse-double-click) for each mouse button,
  • mouse-move and mouse-drag for moving or dragging the mouse, and
  • mouse-enter and mouse-exit for entering and exiting a user interface component (window).

Every mouse event carries the mouse coordinates and the state of the modifier buttons.

Mouse click occurs when the mouse button is pressed down and released sufficiently close to the same spot. Depending on the situation, the spot might be one pixel or a single ui component.

Usually, a window receives mouse messages that happens inside it. A window can capture the mouse in order to receive all mouse events.

Keyboard events:

  • key-down and key-up events for pressing the keyboard keys,
  • character events for generated characters (ASCII or UNICODE), and
  • system keyboard events for menu shortcuts.

Window receives keyboard events if it has the input focus. Window gets input focus for example when it is clicked with the mouse. The window gets notified with event messages about receiving and losing input focus.

Windowing events

Window management events:

  • window-create event when the window is initially created,
  • window-size event when the size of the window changes, and
  • window-activate and window-deactivate events for changing the active window.

Redrawing

When the contents of the window need to be redrawn, an update event is sent to the window. The window handles the event by drawing the contents of the window or a portion of the contents.

Redrawing (or updating) is commonly a low-level event. It is "sent" to a window when there are no other messages to process.

The main event loop

The main event loop get the events from the event queue, translates, and dispatches then to the corresponding windows. The generic form fo the main event loop is

Initialization
while (not time to quit) {
  Get next event E
  if (not FilteredEvent(E)) {
    Translate event E
    Dispatch event E
  }
}

In object oriented systems (Java, MFC, .NET) the main event loop is hidden behind the class implementation. In Win32 API -programming the event loop is similar to the above example.

Event handling

Dispatching events

Dispatching commonly uses bottom-first algorithm in using the mouse position for identifying the window. It starts from the bottom of the window hierarchy and proceeds upwards to find the window that wants to process the event.

Keyboard event are sent to the window that has the input focus. Input focus can be changed by clicking on a window or just moving the mouse on top of a window.

Mouse events usually go to topmost (visually, hierarchically at the bottom) window including the mouse coordinates. A window can capture the mouse in order to receive other mouse events.

Window procedure in Win API

Win32 uses a callback function called the window procedure to handle the messages sent to the window. The window procedure is linked to the window with the help of a window class.

// in Winmain
wndclass.lpfnWndProc = WndProc;
RegisterClass(&wndclass);
...

// the window procedure
LRESULT CALLBACK WndProc (HWND hwnd,
                          UINT message,
                          WPARAM wParam, 
                          LPARAM lParam) {
  switch (message) {
  ...
  case WM_LBUTTONDOWN:
    // handle the event
    return 0;
  ...
  }
}

The window has a reference to the window class and the window class has a reference to the window procedure. Using these two references Win32 OS can call the window procedure.

Message maps in MFC

In MFC window class, a message map is used to link member functions to messages. Message map is a simple associative array where keys are message types (WM_PAINT, ...) and elements are function pointers.

Because the correct implementation of message maps requires relatively complex code MFC provides a large set of macros for creating message maps. To process left-mouse-button-down messages (WM_LBUTTONDOWN) you write:

class CMainWindow : public CWnd
{
...
protected:
  afx_msg void OnLButtonDown (UINT nFlags, CPoint point);

  DECLARE_MESSAGE_MAP ()
};


// implementation
BEGIN_MESSAGE_MAP (CMainWindow, CWnd)
  ...
  ON_WM_LBUTTONDOWN ()
  ...
END_MESSAGE_MAP ()


void CMainWindow::OnLButtonDown (UINT nFlags, CPoint point)
{
  // handle the event
}

The macros DECLARE_MESSAGE_MAP, BEGIN_MESSAGE_MAP, END_MESSAGE_MAP and message type related macros create the message map data structure as an array. The created code is demonstrated below. The statements

// In the class declaration
  DECLARE_MESSAGE_MAP ()
  
// In the class implementation
  BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
    ON_WM_PAINT ()
  END_MESSAGE_MAP ()

are precompiled to

// In the class declaration
  private:
     static const AFX_MSGMAP_ENTRY _messageEntries[];
  protected:
     static const AFX_MSGMAP messageMap;
     virtual const AFX_MSGMAP* GetMessageMap() const;
	 
// In the class implementation
  const AFX_MSGMAP* CMainWindow::GetMessageMap() const
  {
    return &CMainWindow::messageMap;
  }

  const AFX_MSGMAP CMainWindow::messageMap = {
    &CFrameWnd::messageMap,
    &CMainWindow::_messageEntries[0]
  };
  
  const AFX_MSGMAP_ENTRY CMainWindow::_messageEntries[] = {
    { WM_PAINT, 0, 0, 0, AfxSig_vv,
      (AFX_PMSG)(AFX_PMSGW)(void (CWnd::*)(void))OnPaint },
    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
  };

Notice the reference to the superclass message map. If the message type can not be found from this message map the search continues from the superclass.

Event listeners in Java

In Java, a user interface component include a list of event handlers for different event groups. Event handlers are interfaces called "listeners". To listen to a set of events, a class needs to implements one of more of these interfaces.

The user interface components include functions for adding all supported event listener types. The general form of these functions is

public void addEventListener(EventListener l);

where Event is replaced with the real listener type. For example, to listen to mouse-button-pressed-down -events, we write

public class MyComponent
  extends JComponent
  implements MouseListener
{
  public Mycomponent() {
    ...
    addMouseListener(this);
    ...
  }


  public void mousePressed(MouseEvent e) {
    // handle the event
  }


  // the other mouse listener functions
  ...
}

Package java.awt.event includes the most common event listener interfaces:

Listener interface Description Adapter
ActionListener Listen to action events: button clicks, timer ticks, menu selections, ...  
KeyListener Listen to keyboard events (keystrokes). KeyAdapter
MouseListener Listen to mouse events press, release, click, enter and exit. MouseAdapter
MouseMotionListener Listen to mouse motion events on a component. MouseMotionAdapter
WindowListener Listen to window events like window activating and window closing WindowAdapter

Adapter is a class including an empty implementation of every method in the listener interface. Thus you do not have to write the empty methods in your own code, if you are using separate event processing classes.

Additional event listener interfaces can be found in package javax.swing.event.

Delegates and event handlers in .NET

.NET uses delegate classes to process events. A delegate is a special kind of class that holds a reference to a method with a pre-defined signature. Namespace System.Windows.Forms includes a set of delegate declarations for event processing. For example, the delegate for mouse event handling is

public delegate void MouseEventHandler(
  object sender,
  MouseEventArgs e
);

By convention, the first parameter for event handlers delegate is the source of the event, and the second parameter is an object including event arguments. There is also a generic event delegate for events with no arguments:

public delegate void EventHandler(
  object sender,
  EventArgs e
);

The class EventHandler (and also EventArgs) is in the namespace System.

Event handlers can be connected to a class with a special language construct called event. For mouse button down events the corresponding handler in the class Form is

public event MouseEventHandler MouseDown;

The event handler can be connected (wired) to the form with

MouseDown += new MouseEventHandler(MyMouseDown);

where MyMouseDown is your own local event handler method:

private void MyMouseDown(object sender, MouseEventArgs e) {
  // x-coordinate
  int x = e.X;
  // y-coordinate
  int y = e.Y;
  // which button
  if (e.Button == MouseButtons.Left) {
    ...
  }
}

Examples

DrawLines-example in several versions.

Moving things in several versions.

See also:
Package java.awt.event
Messages and message queues