aboutsummaryrefslogtreecommitdiffstats
path: root/src/gwin
diff options
context:
space:
mode:
Diffstat (limited to 'src/gwin')
-rw-r--r--src/gwin/frame.c247
-rw-r--r--src/gwin/frame.h68
-rw-r--r--src/gwin/gwidget.h6
-rw-r--r--src/gwin/gwin.c150
-rw-r--r--src/gwin/sys_defs.h69
-rw-r--r--src/gwin/sys_make.mk1
-rw-r--r--src/gwin/sys_options.h14
-rw-r--r--src/gwin/sys_rules.h18
8 files changed, 561 insertions, 12 deletions
diff --git a/src/gwin/frame.c b/src/gwin/frame.c
new file mode 100644
index 00000000..fbef54dc
--- /dev/null
+++ b/src/gwin/frame.c
@@ -0,0 +1,247 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gwin/frame.c
+ * @brief GWIN sub-system frame code.
+ *
+ * @defgroup Frame Frame
+ * @ingroup GWIN
+ *
+ * @{
+ */
+
+#include "gfx.h"
+
+#if (GFX_USE_GWIN && GWIN_NEED_FRAME) || defined(__DOXYGEN__)
+
+/* Some values for the default render */
+#define BORDER_X 5
+#define BORDER_Y 30
+#define BUTTON_X 20
+#define BUTTON_Y 20
+
+/* Some useful macros for data type conversions */
+#define gh2obj ((GFrameObject *)gh)
+
+/* Forware declarations */
+void gwinFrameDraw_Std(GWidgetObject *gw, void *param);
+static void _callbackBtn(void *param, GEvent *pe);
+
+static void _frameDestroy(GHandle gh) {
+ /* Deregister the button callback */
+ geventRegisterCallback(&gh2obj->gl, NULL, NULL);
+ geventDetachSource(&gh2obj->gl, NULL);
+
+ /* call the gwidget standard destroy routine */
+ _gwidgetDestroy(gh);
+}
+
+#if GINPUT_NEED_MOUSE
+ static void _mouseDown(GWidgetObject *gw, coord_t x, coord_t y) {
+
+ }
+
+ static void _mouseUp(GWidgetObject *gw, coord_t x, coord_t y) {
+
+ }
+
+ static void _mouseMove(GWidgetObject *gw, coord_t x, coord_t y) {
+
+ }
+#endif
+
+static const gwidgetVMT frameVMT = {
+ {
+ "Frame", // The classname
+ sizeof(GFrameObject), // The object size
+ _frameDestroy, // The destroy routie
+ _gwidgetRedraw, // The redraw routine
+ 0, // The after-clear routine
+ },
+ gwinFrameDraw_Std, // The default drawing routine
+ #if GINPUT_NEED_MOUSE
+ {
+ _mouseDown, // Process mouse down event
+ _mouseUp, // Process mouse up events
+ _mouseMove, // Process mouse move events
+ },
+ #endif
+ #if GINPUT_NEED_TOGGLE
+ {
+ 0, // 1 toggle role
+ 0, // Assign Toggles
+ 0, // Get Toggles
+ 0, // Process toggle off events
+ 0, // Process toggle on events
+ },
+ #endif
+ #if GINPUT_NEED_DIAL
+ {
+ 0, // 1 dial roles
+ 0, // Assign Dials
+ 0, // Get Dials
+ 0, // Process dial move events
+ },
+ #endif
+};
+
+GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint16_t flags) {
+ uint16_t tmp;
+
+ if (!(fo = (GFrameObject *)_gwidgetCreate(g, &fo->w, pInit, &frameVMT)))
+ return 0;
+
+ fo->btnClose = NULL;
+ fo->btnMin = NULL;
+ fo->btnMax = NULL;
+
+ /* Buttons require a border */
+ tmp = flags;
+ if ((tmp & GWIN_FRAME_CLOSE_BTN || tmp & GWIN_FRAME_MINMAX_BTN) && !(tmp & GWIN_FRAME_BORDER)) {
+ tmp |= GWIN_FRAME_BORDER;
+ }
+
+ /* apply flags */
+ fo->w.g.flags |= tmp;
+
+ /* create and initialize the listener if any button is present. */
+ if ((fo->w.g.flags & GWIN_FRAME_CLOSE_BTN) || (fo->w.g.flags & GWIN_FRAME_MINMAX_BTN)) {
+ geventListenerInit(&fo->gl);
+ gwinAttachListener(&fo->gl);
+ geventRegisterCallback(&fo->gl, _callbackBtn, (GHandle)fo);
+ }
+
+ /* create close button if necessary */
+ if (fo->w.g.flags & GWIN_FRAME_CLOSE_BTN) {
+ GWidgetInit wi;
+
+ wi.customDraw = 0;
+ wi.customParam = 0;
+ wi.customStyle = 0;
+ wi.g.show = TRUE;
+
+ wi.g.x = fo->w.g.width - BORDER_X - BUTTON_X;
+ wi.g.y = (BORDER_Y - BUTTON_Y) / 2;
+ wi.g.width = BUTTON_X;
+ wi.g.height = BUTTON_Y;
+ wi.text = "X";
+ fo->btnClose = gwinButtonCreate(NULL, &wi);
+ gwinAddChild((GHandle)fo, fo->btnClose, FALSE);
+ }
+
+ /* create minimize and maximize buttons if necessary */
+ if (fo->w.g.flags & GWIN_FRAME_MINMAX_BTN) {
+ GWidgetInit wi;
+
+ wi.customDraw = 0;
+ wi.customParam = 0;
+ wi.customStyle = 0;
+ wi.g.show = TRUE;
+
+ wi.g.x = (fo->w.g.flags & GWIN_FRAME_CLOSE_BTN) ? fo->w.g.width - 2*BORDER_X - 2*BUTTON_X : fo->w.g.width - BORDER_X - BUTTON_X;
+ wi.g.y = (BORDER_Y - BUTTON_Y) / 2;
+ wi.g.width = BUTTON_X;
+ wi.g.height = BUTTON_Y;
+ wi.text = "O";
+ fo->btnMin = gwinButtonCreate(NULL, &wi);
+ gwinAddChild((GHandle)fo, fo->btnMin, FALSE);
+
+ wi.g.x = (fo->w.g.flags & GWIN_FRAME_CLOSE_BTN) ? fo->w.g.width - 3*BORDER_X - 3*BUTTON_X : fo->w.g.width - BORDER_X - BUTTON_X;
+ wi.g.y = (BORDER_Y - BUTTON_Y) / 2;
+ wi.g.width = BUTTON_X;
+ wi.g.height = BUTTON_Y;
+ wi.text = "_";
+ fo->btnMax = gwinButtonCreate(NULL, &wi);
+ gwinAddChild((GHandle)fo, fo->btnMax, FALSE);
+ }
+
+ gwinSetVisible(&fo->w.g, pInit->g.show);
+
+ return (GHandle)fo;
+}
+
+/* Process a button event */
+static void _callbackBtn(void *param, GEvent *pe) {
+ switch (pe->type) {
+ case GEVENT_GWIN_BUTTON:
+ if (((GEventGWinButton *)pe)->button == ((GFrameObject*)(GHandle)param)->btnClose)
+ gwinDestroy((GHandle)param);
+
+ else if (((GEventGWinButton *)pe)->button == ((GFrameObject*)(GHandle)param)->btnMin)
+ ;/* ToDo */
+
+ else if (((GEventGWinButton *)pe)->button == ((GFrameObject*)(GHandle)param)->btnMax)
+ ;/* ToDo */
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Default render routines //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static const GColorSet* _getDrawColors(GWidgetObject *gw) {
+ if (!(gw->g.flags & GWIN_FLG_ENABLED))
+ return &gw->pstyle->disabled;
+ //if ((gw->g.flags & GBUTTON_FLG_PRESSED))
+ // return &gw->pstyle->pressed;
+
+ return &gw->pstyle->enabled;
+}
+
+void gwinFrameDraw_Std(GWidgetObject *gw, void *param) {
+ GColorSet *pcol;
+ color_t border;
+ color_t background;
+ (void)param;
+
+ if (gw->g.vmt != (gwinVMT *)&frameVMT)
+ return;
+
+ pcol = _getDrawColors(gw);
+
+ // do some magic to make the background lighter than the widgets. Fix this somewhen.
+ border = HTML2COLOR(0x2698DE);
+ background = HTML2COLOR(0xEEEEEE);
+
+ #if GDISP_NEED_CLIP
+ gdispGSetClip(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height);
+ #endif
+
+ // Render the actual frame (with border, if any)
+ if (gw->g.flags & GWIN_FRAME_BORDER) {
+ gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, border);
+ gdispGFillArea(gw->g.display, gw->g.x + BORDER_X, gw->g.y + BORDER_Y, gw->g.width - 2*BORDER_X, gw->g.width - BORDER_Y - BORDER_X, background);
+ } else {
+ // This ensure that the actual frame content (it's children) render at the same spot, no mather whether the frame has a border or not
+ gdispGFillArea(gw->g.display, gw->g.x + BORDER_X, gw->g.y + BORDER_Y, gw->g.width, gw->g.height, background);
+ }
+
+ // Render frame title - if any
+ if (gw->text != NULL) {
+ coord_t text_y;
+
+ text_y = ((BORDER_Y - gdispGetFontMetric(gw->g.font, fontHeight))/2);
+
+ gdispGDrawString(gw->g.display, gw->g.x + BORDER_X, gw->g.y + text_y, gw->text, gw->g.font, pcol->text);
+ }
+
+ #if GDISP_NEED_CLIP
+ gdispGUnsetClip(gw->g.display);
+ #endif
+
+ gwinRedrawChildren((GHandle)gw);
+}
+
+#endif /* (GFX_USE_GWIN && GWIN_NEED_FRAME) || defined(__DOXYGEN__) */
+/** @} */
+
diff --git a/src/gwin/frame.h b/src/gwin/frame.h
new file mode 100644
index 00000000..9d1d63fb
--- /dev/null
+++ b/src/gwin/frame.h
@@ -0,0 +1,68 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gwin/frame.h
+ * @brief GWIN Graphic window subsystem header file.
+ *
+ * @defgroup Frame Frame
+ * @ingroup GWIN
+ *
+ * @details A frame is a rectangular window that can have optional border as well as buttons to
+ * close, maximize and minimize it. The main purpose of this widget is to contain children.
+ *
+ * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h
+ * @pre GWIN_NEED_FRAME must be set to TRUE in your gfxconf.h
+ * @{
+ */
+
+#ifndef _GWIN_FRAME_H
+#define _GWIN_FRAME_H
+
+#include "src/gwin/class_gwin.h"
+
+// Flags for gwinFrameCreate()
+#define GWIN_FRAME_BORDER (GWIN_FIRST_CONTROL_FLAG << 0)
+#define GWIN_FRAME_CLOSE_BTN (GWIN_FIRST_CONTROL_FLAG << 1)
+#define GWIN_FRAME_MINMAX_BTN (GWIN_FIRST_CONTROL_FLAG << 2)
+
+typedef struct GFrameObject {
+ GWidgetObject w;
+
+ GListener gl; // internal listener for the buttons
+ // These could probably be removed... I have to think harder later
+ GHandle btnClose;
+ GHandle btnMin;
+ GHandle btnMax;
+} GFrameObject;
+
+/**
+ * @brief Create a frame widget
+ *
+ * @details This widget provides a window like we know it from desktop systems. You usually use this together with
+ * gwinAddChild().
+ *
+ * @param[in] g The GDisplay to display this window on
+ * @param[in] fo The GFrameObject structure to initialize. If this is NULL the structure is dynamically allocated.
+ * @param[in] pInit The initialization parameters
+ * @param[in] flags Some flags, see notes.
+ *
+ * @note Possible flags are: GWIN_FRAME_BORDER, GWIN_FRAME_CLOSE_BTN, GWIN_FRAME_MINMAX_BTN.
+ * Whether the close or the minimize maximize buttons are used, the boarder is automatically invoked.
+ * @note These frame buttons are processed internally. The close button will invoke a gwinDestroy() which will
+ * destroy the window itself and EVERY child it contains (also children of children).
+ *
+ * @return NULL if there is no resulting widget. A valid GHandle otherwise.
+ *
+ * @api
+ */
+GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint16_t flags);
+#define gwinFrameCreate(fo, pInit, flags) gwinGFrameCreate(GDISP, fo, pInit, flags);
+
+#endif /* _GWIN_FRAME_H */
+/** @} */
+
diff --git a/src/gwin/gwidget.h b/src/gwin/gwidget.h
index 96832fe3..6568859d 100644
--- a/src/gwin/gwidget.h
+++ b/src/gwin/gwidget.h
@@ -27,7 +27,7 @@
* @{
*/
-// Forward definition
+/* Forward definition */
struct GWidgetObject;
/**
@@ -304,5 +304,9 @@ bool_t gwinAttachListener(GListener *pl);
#include "src/gwin/progressbar.h"
#endif
+#if GWIN_NEED_FRAME || defined(__DOXYGEN__)
+ #include "gwin/frame.h"
+#endif
+
#endif /* _GWIDGET_H */
/** @} */
diff --git a/src/gwin/gwin.c b/src/gwin/gwin.c
index 54f42077..b9a488d3 100644
--- a/src/gwin/gwin.c
+++ b/src/gwin/gwin.c
@@ -142,6 +142,12 @@ GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit
_gwm_redim(pgw, pInit->x, pInit->y, pInit->width, pInit->height);
#endif
+ #if GWIN_NEED_HIERARCHY
+ pgw->parent = NULL;
+ pgw->sibling = NULL;
+ pgw->child = NULL;
+ #endif
+
return (GHandle)pgw;
}
@@ -189,6 +195,27 @@ GHandle gwinGWindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pI
}
void gwinDestroy(GHandle gh) {
+ if (!gh) {
+ // should log a runtime error here
+ return;
+ }
+
+ #if GWIN_NEED_HIERARCHY
+ GHandle tmp;
+
+ // recursively destroy our children first
+ for(tmp = gh->child; tmp; tmp = tmp->sibling)
+ gwinDestroy(tmp);
+
+ // remove myself from the hierarchy
+ gwinRemoveChild(gh);
+
+ // issue a redraw of my parent if any
+ if (gh->parent) {
+ gwinRedraw(gh->parent);
+ }
+ #endif
+
// Make the window invisible
gwinSetVisible(gh, FALSE);
@@ -228,7 +255,17 @@ void gwinSetVisible(GHandle gh, bool_t visible) {
}
bool_t gwinGetVisible(GHandle gh) {
- return (gh->flags & GWIN_FLG_VISIBLE) ? TRUE : FALSE;
+ #if GWIN_NEED_HIERARCHY
+ // return TRUE if all widgets (itself + parents) are visble, false otherwise
+ GHandle e = gh;
+ for (e = gh; e; e = e->parent) {
+ if (!(e->flags & GWIN_FLG_VISIBLE))
+ return FALSE;
+ }
+ return TRUE;
+ #else
+ return (gh->flags & GWIN_FLG_VISIBLE) ? TRUE : FALSE;
+ #endif
}
void gwinSetEnabled(GHandle gh, bool_t enabled) {
@@ -246,7 +283,17 @@ void gwinSetEnabled(GHandle gh, bool_t enabled) {
}
bool_t gwinGetEnabled(GHandle gh) {
- return (gh->flags & GWIN_FLG_ENABLED) ? TRUE : FALSE;
+ #if GWIN_NEED_HIERARCHY
+ // return TRUE if all widgets (itself + parents) are enabled, false otherwise
+ GHandle e = gh;
+ for (e = gh; e; e = e->parent) {
+ if (!(e->flags & GWIN_FLG_ENABLED))
+ return FALSE;
+ }
+ return TRUE;
+ #else
+ return (gh->flags & GWIN_FLG_ENABLED) ? TRUE : FALSE;
+ #endif
}
void gwinMove(GHandle gh, coord_t x, coord_t y) {
@@ -258,7 +305,13 @@ void gwinResize(GHandle gh, coord_t width, coord_t height) {
}
void gwinRedraw(GHandle gh) {
- _gwm_redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR);
+ _gwm_redraw(gh, GWIN_WMFLG_PRESERVE | GWIN_WMFLG_NOBGCLEAR);
+
+ #if GWIN_NEED_HIERARCHY
+ GHandle tmp;
+ for (tmp = gh->child; tmp; tmp = tmp->sibling)
+ gwinRedraw(tmp);
+ #endif
}
#if GDISP_NEED_TEXT
@@ -267,10 +320,81 @@ void gwinRedraw(GHandle gh) {
}
#endif
+#if GWIN_NEED_HIERARCHY
+ void gwinAddChild(GHandle parent, GHandle child, bool_t last) {
+ child->parent = parent;
+ child->sibling = NULL;
+ child->child = NULL;
+
+ if (!parent)
+ return;
+
+ if (last && parent->child) {
+ GHandle s = parent->child;
+ while (s->sibling)
+ s = s->sibling;
+ s->sibling = child;
+ } else {
+ child->sibling = parent->child;
+ parent->child = child;
+ }
+
+ // clear the area of the current child position as it will be moved
+ gwinClear(child);
+
+ // window coordinates until now are relative, make them absolute now.
+ child->x += parent->x;
+ child->y += parent->y;
+
+ // redraw the window
+ gwinRedraw(parent);
+ }
+
+ void gwinRemoveChild(GHandle gh) {
+ if(!gh || !gh->parent) {
+ // without a parent, removing is impossible
+ // should log a runtime error here
+ return;
+ }
+
+ if (gh->parent->child == gh) {
+ // we are the first child, update parent
+ gh->parent->child = gh->sibling;
+ } else {
+ // otherwise find our predecessor
+ GHandle tmp = gh->parent->child;
+ while (tmp && tmp->sibling != gh)
+ tmp = tmp->sibling;
+
+ if(!tmp) {
+ // our parent's children list is corrupted
+ // should log a runtime error here
+ return;
+ }
+
+ tmp->sibling = gh->sibling;
+ }
+ }
+
+ void gwinRedrawChildren(GHandle gh) {
+ GHandle tmp;
+ for (tmp = gh->child; tmp; tmp = tmp->sibling)
+ gwinRedraw(tmp);
+ }
+
+ GHandle gwinGetFirstChild(GHandle gh) {
+ return gh->child;
+ }
+
+ GHandle gwinGetNextChild(GHandle gh) {
+ return gh->sibling;
+ }
+#endif
+
void gwinClear(GHandle gh) {
/*
* Don't render anything when the window is not visible but
- * still call the AfterClear() routine as some widgets will
+ * still call tthe AfterClear() routine as some widgets will
* need this to clear internal buffers or similar
*/
if (!((gh->flags & GWIN_FLG_VISIBLE))) {
@@ -278,14 +402,20 @@ void gwinClear(GHandle gh) {
gh->vmt->AfterClear(gh);
} else {
- #if GDISP_NEED_CLIP
- gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
- #endif
+ #if GDISP_NEED_CLIP
+ gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
+ #endif
- gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
- if (gh->vmt->AfterClear)
- gh->vmt->AfterClear(gh);
+ gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
+ if (gh->vmt->AfterClear)
+ gh->vmt->AfterClear(gh);
}
+
+ #if GWIN_NEED_HIERARCHY
+ GHandle tmp;
+ for (tmp = gh->child; tmp; tmp = tmp->sibling)
+ gwinClear(tmp);
+ #endif
}
void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) {
diff --git a/src/gwin/sys_defs.h b/src/gwin/sys_defs.h
index ac2c98c7..6302f28f 100644
--- a/src/gwin/sys_defs.h
+++ b/src/gwin/sys_defs.h
@@ -27,6 +27,9 @@
#if GFX_USE_GWIN || defined(__DOXYGEN__)
+/* Forward declaration */
+typedef struct GWindowObject *GHandle;
+
/**
* @brief A window object structure
* @note Do not access the members directly. Treat it as a black-box and use the method functions.
@@ -39,13 +42,18 @@ typedef struct GWindowObject {
#endif
const struct gwinVMT *vmt; // @< The VMT for this GWIN
GDisplay * display; // @< The display this window is on.
- coord_t x, y; // @< Screen relative position
+ coord_t x, y; // @< Position relative to parent
coord_t width, height; // @< Dimensions of this window
color_t color, bgcolor; // @< The current drawing colors
uint32_t flags; // @< Window flags (the meaning is private to the GWIN class)
#if GDISP_NEED_TEXT
font_t font; // @< The current font
#endif
+ #if GWIN_NEED_HIERARCHY
+ GHandle parent; // @< The parent widget
+ GHandle sibling; // @< The widget to its left (add right later as well)
+ GHandle child; // @< The child widget
+ #endif
} GWindowObject, * GHandle;
/* @} */
@@ -395,6 +403,65 @@ extern "C" {
*/
void gwinRedraw(GHandle gh);
+ #if GWIN_NEED_HIERARCHY
+ /**
+ * @brief Add a child widget to a parent one
+ *
+ * @param[in] parent The parent window (does not need to be parent yet)
+ * @param[in] child The child window
+ * @param[in] last Should the child window be added to the front or the back of the list?
+ *
+ * @api
+ */
+ void gwinAddChild(GHandle parent, GHandle child, bool_t last);
+
+ /**
+ * @brief Remove a child from a parent
+ *
+ * @note Other children of the same parent stay
+ * @note Children of the child are lost, they have to be reassigned manually if necessary.
+ *
+ * @param[in] child The child window
+ *
+ * @api
+ */
+ void gwinRemoveChild(GHandle child);
+
+ /**
+ * @brief Redraw only the children of a parent but not the parent itself
+ *
+ * @details This routine does exactly the same as @p gwinRedraw() but does not
+ * issue a redraw of the passed widget but only of it's children.
+ *
+ * @param[in] gh The widget
+ *
+ * @api
+ */
+ void gwinRedrawChildren(GHandle gh);
+
+ /**
+ * @brief Get first child of a widget
+ *
+ * @return The first child or NULL if the widget has no children
+ *
+ * @param[in] gh The parent widget
+ *
+ * @api
+ */
+ GHandle gwinGetFirstChild(GHandle gh);
+
+ /**
+ * @brief Get the next child of a widget
+ *
+ * @return The next child or NULL if no more childs
+ *
+ * @param[in] gh The parent widget
+ *
+ * @api
+ */
+ GHandle gwinGetNextChild(GHandle gh);
+ #endif
+
#if GWIN_NEED_WINDOWMANAGER || defined (__DOXYGEN__)
/**
* @brief Redraw a window
diff --git a/src/gwin/sys_make.mk b/src/gwin/sys_make.mk
index 4c670ea2..dbca7fd8 100644
--- a/src/gwin/sys_make.mk
+++ b/src/gwin/sys_make.mk
@@ -11,4 +11,5 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin.c \
$(GFXLIB)/src/gwin/radio.c \
$(GFXLIB)/src/gwin/list.c \
$(GFXLIB)/src/gwin/progressbar.c \
+ $(GFXLIB)/src/gwin/frame.c \
diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h
index 656e0e3f..5fe2d93e 100644
--- a/src/gwin/sys_options.h
+++ b/src/gwin/sys_options.h
@@ -30,6 +30,13 @@
#define GWIN_NEED_WIDGET FALSE
#endif
/**
+ * @brief Should the widget hierarchy be included. This provides parent-child features.
+ * @details Defaults to FALSE
+ */
+ #ifndef GWIN_NEED_HIERARCHY
+ #define GWIN_NEED_HIERARCHY FALSE
+ #endif
+ /**
* @brief Should widget functions be included. Needed for any widget (eg Buttons, Sliders etc)
* @details Defaults to FALSE
*/
@@ -37,6 +44,13 @@
#define GWIN_NEED_WIDGET FALSE
#endif
/**
+ * @brief Should the frame widget be included.
+ * @details Defaults to FALSE
+ */
+ #ifndef GWIN_NEED_FRAME
+ #define GWIN_NEED_FRAME FALSE
+ #endif
+ /**
* @brief Should console functions be included.
* @details Defaults to FALSE
*/
diff --git a/src/gwin/sys_rules.h b/src/gwin/sys_rules.h
index 77f029f0..3178def8 100644
--- a/src/gwin/sys_rules.h
+++ b/src/gwin/sys_rules.h
@@ -37,6 +37,24 @@
#endif
#endif
#endif
+ #if GWIN_NEED_HIERARCHY
+ #if !GQUEUE_NEED_ASYNC
+ #if GFX_DISPLAY_RULE_WARNINGS
+ #warning "GWIN: GQUEUE_NEED_ASYNC is required when GWIN_NEED_HIERARCHY is enabled. It has been turned on for you."
+ #endif
+ #undef GQUEUE_NEED_ASYNC
+ #define GQUEUE_NEED_ASYNC TRUE
+ #endif
+ #endif
+ #if GWIN_NEED_FRAME
+ #if !GWIN_NEED_HIERARCHY
+ #if GFX_DISPLAY_RULE_WARNINGS
+ #warning "GWIN: GWIN_NEED_HIERARCHY is required when GIWN_NEED_FRAME is enabled. It has been turned on for you."
+ #endif
+ #undef GWIN_NEED_HIERARCHY
+ #define GWIN_NEED_HIERARCHY TRUE
+ #endif
+ #endif
#if GWIN_NEED_BUTTON || GWIN_NEED_SLIDER || GWIN_NEED_CHECKBOX || GWIN_NEED_LABEL || GWIN_NEED_RADIO || GWIN_NEED_LIST || \
GWIN_NEED_IMAGE || GWIN_NEED_CHECKBOX || GWIN_NEED_PROGRESSBAR
#if !GWIN_NEED_WIDGET