GUI Framework Details
This document describes the behavior of various parts of clanGUI into further detail. It is mostly intended for developers working directly on clanGUI, acting as a specification for how things are meant to work (as opposed to how things might actually work due to bugs).
If you see anything in clanGUI not working as described here, it is a bug in the framework.
Input messages are processed as follows
- Input message is delivered to the window manager from the OS
- Window manager attributes the message to the active window. For the system WM this is always the window the WM_INPUT message was designated, but for the texture WM it does its internal tracking of which toplevel window is the currently active window
- The GUI manager is notified by the WM about a new message attributed to a specific toplevel window. Each toplevel window has a single focused component that gets the first chance to process this message. The message is delivered to this component
- If the focus component does not consume the message, the message is routed to the parent window. This continues until a component consumes the message or it reaches the toplevel component
- If the message is still not consumed after the toplevel component, the accelerator table gets a chance to process the message
- If the accelerator table does not consume the message, the dialog input message handler processes the message. This is where things like tabbing between controls and the default action of enter is handled
Focus switching details
- Tab order is controlled by the component tree and the FocusPolicy of the individual components. The focus moving order is depth-first iteration of the component tree.
- If a child component has focus and is about to be made hidden, CL_GUIComponent::focus_next() is called before making it invisible
- If the child component being made hidden is still the focused component after focus_next(), the focus component is set to null
- If the current focus component is null, and a component that accepts focus is made visible, the shown component will get the focus. Otherwise the focus will remain null
- If <tab>, <alt-tab> or arrow keys reach the dialog input handler, the messages are translated into focus_next() or focus_previous() calls on the currently focused component.
- If FocusPolicy of the currently focused component is focus_group, all components with the same group-name -attribute will be skipped over
- If FocusPolicy of the component to be focused is focus_group, the focus is set on the selected component in the group. If no component in the group is set as selected, normal focus changing rules apply.
- Focus indication (dashed line surrounding text) is drawn on the focused component only after the keyboard has been used to navigate the dialog. Otherwise it is not drawn.
- Focusable components receive focus on mouse clicks after keyboard navigation has been used.
Dialog input handler details
- A toplevel window has one single default action component. If <enter> or <space> reaches the dialog input handler, the messages are sent to the default action component
- The defaulted component is painted with the default state when the following conditions are met:
- The top level window has activation.
- The focused component does not consume the enter key.
- Any component that consumes the enter key may paint itself defaulted when focused.
Geometry details
- The GUI consists of two types of components: top-level and child components
- The geometry rectangle of a top-level component is the client area of the windowing system window, but where the origin is the upper left corner of the primary screen (this differs from GetClientRect() where the origin is the client area itself)
- The geometry rectangle of a child component is its position relative to its parent component
- Top-level components also have a window frame geometry, which include the caption and border frames of the window. This geometry is retrieved and set using get_window_geometry() and set_window_geometry(). The origin of the rect is also the upper left corner of the primary screen.
- In Windows, the workspace is the area defined by GetWindowPlacement() and SetWindowPlacement(). This is the screen area that excludes the task bar and is the only 'safe' geometry to be stored and restored in Windows between sessions. When we add support for the workspace area, those functions should be named get_window_placement() and set_window_placement() and should not replace any of the other geometry functions