//--- // JustUI.jwidget: Base object for all widgets //--- #ifndef _J_JWIDGET #define _J_JWIDGET #include #include /* jwidget: Base object for all widgets. Functions that ought to work on any widget take void * parameters; this includes jwidget and derived types whose first attribute is a jwidget. A void * parameter named "w" is implicitly a widget. Widgets for a scene are arranged in a tree hierarchy; each object has a unique parent and a number of distinct children, which they own. Widgets destroy their children when they are destroyed. The parent of all the widgets in the scene is called the root, it's the only one without a parent. Every widget has an HTML-like box model described by its geometry object. See the jwidget_geometry type below for details. By default widgets have no geometry to save memory, it is only created when actually used. The size of each widget can be controlled in two ways. Initially, the widget's contents suggest a "natural size". The user can then restrict acceptable sizes by specifying a size hint along with a policy. See the jwidget_size_policy type below for details. Additionally, the user can set stretch parameters to allow the widget to grow and occupy available space. Widgets usually have one of two roles; either they are "containers" for other widgets, or they are "content widgets" that render text and images, or take input from the user. In order to make containers easy to design, each widget can be equipped with a "layout" that automatically arranges children in useful ways, such as vertical lists or grids. Content widgets usually don't have layouts. Polymorphic operations are defined for each widget type and accessed through the type attribute. See the widget extension API at the end of this header for details on polymorphic operations. The following attributes can be accessed by the user: .parent (read-only) .children[] (read-only) .child_count (read-only) */ typedef struct jwidget { /* Parent and children in the widget tree */ struct jwidget *parent; struct jwidget **children; /* Location within the content-box of the parent (after layout) */ int16_t x, y; /* Margin-box size in pixels (after layout) */ int16_t w, h; /* Size hints: these are user-provided sizes, which are combined with the size policy to determine acceptable widget dimensions */ int16_t min_w, min_h; int16_t max_w, max_h; /* Widget geometry, defaults to a fixed geometry object */ struct jwidget_geometry *geometry; /* Layout data, access with the jlayout_{get,set}_*() functions) */ union { jlayout_box layout_box; jlayout_stack layout_stack; jlayout_grid layout_grid; }; /* Widget type, used to find polymorphic operations */ uint8_t type; /* Number of children */ uint8_t child_count; /* Number of pointers allocated in the children array */ uint8_t child_alloc; /* Horizontal and vertical stretch rates */ uint stretch_x :4; uint stretch_y :4; /* Type of layout (see the jlayout_type enum) */ uint layout :4; /* Whether stretch can go beyond the maximum size */ uint stretch_force :1; /* Whether the layout needs to be recomputed */ uint dirty :1; /* Whether the widget needs to be redrawn */ uint update :1; /* Whether widget is visible inside its parent */ uint visible :1; uint :24; } jwidget; /* jwidget_border_style: Styles of widget borders */ typedef enum { /* No border */ J_BORDER_NONE, /* Border is a solid color */ J_BORDER_SOLID, /* TODO: More border styles (especially on fx-CG 50) */ } jwidget_border_style; /* jwidget_geometry: Built-in positioning and border geometry Every widget has a "geometry", which consists of a border and two layers of spacing around the widget: * The "padding" is the spacing between the widget's contents and its border * The "border" is a visible decoration around the widget's contents * The "margin" is the spacing between the border and the widget's edge For users familiar with the CSS box model, this is it. Note, however, that unlike the common CSS property (box-sizing: border-box), JustUI counts the margin as widget-owned space and measures widgets using the margin box. */ typedef struct jwidget_geometry { /* Padding (in pixels) on all four sides; access either using padding.top, .right, .bottom and .left, or using paddings[0] through paddings[3] */ union { uint8_t paddings[4]; jdirs padding; }; /* Width of the border on all four sides */ union { uint8_t borders[4]; jdirs border; }; /* Size of the margin (in pixel) on all four sides */ union { uint8_t margins[4]; jdirs margin; }; /* Border color (as in ) */ int border_color; /* Border style */ jwidget_border_style border_style; /* Background color */ int background_color; } jwidget_geometry; /* Downwards key event: widget is notified of a key press that ocurred while it had active focus. -> .data.key: Key event */ extern uint16_t JWIDGET_KEY; /* Downwards focus-in event: the widget has just received focus */ extern uint16_t JWIDGET_FOCUS_IN; /* Downwards focus-out event: the widget has just lost focus */ extern uint16_t JWIDGET_FOCUS_OUT; //--- // Creation and destruction //--- /* jwidget_create(): Create a widget This function creates a type-less widget. If you want to create labels, buttons, input fields... you need to use the specific creation functions such as jlabel_create(). This function only creates empty widgets, which are primarily useful as containers. If a non-NULL parent is specified, then the new widget becomes a child of that parent. The parent will then handle the positioning and sizing of the new widget, and destroy it automatically. After creating a container with jwidget_create(), it is common to give it a layout and add children to it, either with jwidget_add_child() or through the children's constructors. @parent This widget's parent. -> Returns the new widget (NULL on error). */ jwidget *jwidget_create(void *parent); /* jwidget_destroy(): Destroy a widget This function destroys the specified widget and its children. If the destroyed widget has a parent, the parent is notified, so the widget tree cannot become invalid. However, the layout process should be re-run to layout the remaining scene elements. */ void jwidget_destroy(void *w); //--- // Widget tree manipulation //--- /* jwidget_set_parent(): Change a widget's parent Moves the widget from its current parent to another parent. If the widget already had a parent, it is notified. If the new parent is NULL, the widget is left without a parent. */ void jwidget_set_parent(void *w, void *parent); /* jwidget_add_child(): Add a child at the end of a widget's child list If the widget already had a parent, that parent is notified. */ void jwidget_add_child(void *w, void *child); /* jwidget_insert_child(): Insert a child in the widget's child list Similar to jwidget_add_child(), but the child is added at the requestd position in the parent's child list. The position must be in the range [0 ... w->child_count]. */ void jwidget_insert_child(void *w, void *child, int position); /* jwidget_remove_child(): Remove a child from a widget (w) must be the parent of (child). The child is left without a parent. */ void jwidget_remove_child(void *w, void *child); //--- // Sizing and stretching //--- /* Functions to set the minimum width, minimum height, or both. The minimum size can be cleared by specifying 0. */ void jwidget_set_minimum_width(void *w, int min_width); void jwidget_set_minimum_height(void *w, int min_height); void jwidget_set_minimum_size(void *w, int min_width, int min_height); /* Functions to set the maximum width, maximum height, or both. The maximum size can be cleared by specifying -1. */ void jwidget_set_maximum_width(void *w, int max_width); void jwidget_set_maximum_height(void *w, int max_height); void jwidget_set_maximum_size(void *w, int max_width, int max_height); /* Functions to set both the minimum and maximum size at the same time. */ void jwidget_set_fixed_width(void *w, int width); void jwidget_set_fixed_height(void *w, int height); void jwidget_set_fixed_size(void *w, int width, int height); /* jwidget_set_stretch(): Set the stretch factors for a widget Stretch factors indicate how much a widget wants to grow. Stretch is used in all size policies except the fixed one. Due to storage limits, the stretch factors should be in the range [0 ... 15]. The last parameter indicates whether to allow stretching beyond the maximum size of the widget. In almost all situations this should be false. However, in some cases you might want to completely ignore natural size and allocate space based uniquely on stretch factors. In this case, you can set a fixed size of 0 and enable stretching beyond limits. */ void jwidget_set_stretch(void *w, int stretch_x, int stretch_y, bool stretch_beyond_limits); //--- // Geometry //-- /* jwidget_geometry_r(): Get read-only access to a widget's geometry This function returns a read-only pointer to the specified widget's geometry. Because most widgets don't have customized geometry, JustUI doesn't store any data until the geometry is modified, to save memory. This is why it makes sense to separate read-only and read-write accesses. For widgets without customized geometry, this functions returns a pointer to a fixed constant geometry with zero padding, border and margin. */ jwidget_geometry const *jwidget_geometry_r(void *w); /* jwidget_geometry_rw(): Get read-write access to a widget's geometry This function returns a read-write pointer to the specified widget's geometry. For widgets that don't have customized geometry yet, this will duplicate the default settings. This avoids memory consumption on widgets that don't need custom geometry. Returns NULL if duplication fails because of memory exhaustion. */ jwidget_geometry *jwidget_geometry_rw(void *w); /* jwidget_set_border(): Set a uniform border around a widget This is a shorthand to set (border_style), (border_color), and a uniform border width on a widget's geometry. */ void jwidget_set_border(void *w, jwidget_border_style s, int width, int color); /* jwidget_set_padding(): Set all padding distances around a widget */ void jwidget_set_padding(void *w, int top, int right, int bottom, int left); /* jwidget_set_margin(): Set all margin distances around a widget */ void jwidget_set_margin(void *w, int top, int right, int bottom, int left); /* jwidget_set_background(): Set the widget's background color The default is C_NONE. The background covers content and padding. */ void jwidget_set_background(void *w, int color); //--- // Layout //--- /* jwidget_layout_dirty(): Check whether the tree needs to be laid out This function checks the dirty bit of every widget in the tree. If any widget changes size, the whole tree needs to be laid out again (there are possible optimizations, but they are not implemented yet). */ bool jwidget_layout_dirty(void *scene_root); /* jwidget_layout(): Layout a widget tree This function lays out the specified widget (computing its size and the position of its children) and its children recursively. Because this is a two-phase process going from the children to their parents and then from the parents to their children, it only makes sense to layout the whole tree at once. You should thus call jwidget_layout() only with your scene root. A scene's layout should always be up-to-date before rendering. There is no need to layout at every frame (this would be a waste of resources), but you need to layout after doing any of the following things: * Creating the scene * Adding, removing, or moving visible children around * Changing a widget's contents in a way that affects its natural size * Changing geometry or layout parameters on a widget The layout process determines the size and position of every widget in the tree. Thus, if you need to access this size and position information, you need to keep the layout up-to-date before doing it. */ void jwidget_layout(void *scene_root); /* jwidget_width(): With of a widget's content box jwidget_height(): Height of a widget's content box These functions return the size of the content box of a widget. The content box does not comprise the geometry (padding, border and margins). These dimensions are known only after layout; calling these functions when the layout is not up-to-date will return funny results. */ int jwidget_content_width(void *w); int jwidget_content_height(void *w); /* jwidget_full_width(): Width of a widget's margin box jwidget_full_height(): Height of a widget's margin box These functions return the whole size of the margin box of a widgets; this includes the contents, padding, border and margins. These functions only make sense to call when the layout is up-to-date. */ int jwidget_full_width(void *w); int jwidget_full_height(void *w); //--- // Rendering //--- /* jwidget_visible(): Whether widget is visible A non-visible widget occupies no space and is not rendered, as if it did not exist at all. */ bool jwidget_visible(void *w); /* jwidget_set_visible(): Hide or show a widget */ void jwidget_set_visible(void *w, bool visible); /* jwidget_needs_update(): Check whether the tree needs to be re-rendered If this function returns true, you should re-render the tree. Aditionally, if jwidget_layout_dirty() returns true, you should re-layout the tree and repaint it. jscene_render() will layout automatically if needed, so you just need to call it if either function returns true. When using jscene_run(), a JSCENE_REPAINT event will be emitted in this exact conditions, so just jscene_render() upon JSCENE_REPAINT. */ bool jwidget_needs_update(void *w); /* jwidget_render(): Render a widget This function renders the widget. The specified location (x,y) is the top-left corner of the margin box of the widget. There is no clipping. Unlike jscene_render(), this function does not automatically layout the widgets if there has been changes. */ void jwidget_render(void *w, int x, int y); //--- // Misc //--- /* jwidget_type(): Get a widget's human-readable type name This is the name specified in the jwidgetPoly structure for the type. */ char const *jwidget_type(void *w); #endif /* _J_JWIDGET */