aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gfxconf.example.h3
-rw-r--r--include/gfx_rules.h20
-rw-r--r--include/gwin/frame.h53
-rw-r--r--include/gwin/gwidget.h6
-rw-r--r--include/gwin/gwin.h69
-rw-r--r--include/gwin/options.h7
-rw-r--r--src/gwin/frame.c231
-rw-r--r--src/gwin/gwin.c136
-rw-r--r--src/gwin/gwin.mk1
9 files changed, 518 insertions, 8 deletions
diff --git a/gfxconf.example.h b/gfxconf.example.h
index 4631cddc..349f0c3c 100644
--- a/gfxconf.example.h
+++ b/gfxconf.example.h
@@ -128,8 +128,8 @@
#define GWIN_CONSOLE_USE_BASESTREAM FALSE
#define GWIN_CONSOLE_USE_FLOAT FALSE
#define GWIN_NEED_GRAPH FALSE
-
#define GWIN_NEED_WIDGET FALSE
+ #define GWIN_NEED_HIERARCHY FALSE
#define GWIN_NEED_LABEL FALSE
#define GWIN_NEED_BUTTON FALSE
#define GWIN_BUTTON_LAZY_RELEASE FALSE
@@ -140,6 +140,7 @@
#define GWIN_NEED_RADIO FALSE
#define GWIN_NEED_LIST FALSE
#define GWIN_NEED_PROGRESSBAR FALSE
+ #define GWIN_NEED_FRAME FALSE
///////////////////////////////////////////////////////////////////////////
diff --git a/include/gfx_rules.h b/include/gfx_rules.h
index a129ef76..d547f923 100644
--- a/include/gfx_rules.h
+++ b/include/gfx_rules.h
@@ -58,11 +58,29 @@
#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
#if GFX_DISPLAY_RULE_WARNINGS
- #warning "GWIN: GWIN_NEED_WIDGET is required when a Widget is used. It has been turned on for you."
+ #warning "GWIN: GWIN_NEED_WIDGET is required when a widget is used. It has been turned on for you."
#endif
#undef GWIN_NEED_WIDGET
#define GWIN_NEED_WIDGET TRUE
diff --git a/include/gwin/frame.h b/include/gwin/frame.h
new file mode 100644
index 00000000..885d4ad5
--- /dev/null
+++ b/include/gwin/frame.h
@@ -0,0 +1,53 @@
+/*
+ * 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 include/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 "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;
+
+ // These could probably be removed... I have to think harder later
+ GHandle btnClose;
+ GHandle btnMin;
+ GHandle btnMax;
+} GFrameObject;
+
+GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint16_t flags);
+#define gwinFrameCreate(fo, pInit, flags) gwinGFrameCreate(GDISP, fo, pInit, flags);
+
+GHandle gwinFrameGetClose(GHandle gh);
+
+GHandle gwinFrameGetMin(GHandle gh);
+
+GHandle gwinFrameGetMax(GHandle gh);
+
+#endif /* _GWIN_FRAME_H */
+/** @} */
+
diff --git a/include/gwin/gwidget.h b/include/gwin/gwidget.h
index a18d69b6..8697ca92 100644
--- a/include/gwin/gwidget.h
+++ b/include/gwin/gwidget.h
@@ -27,7 +27,7 @@
* @{
*/
-// Forward definition
+/* Forward definition */
struct GWidgetObject;
/**
@@ -304,5 +304,9 @@ bool_t gwinAttachListener(GListener *pl);
#include "gwin/progressbar.h"
#endif
+#if GWIN_NEED_FRAME || defined(__DOXYGEN__)
+ #include "gwin/frame.h"
+#endif
+
#endif /* _GWIDGET_H */
/** @} */
diff --git a/include/gwin/gwin.h b/include/gwin/gwin.h
index 96055376..465dcbb5 100644
--- a/include/gwin/gwin.h
+++ b/include/gwin/gwin.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
uint16_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;
/* @} */
@@ -377,6 +385,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/include/gwin/options.h b/include/gwin/options.h
index e6d2a81e..83aaee2c 100644
--- a/include/gwin/options.h
+++ b/include/gwin/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
*/
diff --git a/src/gwin/frame.c b/src/gwin/frame.c
new file mode 100644
index 00000000..1699a3c2
--- /dev/null
+++ b/src/gwin/frame.c
@@ -0,0 +1,231 @@
+/*
+ * 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);
+
+#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
+ _gwidgetDestroy, // The destroy routine
+ _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 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;
+}
+
+GHandle gwinFrameGetClose(GHandle gh) {
+ if (gh->vmt != (gwinVMT *)&frameVMT)
+ return;
+
+ return gh2obj->btnClose;
+}
+
+GHandle gwinFrameGetMin(GHandle gh) {
+ if (gh->vmt != (gwinVMT *)&frameVMT)
+ return;
+
+ return gh2obj->btnMin;
+}
+
+GHandle gwinFrameGetMax(GHandle gh) {
+ if (gh->vmt != (gwinVMT *)&frameVMT)
+ return;
+
+ return gh2obj->btnMax;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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/gwin.c b/src/gwin/gwin.c
index 6b9cb81e..0e26149f 100644
--- a/src/gwin/gwin.c
+++ b/src/gwin/gwin.c
@@ -167,11 +167,40 @@ color_t gwinGetDefaultBgColor(void) {
GHandle gwinGWindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit) {
if (!(pgw = _gwindowCreate(g, pgw, pInit, &basegwinVMT, 0)))
return 0;
+
+ #if GWIN_NEED_HIERARCHY
+ pgw->parent = NULL;
+ pgw->sibling = NULL;
+ pgw->child = NULL;
+ #endif
+
gwinSetVisible(pgw, pInit->show);
+
return pgw;
}
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);
@@ -211,7 +240,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) {
@@ -229,7 +268,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) {
@@ -241,7 +290,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
@@ -250,10 +305,83 @@ 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
+ gwinClear(child);
+ gwinClear(parent);
+ 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))) {
diff --git a/src/gwin/gwin.mk b/src/gwin/gwin.mk
index 4c670ea2..dbca7fd8 100644
--- a/src/gwin/gwin.mk
+++ b/src/gwin/gwin.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 \