# Goals

The main goal of Crystal Space Windowing System (CSWS) is to provide a clean, effective and cross-platform base for creating CrystalSpace-related utilites. Currently there is only one such utility under development: CrystalSpace maze editor.

# Overall concepts

There is nothing new in CSWS regarding the window system. All base conceptions for building such an system were invented far ago, at least when first X-windows system was built (at MIT(?)) or even earlier.

The system is event-based, events are generated by hardware (mouse, keyboard) or software (components can send messages to each other). Event-system is system-dependent as well as graphics subsystem. They were developed first as part of CrystalSpace itself, then some modifications were made to better fit CSWS requirements. CSWS itself is completely system-independent, i.e. you can easily develop programs that will compile and work with no problems under any system supported by CrystalSpace.

Since Crystal Space always uses buffered visuals, the screen repainting is always done on the invisible page. This means flickering on redraw is almost impossible in CSWS, while it is common on usual GUIs with bad-written programs. If visual has more than one backbuffer (like OS/2 port), the image is automatically re-syncronized on all back-buffers.

Other CrystalSpace-specific feature is the division of event processing into "frames". Since CrystalSpace is a animation engine it operates in terms of frames, i.e. when the frame begins, all pending events in queue are processed, then all invalid components are redrawn, and at last the image is blitted onto the screen.

# Coordinate system

Coordinate system is based on most-used (wrong :-) system when top-left corner is (0,0) and bottom-right corner is (width, height). It would be better to use the 'right' coordinate system used, for examplem, in OS/2 and OpenGL but most programmers are already common with the 'wrong' system.

Most CSWS functions works with rectangles (csRect class). The csRect class has four data fields: xmin, xmax, ymin, ymax which are self-explanatory. Here is a example of rectangle (xmin = 0, ymin = 0, xmax = 3, ymax = 2):

           0  1  2  3  4 ...
           |  |  |  |  |  |
       0 --@@@@@@@@@@--+--+--
           @//|//|//@  |  |
       1 --@--+--+--@--+--+--
           @//|//|//@  |  |
       2 --@@@@@@@@@@--+--+--
           |  |  |  |  |  |
       3 --+--+--+--+--+--+--
           |  |  |  |  |  |
      ...--+--+--+--+--+--+--

Vertical line 'X=3' and horizontal line 'Y=2' does NOT belong to the rectangle. The hashed pixels belong to the rectangle. So, if you have two windows: (0, 0) - (3, 2) and (3, 0) - (6, 2) they do NOT overlap.

# Palette

CSWS has been designed to operate transparently independent of screen geometry or number of colors. To allow normal operation in different pixel format conditions colors are kept in a dynamically-built table which is referenced through indexes in that table. To allow different palettes for windowing system each component does not use colors directly when drawing, instead each component has a pointer to a table which contains color values. So, for example, a component can have a palette which's first element is used as background color, second is used to display text in window and so on. By changing only the palette table we can change at once how all these components are looking.

There is a number of pre-defined colors called cs_Color_XXX, for example cs_Color_Black, cs_Color_White etc. Components does not draw with cs_Color_XXX constants, they just indicate the index into their palette. All standard component palettes are contained in cswspal.cpp and cswspal.h files.

# Components

The base class for windowing system is csComponent. csComponent is a very complex class with lots of built-in functionality. This allows for easier creation of new components for specific needs. Most csComponent methods are virtual, allowing to override them as needed. There are some basic methods, which are described below. Other methods are described as needed during this document.

## csComponent::Draw ()

Each component should know how to paint itself. When windowing system thinks a part of a component image or entire component image is invalid (for example, when a window is moved from the top of other components), it adds that rectangle to the 'dirty' rectangle of the component. The dirty rectangle is initially empty, then all invalid rectangles are added to it. At the end of each frame windowing system sends a broadcast event with cscmdRedraw command code. When component catches this event, it calls Redraw method which in turn calls csComponent::Draw. After csComponent::Draw returns control, the dirty rectangle is emptied again.

All draw operations are clipped first against dirty rectangle and then against all component childs and neightbours. So drawing inside a component does not change even a pixel in other components, even if component is partialy covered by other components.

There is a number of drawing operations that you can use. Other operations can be synthesized from existing. All coordinates used for drawing are component-origin related, so (0,0) corresponds to component top-left corner.

- `Line (x1, y1, x2, y2, colorindex)`

  Draw a line from (x1, y1) to (x2, y2). Example: Line (0, 0, 3, 3) produces the following output:

                   0  1  2  3  4 ...
                   |  |  |  |  |  |
               0 --@--+--+--+--+--+--
                   |//|  |  |  |  |
               1 --+--@--+--+--+--+--
                   |  |//|  |  |  |
               2 --+--+--@--+--+--+--
                   |  |  |//|  |  |
               3 --+--+--+--@--+--+--
                   |  |  |  |//|  |
              ...--+--+--+--+--+--+--

- `Box (xmin, ymin, xmax, ymax, colorindex)`

  Draw a filled box that is closed between lines (X >= xmin), (X < xmax), (Y >= ymin) and (Y < ymax). Example: Box (0, 0, 3, 2) will produce the following output:

                   0  1  2  3  4 ...
                   |  |  |  |  |  |
               0 --@--@--@--+--+--+--
                   |//|//|//|  |  |
               1 --@--@--@--+--+--+--
                   |//|//|//|  |  |
               2 --+--+--+--+--+--+--
                   |  |  |  |  |  |
              ...--+--+--+--+--+--+--

- `Text (x, y, foregroundindex, backgroundindex, textstring)`

  Display a text string starting from (x, y) with color "foregroundindex" and background "backgroundindex". If backgroundindex == -1, the text is drawn transparently. To determint text width and height you can use the `TextWidth (textstring)` and `TextHeight ()` functions. There is a number of built-in CSWS fonts, they can be used by calling SetFont (cs_Font_XXXX). Note that both TextWidth() and TextHeight() depends on current font.

- `Sprite2D (sprite2d, x, y, w, h)` (you can ommit w,h to use normal sprite size).

  Draw a 2D sprite or bitmap. "sprite2d" is a object of csSprite2D class. Sprite can have transparent colors, this way you can define "holes" in textures.

- `Rect3D (xmin, ymin, xmax, ymax, darkindex, lightindex)`

  Draw a 3D-looking rectangle. This method just draws four lines to form a non-filled rectangle. Example: Rect3D (0, 0, 4, 4);

                   0  1  2  3  4 ...
                   |  |  |  |  |  |
               0 --@--@--@--@--+--+--
                   |LL|LL|LL|DD|  |
               1 --@--+--+--@--+--+--
                   |LL|  |  |DD|  |
               2 --@--+--+--@--+--+--
                   |LL|  |  |DD|  |
               3 --@--@--@--@--+--+--
                   |LL|DD|DD|DD|  |
               4 --+--+--+--+--+--+--
                   |  |  |  |  |  |

- `ObliqueRect3D (xmin, ymin, xmax, ymax, cornersize, darkindex, lightindex)`

  Draw a rectangle with oblique corners of given size. Example: ObliqueRect3D (0, 0, 10, 7, 3).

                   0  1  2  3  4  5  6  7  8  9 ...
                   |  |  |  |  |  |  |  |  |  |  |
               0 --+--+--@--@--@--@--@--@--@--@--+--
                   |  |  |LL|LL|LL|LL|LL|LL|LL|DD|
               1 --+--@--+--+--+--+--+--+--+--@--+--
                   |  |LL|  |  |  |  |  |  |  |DD|
               2 --@--+--+--+--+--+--+--+--+--@--+--
                   |LL|  |  |  |  |  |  |  |  |DD|
               3 --@--+--+--+--+--+--+--+--+--@--+--
                   |LL|  |  |  |  |  |  |  |  |DD|
               4 --@--+--+--+--+--+--+--+--+--@--+--
                   |LL|  |  |  |  |  |  |  |  |DD|
               5 --@--+--+--+--+--+--+--+--@--+--+--
                   |LL|  |  |  |  |  |  |  |DD|  |
               6 --@--@--@--@--@--@--@--@--+--+--+--
                   |LL|DD|DD|DD|DD|DD|DD|DD|  |  |
              ...--+--+--+--+--+--+--+--+--+--+--+--

- `SetClipRect (xmin, ymin, xmax, ymax)`

  Define a local clipping rectangle for subsequent drawing operations. For example, when drawing a button you may want to set clipping rectangle after drawing button borders so that button text accidentally won't draw over its borders even if it does not fit. To disable clipping you can define a empty rectangle (SetClipRect (0, 0, 0, 0));

## csComponent::HandleEvent (csEvent &Event)

This is the main component's entry point for handling windowing system events. Each time user presses a key, moves mouse etc windowing system generates a event and sends it to appropiate component. The normal event flow is top-down, i.e. the root window receives events first, then passes them if appropiate to its childs which passes it to their respective childs and so on. However, there are some basical constraints on event flow that you should know about:

- HandleEvent should return a boolean value that states whenever this object used the event or not. If the event has been used, no other component will receive it anymore, otherwise windowing system looks for other appropiate candidates for this event. The only exception from this rule are broadcast events that are sent to all childs independently of the fact that component "eaten" event or not.
- Mouse events are received only by the topmost component mouse pointer is over and its parents. If other behaviour is desired, a component should capture the mouse by calling `app->CaptureMouse (this)` - in this case mouse events are sent by application object directly to the object that captured the mouse, bypassing normal event flow. To revert to normal mode of operation component should call `app->CaptureMouse (NULL)`.
- Keyboard events are sent only to components from "focus chain". If a different behaviour is desired, component should capture keyboard events by calling `app->CaptureKeyboard (this)`. To disable capturing keyboard events component should call `app->CaptureKeyboard (NULL)`
- Command events are not sent to child windows. A reply to command event is normally expected from the component it has been sent to, not from any of its childs. Moreover, re-sending command events to childs can be dangerous since the event can be sent by one of its childs, and the application can get caught in an endless loop. Re-sending command events to parents is a normal practice, for example all menus re-send command events to their parents since commands are usually sent to menus by their childs (menu items or even sub-menus).
- Broadcast events are the only type of events that are guaranteed to reach every object in a subtree of components.

There is another method called `csComponent::PreHandleEvent (csEvent &Event)` which is much like HandleEvent except that it is called for each event pulled from event queue before HandleEvent, and without rules above, i.e. any component can preprocess any hardware-generated event before it is sent along normal event handling chain. This can be used for popup menus, hot keys and so on. If PreHandleEvent returns true, the event is considered "eaten" and is discarded. Be careful with this feature to avoid interfering with normal component functions.

And last method related to event processing is `csComponent::PostHandleEvent (csEvent &Event)`. It is called when no component handled the event and it is called for all components in parent->children order. It can be used to process events that nobody wants, for example it is used to process hotkeys on buttons (if active component is for example, a input line, and user presses 'A' key it should be entered into input text, while if active component is a non text-hungry component (for example, a listbox) the 'A' key can be used by some button to activate itself).

## Data csComponent fields

csComponent class has a number of data fields. The most important is

      csRect bound;

which defines the physical bounds of the component. All drawing operations happens inside and are clipped to this bound. Component does not have control over any other pixel outside this bound. The bounds are defined relative to parent's origin. For example, if we have a component with bounds (xmin = 20, ymin = 10, xmax = 100, ymax = 50) and it has a child (10, 10, 30, 40) then its real (physical) pixel coordinates are (20+10, 10+10, 20+30, 10+40). A child is always clipped to the bounds of all its parents (however, there is a exception from this rule).

      int state;

contains the state flags of the component. There are two types of state flags (although CSWS does not make a difference between them) - dynamic flags and option flags. Option flags usually define some static aspect of component - for example, whenever component is selectable (i.e. if it can become the focused element), or it is partially transparent (irregularily-shaped components) etc. Dynamic flags often changes during component's life, for example component visibility flag, "component focused" flag and so on. The component state flags starts with the CSS_ prefix.

You should never change manually state flags. Instead, there are two methods for this: `SetState (mask, state)` and `GetState (mask)`. For example, if component is not selectable and you want to make it selectable, call component.SetState (CSS_SELECTABLE, true). The SetState method is virtual and is often overriden to execute some specific functions when component state changes. For example, default SetState handler shows or hides the component if CSS_VISIBLE flag state changes.

      csRect dirty;

contains the "dirty" rectangle. If it is non-empty, the dirty area of component is redrawn just before the end-of-frame.

      int DragStyle;

Contains a combination of bits (CS_DRAG_XXX) that defines which sides of component can be dragged with mouse when you call first the csComponent::Drag method. By default it is equal to CS_DRAG_ALL. Note that if component won't call csComponent::Drag you won't be able to resize the component even if DragStyle will be equal to CS_DRAG_ALL.

      csApp *app;

This is the "main" application object, the root of entire window tree. It is a descendant of csApp class which is a superclass of csComponent. csApp has some application-global methods such as CaptureMouse, CaptureKeyboard, it contains the global mouse pointer object and many other.

      csComponent *parent;

Contains the parent component. Components are chained into a tree, each component knows its neightbours (through "next" and "prev" fields), each parent knows its focused child, and can traverse its list of childs by starting from focused, then going to focused->next and so on.

      csComponent *prev, *next;

Contains the next and previous neightbours in parent's child list. They are never NULL, chains are always closed. If a component is the only child of another, its next and prev fields points to himself.

      csComponent *focused;

This is the focused child component. For example, keyboard events are sent only to components in "focused" chain, i.e. to app, app->focused, app->focused->focused and so on. If component does not have any child components, focused is NULL. This is the only possible case when "focused" can be NULL.

      csComponent *top;

This is the top component in Z-order. Mouse events are processed in Z-order unlike keyboard events. The focused event is not neccessarily the top component, although usually this is true. If component has "CSS_TOPSELECT" flag set, when user activates (focuses) a component, it is made the topmost, however if it has this flag reset, its Z-order does not change.

      int id;

This is component's identifier. This is a user-definable value, but it is expected to be unique within all childs of a parent component. You can search for child components with given ID by calling `parent->GetChild(int find_id)`.

# Events

There are several event types (other event types can be defined as needed, but existing set covers all current CSWS needs). There is the base event class called csEvent. It has several public data fields, the mostly used is Type which contains the event type. There are following currently defined event types:

- csevNothing - "empty" event, mostly used internally by CSWS.
- csevKeyDown - key has been pressed event.
- csevKeyUp - key has been released event. On KeyUp and KeyDown events the following data fields in event object are also valid:
  - Key.Code - contains code of key that has been pressed or released. It can be a literal value ('A', 'z', ' ') or one of predefined CSKEY_XXX values (CSKEY_UP, CSKEY_ESC, CSKEY_SPACE, CSKEY_ENTER etc).
  - Key.ShiftKeys - contains the state of shift keys at time when this event has been generated. Note that it not neccessarily is the same as current key state. To decode its value you should use the pre-defined CSMASK_XXX constants (for example,

                      if (Event.Key.ShiftKeys == CSMASK_ALT)
                      {
                        ... ALT + Event.Key.Code pressed ...
                      }

    or,

                      if (Event.Key.ShiftKeys & CSMASK_ALT)
                      {
                        ... (at least ALT) + Event.Key.Code pressed ...
                      }

    Also there is a bit in ShiftKeys called CSMASK_FIRST that is set only when key is pressed for first time. With this you can separate actual key presses from autorepeated keyboard events.
- csevMouseMove - "mouse has been moved" event.
- csevMouseDown - "a mouse button has been pressed" event.
- csevMouseUp - "a mouse button has been released" event. On all mouse events the following data fields in event object are also valid:
  - Mouse.x, Mouse.y - the screen position where mouse event happened. Mouse position is always converted to component coordinate system, i.t. Mouse.x == 0 && Mouse.y == 0 means that mouse is at component top-left system.
  - Button - mouse button number, counting from 1. Not valid for "mouse moved" event.
  - Key.ShiftKeys - contains the state of shift keys at time when this event has been generated. See above for comments on shift keys.
- csevCommand - a command event. Command events are generated by windowing system components, not by hardware. For example, when a button is pressed, it sends a command event with some command code to its parent component. There is only one data field for this type of events called Info of type "void *". It can point to any event-related information.
- csevBroadcast - broadcast events are much like command events, except that no component can intercept ("eat") it, and they are feed to all child, grandchild etc components in a component tree (starting from the component that received first this event).

To check if the event is generated by keyboard, you can use the predefined IS_KEYBOARD_EVENT(Event) macro. To check for mouse events, you can use the IS_MOUSE_EVENT(Event) macro.

# Clipping

Clipping is the process of removing parts of output covered by other components. CSWS has full clipping implemented for all existing graphics primitives. There is no drawing operation that can influence other component's image (except transparent windows). Clipping is performed in the following way: First primitive is clipped against "dirty" rectangle (to cut off excessive output), then it is clipped against all its neightbours, then against "clip parent" window and all its neightbours and so on.

## Clip parents

All components have a "parent" component and a "clipparent" component. What they are used for? Usually "clipparent" is equal to "parent" so there is no difference between them. But in some rare cases there is a need to perform clipping in a different way. For example, popup menus that belong to a window usually should not be clipped against that window, but rather to the parent of parent window or even clipped only to desktop. In this case you should assign a different clip parent to that component. This can be performed by using `InsertClipChild()` method. For example, to make a menu clip to application's bounds, you should call

      app->InsertClipChild (menu);

You should not assign directly to "clipparent" variable since InsertClipChild does many extra work, for example it inserts the so-called "clip child" into component's clip child list, removes component from former clipparent's "clip child" list and so on.

## Partialy transparent components

You can set a state flag called CSS_TRANSPARENT to mark semi-transparent components. Transparent components works differently in the following ways:

- When a part of such a component is invalidated, the same area of parent component is invalidated too.
- Mouse events for such components are considered "eaten" only if component's HandleEvent method returned TRUE. Otherwise, the event is sent to the component placed one level below transparent area.
- Parent's window output is not clipped against transparent childs. Instead of this, child's area covered by output is invalidated.