Navigation C API Pages Python bindings Applications

Widgets

There are several key design points that make this library a bit different than the mainstream libraries but apart from that it’s a just widget library, nothing too unexpected here.

One of the major differencies is that widgets are represented by C structures that store the widget state, read only access could be done just by accessing the structure members. If you want to change the state you call helper function that changes it, it may also do sanity checks, and marks the widget to be repainted. Setting values may also invoke on_event() callback, e.g. if user changes checkbox value the application will receive an event.

Another interesting point is that the library uncouples the GUI layout from the actual application functionality. As a matter of a fact the layout can be expressed in JSON and loaded from a file. It does not matter where the GUI elements are put as far as the on_event() callbacks and widgets uid match. An example code is worth of thousands words.

Widget structure

Widget type and payload

Each widget is represented by a gp_widget structure that implements common functionality for all widgets and among others has the type member that is an enum of all implemented widget types. This is commonly used to distinguish the type of widget we are working with and also for sanity checks. Each widget also has an on_event callback and priv pointer which are reserved for the application, i.e. not touched by the library.

The payload pointer points to the actual widget structure and is an anonymous enum of all implemented widget structures so you can do widget->tbox->buf instead of ((struct gp_widget_text_box)*widget->payload)->buf. Which also means that it’s up to you to make sure which type of widget you are working with.

Alignment and fill

Another common functionality implemented in widget structure is alignment and fill. In order to explain these we have to introduce how widgets are structured in an layout.

The widgets are organized in a two dimensional tree where each widget/layer is an rectangle in a plane. The rectanles on a given tree layer are distinct and the rectanle on an upper layer contains all rectangles on lower layer.

The widget layout is computed in two steps, first minimal size is computed recursively from the top level widget down to the leaf widgets, then if the window is bigger than the minimal needed size, the leftover space is being distributed between the widgets.

In order for a widget to take more space than the minimal size, i.e. be resizable the horizontal and/or vertical alignment has to be set to fill. Which especially means that layout can be resized only and only if the top level layout widget is resizable. Apart from fill each widget can be set to be positioned top/center/bottom vertically as well as left/center/right horizontally.

Examples

Grid horizontal and vertical alignment set to fill button to center.

Grid set to fill button to center

Widget layout in JSON
{
 "align": "fill",
 "widgets": [
  {
   "type": "button",
   "label": "Button",
   "on_event": "on_event",
   "align": "center"
  }
 ]
}

Horizontal and vertical alignment set to fill for both.

Both grid and button set to fill

Widget layout in JSON
{
 "align": "fill",
 "widgets": [
  {
   "type": "button",
   "label": "Button",
   "on_event": "on_event",
   "align": "fill"
  }
 ]
}

Horizontal and vertical alignment set to center for grid, button alignment does not matter in this case.

Grid set to center

Widget layout in JSON
{
 "align": "center",
 "widgets": [
  {
   "type": "button",
   "label": "Button",
   "on_event": "on_event",
  }
 ]
}

Widgets

Table 1. Widget JSON attributes
Attribute Type Default Description

uid

string

Widget universal id. Must be unique.

align

enum

center

Sets both haling and valign {center, fill}

haling

enum

center

Horizontal alignment {center, left, right, fill}

valing

enum

center

Vertical alignment {center, top, bottom, fill}

on_event

string

Widget event callback function name

Widget events

Widget events is how the library interacts with the application code, e.g. if button is pressed the button on_event callback handler is called with the gp_widget_event structure pointer that holds information about the event.

Widget event handle and event structure

struct gp_widget {
...
        int (*on_event)(gp_widget_event *);
        void *priv;
...
};

struct gp_widget_event {
        gp_widget *self;
        uint16_t type;
        uint16_t sub_type;
        union {
                void *ptr;
                long val;
                gp_event *input_ev;
        };
};

The event handler is part of the widget structure, the priv field is a user pointer that is not touched by the widget library.

The event handler is passed gp_widget_event structure and returns integer, in most cases the value is not used and it’s customary to return 0 in these cases.

Each widget also has a event mask that can be used to enable or disable particular events. There is also a default mask new widgets are initialized with.

Functions to manipulate widget event mask
void gp_widget_event_mask(gp_widget *self, enum gp_widget_event_type ev_type);

void gp_widget_event_unmask(gp_widget *self, enum gp_widget_event_type ev_type);
Table 2. Widget events
Event name Unmasked Description

GP_WIDGET_EVENT_NEW

Yes

Emitted after widget has been created from a json description.

GP_WIDGET_EVENT_FREE

Yes

Emitted before widget is freed.

GP_WIDGET_EVENT_WIDGET

Yes

Default action, e.g. button pressed, checkbox changed, etc.

GP_WIDGET_EVENT_RESIZE

No

Emitted when widget is resized.

GP_WIDGET_EVENT_INPUT

No

Can be used to redirect unused input events.

The GP_WIDGET_EVENT_NEW is send when widget is created from the JSON description. It’s mainly used to set properties that cannot be known, or shouldn’t be set in the JSON itself.

The GP_WIDGET_EVENT_FREE is send before widget is freed, it can be used for a cleanup.

The GP_WIDGET_EVENT_WIDGET is a widget specific event, e.g. button has been pressed, or text was appended into a textbox. In this case the event sub_type may be set. In most of the cases though widget has only one type of event to send and in this case the sub_type is set to zero.

The GP_WIDGET_EVENT_RESIZE is mainly used for the pixmap widget, where the resized pixmap has tobe repainted by the user code. It can be enabled for any widget though.

The GP_WIDGET_EVENT_INPUT can be used to redirect unused input events, i.e. when widget is focused and gets input events, unhandled events can be send to the widget event handler. In this case the return value from the handler is important. If input event was used by the handler it should return 1 and zero should be returned otherwise, so that the library knows if the event should be propagated to the upper layers or not.