diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gwin/button.c | 12 | ||||
-rw-r--r-- | src/gwin/checkbox.c | 8 | ||||
-rw-r--r-- | src/gwin/class_gwin.h | 144 | ||||
-rw-r--r-- | src/gwin/frame.c | 235 | ||||
-rw-r--r-- | src/gwin/frame.h | 68 | ||||
-rw-r--r-- | src/gwin/gcontainer.c | 169 | ||||
-rw-r--r-- | src/gwin/gcontainer.h | 106 | ||||
-rw-r--r-- | src/gwin/gimage.c | 4 | ||||
-rw-r--r-- | src/gwin/gwidget.c | 65 | ||||
-rw-r--r-- | src/gwin/gwin.c | 249 | ||||
-rw-r--r-- | src/gwin/gwm.c | 136 | ||||
-rw-r--r-- | src/gwin/label.c | 4 | ||||
-rw-r--r-- | src/gwin/list.c | 26 | ||||
-rw-r--r-- | src/gwin/progressbar.c | 10 | ||||
-rw-r--r-- | src/gwin/radio.c | 12 | ||||
-rw-r--r-- | src/gwin/slider.c | 14 | ||||
-rw-r--r-- | src/gwin/sys_defs.h | 51 | ||||
-rw-r--r-- | src/gwin/sys_make.mk | 2 | ||||
-rw-r--r-- | src/gwin/sys_options.h | 21 | ||||
-rw-r--r-- | src/gwin/sys_rules.h | 18 |
20 files changed, 1148 insertions, 206 deletions
diff --git a/src/gwin/button.c b/src/gwin/button.c index f34e4ba3..da68d267 100644 --- a/src/gwin/button.c +++ b/src/gwin/button.c @@ -55,14 +55,14 @@ static void SendButtonEvent(GWidgetObject *gw) { static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; gw->g.flags |= GBUTTON_FLG_PRESSED; - _gwidgetRedraw((GHandle)gw); + _gwidgetUpdate((GHandle)gw); } // A mouse up has occurred (it may or may not be over the button) static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; gw->g.flags &= ~GBUTTON_FLG_PRESSED; - _gwidgetRedraw((GHandle)gw); + _gwidgetUpdate((GHandle)gw); #if !GWIN_BUTTON_LAZY_RELEASE // If the mouse up was not over the button then cancel the event @@ -79,14 +79,14 @@ static void SendButtonEvent(GWidgetObject *gw) { static void ToggleOff(GWidgetObject *gw, uint16_t role) { (void) role; gw->g.flags &= ~GBUTTON_FLG_PRESSED; - _gwidgetRedraw((GHandle)gw); + _gwidgetUpdate((GHandle)gw); } // A toggle on has occurred static void ToggleOn(GWidgetObject *gw, uint16_t role) { (void) role; gw->g.flags |= GBUTTON_FLG_PRESSED; - _gwidgetRedraw((GHandle)gw); + _gwidgetUpdate((GHandle)gw); // Trigger the event on button down (different than for mouse/touch) SendButtonEvent(gw); } @@ -161,7 +161,7 @@ bool_t gwinButtonIsPressed(GHandle gh) { *----------------------------------------------------------*/ static const GColorSet *getDrawColors(GWidgetObject *gw) { - if (!(gw->g.flags & GWIN_FLG_ENABLED)) return &gw->pstyle->disabled; + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed; return &gw->pstyle->enabled; } @@ -336,7 +336,7 @@ static const GColorSet *getDrawColors(GWidgetObject *gw) { if (gw->g.vmt != (gwinVMT *)&buttonVMT) return; pcol = getDrawColors(gw); - if (!(gw->g.flags & GWIN_FLG_ENABLED)) { + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) { sy = 2 * gw->g.height; } else if ((gw->g.flags & GBUTTON_FLG_PRESSED)) { sy = gw->g.height; diff --git a/src/gwin/checkbox.c b/src/gwin/checkbox.c index 8cdd51d9..11d76cb2 100644 --- a/src/gwin/checkbox.c +++ b/src/gwin/checkbox.c @@ -48,7 +48,7 @@ static void SendCheckboxEvent(GWidgetObject *gw) { static void MouseDown(GWidgetObject *gw, coord_t x, coord_t y) { (void) x; (void) y; gw->g.flags ^= GCHECKBOX_FLG_CHECKED; - _gwidgetRedraw((GHandle)gw); + _gwidgetUpdate((GHandle)gw); SendCheckboxEvent(gw); } #endif @@ -57,7 +57,7 @@ static void SendCheckboxEvent(GWidgetObject *gw) { static void ToggleOn(GWidgetObject *gw, uint16_t role) { (void) role; gw->g.flags ^= GCHECKBOX_FLG_CHECKED; - _gwidgetRedraw((GHandle)gw); + _gwidgetUpdate((GHandle)gw); SendCheckboxEvent(gw); } @@ -130,7 +130,7 @@ void gwinCheckboxCheck(GHandle gh, bool_t isChecked) { if (!(gh->flags & GCHECKBOX_FLG_CHECKED)) return; gh->flags &= ~GCHECKBOX_FLG_CHECKED; } - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); SendCheckboxEvent((GWidgetObject *)gh); } @@ -146,7 +146,7 @@ bool_t gwinCheckboxIsChecked(GHandle gh) { *----------------------------------------------------------*/ static const GColorSet *getDrawColors(GWidgetObject *gw) { - if (!(gw->g.flags & GWIN_FLG_ENABLED)) return &gw->pstyle->disabled; + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; if ((gw->g.flags & GCHECKBOX_FLG_CHECKED)) return &gw->pstyle->pressed; return &gw->pstyle->enabled; } diff --git a/src/gwin/class_gwin.h b/src/gwin/class_gwin.h index 50dd480e..b38b6bb8 100644 --- a/src/gwin/class_gwin.h +++ b/src/gwin/class_gwin.h @@ -27,16 +27,20 @@ * @brief The predefined flags for a Window * @{ */ -#define GWIN_FLG_DYNAMIC 0x0001 // @< The GWIN structure is allocated -#define GWIN_FLG_VISIBLE 0x0002 // @< The window is visible -#define GWIN_FLG_MINIMIZED 0x0004 // @< The window is minimized -#define GWIN_FLG_MAXIMIZED 0x0008 // @< The window is maximized -#define GWIN_FLG_ENABLED 0x0010 // @< The window is enabled -#define GWIN_FLG_WIDGET 0x0020 // @< This is a widget -#define GWIN_FLG_ALLOCTXT 0x0040 // @< The widget text is allocated -#define GWIN_FLG_MOUSECAPTURE 0x0080 // @< The widget has captured the mouse -#define GWIN_FIRST_WM_FLAG 0x0100 // @< 4 bits free for the window manager to use -#define GWIN_FIRST_CONTROL_FLAG 0x1000 // @< 4 bits free for Windows and Widgets to use +#define GWIN_FIRST_CONTROL_FLAG 0x00000001 // @< 8 bits free for the control to use +#define GWIN_FLG_VISIBLE 0x00000100 // @< The window is "visible" +#define GWIN_FLG_SYSVISIBLE 0x00000200 // @< The window is visible after parents are tested +#define GWIN_FLG_ENABLED 0x00000400 // @< The window is "enabled" +#define GWIN_FLG_SYSENABLED 0x00000800 // @< The window is enabled after parents are tested +#define GWIN_FLG_DYNAMIC 0x00001000 // @< The GWIN structure is allocated +#define GWIN_FLG_ALLOCTXT 0x00002000 // @< The text/label is allocated +#define GWIN_FLG_MOUSECAPTURE 0x00004000 // @< The window has captured the mouse +#define GWIN_FLG_SUPERMASK 0x000F0000 // @< The bit mask to leave just the window superclass type +#define GWIN_FLG_WIDGET 0x00010000 // @< This is a widget +#define GWIN_FLG_CONTAINER 0x00020000 // @< This is a container +#define GWIN_FLG_MINIMIZED 0x00100000 // @< The window is minimized +#define GWIN_FLG_MAXIMIZED 0x00200000 // @< The window is maximized +#define GWIN_FIRST_WM_FLAG 0x01000000 // @< 8 bits free for the window manager to use /* @} */ /** @@ -104,6 +108,27 @@ typedef struct gwinVMT { /* @} */ #endif +#if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) + + /** + * @brief The Virtual Method Table for a container + * @note A container must have a destroy function. Either use @p _gcontainerDestroy() or use your own function + * which internally calls @p _gcontainerDestroy(). + * @note A container must have a gwin redraw function. Use @p _containerRedraw(). + * @note If toggleroles != 0, ToggleAssign(), ToggleGet() and one or both of ToggleOff() and ToggleOn() must be specified. + * @note If dialroles != 0, DialAssign(), DialGet() and DialMove() must be specified. + * @{ + */ + typedef struct gcontainerVMT { + gwidgetVMT gw; + void (*AdjustPosition) (GHandle gh, coord_t *px, coord_t *py); // @< The container can adjust the relative position of a child (optional) + void (*AdjustSize) (GHandle gh, coord_t *pwidth, coord_t *pheight); // @< The container can adjust the size of a child (optional) + void (*NotifyAdd) (GHandle gh, GHandle ghChild); // @< Notification that a child has been added (optional) + void (*NotifyDelete) (GHandle gh, GHandle ghChild); // @< Notification that a child has been deleted (optional) + } gcontainerVMT; + /* @} */ +#endif + // These flags are needed whether or not we are running a window manager. /** * @brief Flags for redrawing after a visibility change @@ -111,16 +136,11 @@ typedef struct gwinVMT { */ #define GWIN_WMFLG_PRESERVE 0x0001 // @< Preserve whatever existing contents possible if a window can't redraw #define GWIN_WMFLG_NOBGCLEAR 0x0002 // @< Don't clear the area if the window is not visible -#define GWIN_WMFLG_NOZORDER 0x0004 // @< Don't redraw higher z-order windows that overlap +#define GWIN_WMFLG_KEEPCLIP 0x0004 // @< Don't modify the preset clipping area +#define GWIN_WMFLG_NOZORDER 0x0008 // @< Don't redraw higher z-order windows that overlap /* @} */ #if GWIN_NEED_WINDOWMANAGER || defined(__DOXYGEN__) - #if 1 // When we know that wmq is the first element of the GWindowObject structure - #define QItem2GWindow(qi) ((GHandle)qi) - #else - #define QItem2GWindow(qi) ((GHandle)(((char *)(qi)) - (size_t)(&(((GWindowObject *)0)->wmq)))) - #endif - // @note There is only ever one instance of each GWindowManager type typedef struct GWindowManager { const struct gwmVMT *vmt; @@ -136,18 +156,14 @@ typedef struct gwinVMT { bool_t (*Add) (GHandle gh, const GWindowInit *pInit); // @< A window has been added void (*Delete) (GHandle gh); // @< A window has been deleted void (*Redraw) (GHandle gh, int visflags); // @< A window needs to be redraw (or undrawn) - void (*Redim) (GHandle gh, coord_t x, coord_t y, coord_t w, coord_t h); // @< A window wants to be moved or resized + void (*Size) (GHandle gh, coord_t w, coord_t h); // @< A window wants to be resized + void (*Move) (GHandle gh, coord_t x, coord_t y); // @< A window wants to be moved void (*Raise) (GHandle gh); // @< A window wants to be on top void (*MinMax) (GHandle gh, GWindowMinMax minmax); // @< A window wants to be minimized/maximised } gwmVMT; /* @} */ /** - * @brief The list of all windows in the system - */ - extern gfxQueueASync _GWINList; - - /** * @brief The current window manager */ extern GWindowManager * _GWINwm; @@ -197,13 +213,93 @@ GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit void _gwidgetDestroy(GHandle gh); /** - * @brief Redraw the Widget object + * @brief Redraw the Widget object (VMT method only) * * @param[in] gh The widget to redraw * + * @note Do not use this routine to update a widget after a status change. + * Use @p _gwidgetUpdate() instead. The difference is that this routine + * does not set the clip region. This routine should only be used in the + * VMT. + * * @notapi */ void _gwidgetRedraw(GHandle gh); + + /** + * @brief Redraw the Widget object after a widget status change. + * + * @param[in] gh The widget to redraw + * + * @note Use this routine to update a widget after a status change. + * + * @notapi + */ + void _gwidgetUpdate(GHandle gh); +#endif + +#if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) + /** + * @brief Initialise (and allocate if necessary) the base Container object + * + * @param[in] g The GDisplay to display this window on + * @param[in] pgw The GContainerObject structure. If NULL one is allocated from the heap + * @param[in] pInit The user initialization parameters + * @param[in] vmt The virtual method table for the Container object + * + * @return The GHandle of the created widget + * + * @notapi + */ + GHandle _gcontainerCreate(GDisplay *g, GContainerObject *pgw, const GWidgetInit *pInit, const gcontainerVMT *vmt); + + /** + * @brief Destroy the Container object + * + * @param[in] gh The container to destroy + * + * @notapi + */ + void _gcontainerDestroy(GHandle gh); + + /** + * @brief Redraw the Container object (VMT method only) + * + * @param[in] gh The container to redraw + * + * @note Do not use this routine to update a container after a status change. + * Use @p _gcontainerUpdate() instead. The difference is that this routine + * does not set the clip region. This routine should only be used in the + * VMT. + * + * @notapi + */ + void _gcontainerRedraw(GHandle gh); + + /** + * @brief Redraw the Container object after a container status change. + * + * @param[in] gh The container to redraw + * + * @note Use this routine to update a container after a status change. + * + * @notapi + */ + void _gcontainerUpdate(GHandle gh); + + /** + * @brief Apply the specified action to a window and its children. + * @note The action is applied to the parent first and then its children. + * @note This routine is built to keep stack usage from recursing to a minimum. + * + * @param[in] gh The window to recurse through + * @param[in] fn The function to apply. If it returns TRUE any children it has should also have the function applied + * + * @notapi + */ + void _gwinRecurse(GHandle gh, bool_t (*fn)(GHandle gh)); +#else + #define _gwinRecurse(gh, fn) fn(gh) #endif #ifdef __cplusplus diff --git a/src/gwin/frame.c b/src/gwin/frame.c new file mode 100644 index 00000000..16e16987 --- /dev/null +++ b/src/gwin/frame.c @@ -0,0 +1,235 @@ +/* + * 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 + +/* 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 gcontainer standard destroy routine */ + _gcontainerDestroy(gh); +} + +#if 0 && 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 gcontainerVMT frameVMT = { + { + { + "Frame", // The classname + sizeof(GFrameObject), // The object size + _frameDestroy, // The destroy routie + _gcontainerRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinFrameDraw_Std, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + 0,//_mouseDown, // Process mouse down event + 0,//_mouseUp, // Process mouse up events + 0,//_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 + }, + 0, // Adjust the relative position of a child (optional) + 0, // Adjust the size of a child (optional) + 0, // A child has been added (optional) + 0, // A child has been deleted (optional) +}; + +GHandle gwinGFrameCreate(GDisplay *g, GFrameObject *fo, GWidgetInit *pInit, uint32_t flags) { + if (!(fo = (GFrameObject *)_gcontainerCreate(g, &fo->gc, pInit, &frameVMT))) + return 0; + + fo->btnClose = 0; + fo->btnMin = 0; + fo->btnMax = 0; + + /* Buttons require a border */ + if ((flags & (GWIN_FRAME_CLOSE_BTN|GWIN_FRAME_MINMAX_BTN))) + flags |= GWIN_FRAME_BORDER; + + /* apply flags */ + fo->gc.g.flags |= flags; + + /* create and initialize the listener if any button is present. */ + if ((flags & (GWIN_FRAME_CLOSE_BTN|GWIN_FRAME_MINMAX_BTN))) { + geventListenerInit(&fo->gl); + gwinAttachListener(&fo->gl); + geventRegisterCallback(&fo->gl, _callbackBtn, (GHandle)fo); + } + + /* create close button if necessary */ + if ((flags & GWIN_FRAME_CLOSE_BTN)) { + GWidgetInit wi; + + gwinWidgetClearInit(&wi); + wi.g.show = TRUE; + wi.g.parent = &fo->gc.g; + + wi.g.x = fo->gc.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 = gwinGButtonCreate(g, 0, &wi); + } + + /* create minimize and maximize buttons if necessary */ + if ((flags & GWIN_FRAME_MINMAX_BTN)) { + GWidgetInit wi; + + gwinWidgetClearInit(&wi); + wi.g.show = TRUE; + wi.g.parent = &fo->gc.g; + + wi.g.x = (flags & GWIN_FRAME_CLOSE_BTN) ? fo->gc.g.width - 2*BORDER_X - 2*BUTTON_X : fo->gc.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 = gwinGButtonCreate(g, 0, &wi); + + wi.g.x = (flags & GWIN_FRAME_CLOSE_BTN) ? fo->gc.g.width - 3*BORDER_X - 3*BUTTON_X : fo->gc.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 = gwinGButtonCreate(g, 0, &wi); + } + + gwinSetVisible(&fo->gc.g, pInit->g.show); + + return &fo->gc.g; +} + +/* 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_SYSENABLED)) + 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) { + const 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); + + // 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.height - 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); + } +} + +#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..1b20b931 --- /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 { + GContainerObject gc; + + 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, uint32_t flags); +#define gwinFrameCreate(fo, pInit, flags) gwinGFrameCreate(GDISP, fo, pInit, flags); + +#endif /* _GWIN_FRAME_H */ +/** @} */ + diff --git a/src/gwin/gcontainer.c b/src/gwin/gcontainer.c new file mode 100644 index 00000000..704c26c0 --- /dev/null +++ b/src/gwin/gcontainer.c @@ -0,0 +1,169 @@ +/* + * 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/gcontainer.c + * @brief GWIN sub-system container code. + * + * @defgroup Containers Containers + * @ingroup GWIN + * + * @{ + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_CONTAINERS + +#include "src/gwin/class_gwin.h" + +void _gcontainerInit(void) +{ +} + +void _gcontainerDeinit(void) +{ +} + +GHandle _gcontainerCreate(GDisplay *g, GContainerObject *pgc, const GWidgetInit *pInit, const gcontainerVMT *vmt) { + if (!(pgc = (GContainerObject *)_gwidgetCreate(g, (GWidgetObject *)pgc, pInit, &vmt->gw))) + return 0; + + pgc->g.flags |= GWIN_FLG_CONTAINER; + + return &pgc->g; +} + +void _gcontainerDestroy(GHandle gh) { + GHandle child; + + while((child = gwinGetFirstChild(gh))) + gwinDestroy(child); + _gwidgetDestroy(gh); +} + +void _gcontainerRedraw(GHandle gh) { + GHandle child; + + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return; + + ((GWidgetObject *)gh)->fnDraw((GWidgetObject *)gh, ((GWidgetObject *)gh)->fnParam); + + for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) + gwinRedraw(child); +} + +void _gcontainerUpdate(GHandle gh) { + GHandle child; + + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return; + + #if GDISP_NEED_CLIP + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + #endif + ((GWidgetObject *)gh)->fnDraw((GWidgetObject *)gh, ((GWidgetObject *)gh)->fnParam); + + for(child = gwinGetFirstChild(gh); child; child = gwinGetSibling(child)) + gwinRedraw(child); +} + +void _gwinRecurse(GHandle gh, bool_t (*fn)(GHandle gh)) { + if (fn(gh) && (gh->flags & GWIN_FLG_CONTAINER)) { + // Apply to this windows children + for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh)) { + // Only recurse when we have to. Otherwise apply it directly + if ((gh->flags & GWIN_FLG_CONTAINER)) + _gwinRecurse(gh, fn); + else + fn(gh); + } + } +} + +GHandle gwinGetFirstChild(GHandle gh) { + GHandle child; + + for(child = gwinGetNextWindow(0); child; child = gwinGetNextWindow(child)) + if (child->parent == gh) + return child; + return 0; +} + +GHandle gwinGetSibling(GHandle gh) { + GHandle child; + + for(child = gwinGetNextWindow(gh), gh = gh->parent; child; child = gwinGetNextWindow(child)) + if (child->parent == gh) + return child; + return 0; +} + +#endif /* GFX_USE_GWIN && GWIN_NEED_CONTAINERS */ +/** @} */ + +/*----------------------------------------------- + * The simplest container type - a container + *----------------------------------------------- + * + * @defgroup Containers Containers + * @ingroup GWIN + * + * @{ + */ + +#if GFX_USE_GWIN && GWIN_NEED_CONTAINER + +static void DrawSimpleContainer(GWidgetObject *gw, void *param) { + (void) param; + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); +} + +// The container VMT table +static const gcontainerVMT containerVMT = { + { + { + "Container", // The classname + sizeof(GContainerObject), // The object size + _gcontainerDestroy, // The destroy routine + _gcontainerRedraw, // The redraw routine + 0, // The after-clear routine + }, + DrawSimpleContainer, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + 0, 0, 0, // No mouse + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, 0, 0, 0, 0, // No toggles + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, 0, 0, 0, // No dials + }, + #endif + }, + 0, // Adjust the relative position of a child (optional) + 0, // Adjust the size of a child (optional) + 0, // A child has been added (optional) + 0, // A child has been deleted (optional) +}; + +GHandle gwinGContainerCreate(GDisplay *g, GContainerObject *gw, const GWidgetInit *pInit) { + if (!(gw = (GContainerObject *)_gcontainerCreate(g, gw, pInit, &containerVMT))) + return 0; + + gwinSetVisible((GHandle)gw, pInit->g.show); + return (GHandle)gw; +} + +#endif +/** @} */ diff --git a/src/gwin/gcontainer.h b/src/gwin/gcontainer.h new file mode 100644 index 00000000..8159797c --- /dev/null +++ b/src/gwin/gcontainer.h @@ -0,0 +1,106 @@ +/* + * 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/gcontainer.h + * @brief GWIN Containers header file. + */ + +#ifndef _GCONTAINER_H +#define _GCONTAINER_H + +/* This file is included within "gwin/gwin.h" */ + +/** + * @defgroup Containers Containers + * @ingroup GWIN + * + * @details A Container is a GWindow that supports child windows. It is also + * a widget in its own right and therefore can accept user input directly. + * + * @pre GFX_USE_GWIN and GWIN_NEED_CONTAINERS must be set to TRUE in your gfxconf.h + * @{ + */ + +// Forward definition +struct GContainerObject; + +/** + * @brief The GWIN Container structure + * @note A container is a GWIN widget that can have children. + * @note Do not access the members directly. Treat it as a black-box and use the method functions. + * + * @{ + */ +typedef GWidgetObject GContainerObject; +/* @} */ + +/** + * A comment/rant on the above structure: + * We would really like the GWidgetObject member to be anonymous. While this is + * allowed under the C11, C99, GNU and various other standards which have been + * around forever - compiler support often requires special flags e.g + * gcc requires the -fms-extensions flag (no wonder the language and compilers have + * not really progressed in 30 years). As portability is a key requirement + * we unfortunately won't use this useful feature in case we get a compiler that + * won't support it even with special flags. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Get the first child window + * + * @return The first child or NULL if are no children windows + * + * @param[in] gh The parent container or NULL to get the first top level window + * + * @api + */ + GHandle gwinGetFirstChild(GHandle gh); + + /** + * @brief Get the next child window in the z-order + * + * @return The next window or NULL if no more children + * + * @param[in] gh The window to obtain the next sibling of. + * + * @note This returns the next window under the current parent window. + * Unlike @p gwinGetNextWindow() it will only return windows that + * have the same parent as the supplied window. + * + * @api + */ + GHandle gwinGetSibling(GHandle gh); + + /** + * @brief Create a simple container. + * @return NULL if there is no resultant drawing area, otherwise a window handle. + * + * @param[in] g The GDisplay to display this window on + * @param[in] gb The GContainerObject structure to initialise. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialisation parameters + * + * @api + */ + GHandle gwinGContainerCreate(GDisplay *g, GContainerObject *gw, const GWidgetInit *pInit); + #define gwinContainerCreate(gc, pInit) gwinGContainerCreate(GDISP, gc, pInit) + +#ifdef __cplusplus +} +#endif + +/* Include extra container types */ +#if GWIN_NEED_FRAME || defined(__DOXYGEN__) + #include "src/gwin/frame.h" +#endif + +#endif /* _GCONTAINER_H */ +/** @} */ diff --git a/src/gwin/gimage.c b/src/gwin/gimage.c index 9d6363ba..baf14ad1 100644 --- a/src/gwin/gimage.c +++ b/src/gwin/gimage.c @@ -30,7 +30,7 @@ static void _destroy(GWindowObject *gh) { #define gh ((GHandle)param) // We need to re-test the visibility in case it has been made invisible since the last frame. - if ((gh->flags & GWIN_FLG_VISIBLE)) { + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw // but we put it in for safety anyway #if GDISP_NEED_CLIP @@ -160,7 +160,7 @@ bool_t gwinImageOpenGFile(GHandle gh, GFILE *f) { if ((gdispImageOpenGFile(&widget(gh)->image, f) & GDISP_IMAGE_ERR_UNRECOVERABLE)) return FALSE; - if ((gh->flags & GWIN_FLG_VISIBLE)) { + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw // but we put it in for safety anyway #if GDISP_NEED_CLIP diff --git a/src/gwin/gwidget.c b/src/gwin/gwidget.c index c46520ea..de31daa8 100644 --- a/src/gwin/gwidget.c +++ b/src/gwin/gwidget.c @@ -82,16 +82,15 @@ static const GWidgetStyle * defaultStyle = &BlackWidgetStyle; /* Process an event */ static void gwidgetEvent(void *param, GEvent *pe) { - #define gh QItem2GWindow(qi) #define pme ((GEventMouse *)pe) #define pte ((GEventToggle *)pe) #define pde ((GEventDial *)pe) - const gfxQueueASyncItem * qi; + GHandle gh; #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL) - uint16_t role; + uint16_t role; #endif - (void) param; + (void) param; // Process various events switch (pe->type) { @@ -100,14 +99,14 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_MOUSE: case GEVENT_TOUCH: // Cycle through all windows - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { // check if the widget matches this display if (gh->display != pme->display) continue; - // check if it a widget that is enabled and visible - if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_VISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_VISIBLE)) + // check if it is a widget that is enabled and visible + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) continue; // Are we captured? @@ -134,10 +133,10 @@ static void gwidgetEvent(void *param, GEvent *pe) { #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE case GEVENT_TOGGLE: // Cycle through all windows - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { // check if it a widget that is enabled and visible - if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_VISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_VISIBLE)) + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) continue; for(role = 0; role < wvmt->toggleroles; role++) { @@ -158,10 +157,10 @@ static void gwidgetEvent(void *param, GEvent *pe) { #if GFX_USE_GINPUT && GINPUT_NEED_DIAL case GEVENT_DIAL: // Cycle through all windows - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { // check if it a widget that is enabled and visible - if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_VISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_VISIBLE)) + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) continue; for(role = 0; role < wvmt->dialroles; role++) { @@ -178,7 +177,6 @@ static void gwidgetEvent(void *param, GEvent *pe) { break; } - #undef gh #undef pme #undef pte #undef pde @@ -186,11 +184,10 @@ static void gwidgetEvent(void *param, GEvent *pe) { #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE static GHandle FindToggleUser(uint16_t instance) { - #define gh QItem2GWindow(qi) - const gfxQueueASyncItem * qi; - uint16_t role; + GHandle gh; + uint16_t role; - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget continue; @@ -200,17 +197,15 @@ static void gwidgetEvent(void *param, GEvent *pe) { } } return 0; - #undef gh } #endif #if GFX_USE_GINPUT && GINPUT_NEED_DIAL static GHandle FindDialUser(uint16_t instance) { - #define gh QItem2GWindow(qi) - const gfxQueueASyncItem * qi; - uint16_t role; + GHandle gh; + uint16_t role; - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if (!(gh->flags & GWIN_FLG_WIDGET)) // check if it a widget continue; @@ -220,7 +215,6 @@ static void gwidgetEvent(void *param, GEvent *pe) { } } return 0; - #undef gh } #endif @@ -236,9 +230,14 @@ void _gwidgetDeinit(void) } GHandle _gwidgetCreate(GDisplay *g, GWidgetObject *pgw, const GWidgetInit *pInit, const gwidgetVMT *vmt) { - if (!(pgw = (GWidgetObject *)_gwindowCreate(g, &pgw->g, &pInit->g, &vmt->g, GWIN_FLG_WIDGET|GWIN_FLG_ENABLED))) + if (!(pgw = (GWidgetObject *)_gwindowCreate(g, &pgw->g, &pInit->g, &vmt->g, GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED))) return 0; + #if GWIN_NEED_COLLECTIONS + // This window can't be system enabled if the parent is not enabled + if (pgw->parent && !(pgw->parent->flags & GWIN_FLG_SYSENABLED)) + pgw->g.flags &= ~GWIN_FLG_SYSENABLED; + #endif pgw->text = pInit->text ? pInit->text : ""; pgw->fnDraw = pInit->customDraw ? pInit->customDraw : vmt->DefaultDraw; pgw->fnParam = pInit->customParam; @@ -287,13 +286,19 @@ void _gwidgetDestroy(GHandle gh) { } void _gwidgetRedraw(GHandle gh) { - if (!(gh->flags & GWIN_FLG_VISIBLE)) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) + return; + + gw->fnDraw(gw, gw->fnParam); +} + +void _gwidgetUpdate(GHandle gh) { + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif - gw->fnDraw(gw, gw->fnParam); } @@ -310,11 +315,9 @@ void gwinSetDefaultStyle(const GWidgetStyle *pstyle, bool_t updateAll) { pstyle = &BlackWidgetStyle; if (updateAll) { - const gfxQueueASyncItem * qi; GHandle gh; - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { - gh = QItem2GWindow(qi); + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if ((gh->flags & GWIN_FLG_WIDGET) && ((GWidgetObject *)gh)->pstyle == defaultStyle) gwinSetStyle(gh, pstyle); } @@ -359,7 +362,7 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { gw->text = (const char *)str; } else gw->text = text; - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); } const char *gwinGetText(GHandle gh) { @@ -375,7 +378,7 @@ void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle) { gw->pstyle = pstyle ? pstyle : defaultStyle; gh->bgcolor = pstyle->background; gh->color = pstyle->enabled.text; - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); } const GWidgetStyle *gwinGetStyle(GHandle gh) { @@ -391,7 +394,7 @@ void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void *param) { gw->fnDraw = fn ? fn : wvmt->DefaultDraw; gw->fnParam = param; - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); } bool_t gwinAttachListener(GListener *pl) { diff --git a/src/gwin/gwin.c b/src/gwin/gwin.c index 84443f9f..dfcb8d4c 100644 --- a/src/gwin/gwin.c +++ b/src/gwin/gwin.c @@ -39,10 +39,11 @@ static color_t defaultBgColor = Black; #if GWIN_NEED_WINDOWMANAGER #define _gwm_redraw(gh, flags) _GWINwm->vmt->Redraw(gh, flags) - #define _gwm_redim(gh,x,y,w,h) _GWINwm->vmt->Redim(gh,x,y,w,h); + #define _gwm_move(gh,x,y) _GWINwm->vmt->Move(gh,x,y); + #define _gwm_resize(gh,w,h) _GWINwm->vmt->Size(gh,w,h); #else static void _gwm_redraw(GHandle gh, int flags) { - if ((gh->flags & GWIN_FLG_VISIBLE)) { + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { if (gh->vmt->Redraw) { #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); @@ -63,19 +64,22 @@ static color_t defaultBgColor = Black; gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, defaultBgColor); } } - static void _gwm_redim(GHandle gh, coord_t x, coord_t y, coord_t width, coord_t height) { - gh->x = x; gh->y = y; + static void _gwm_resize(GHandle gh, coord_t width, coord_t height) { gh->width = width; gh->height = height; - if (gh->x < 0) { gh->width += gh->x; gh->x = 0; } - if (gh->y < 0) { gh->height += gh->y; gh->y = 0; } - if (gh->x > gdispGetWidth()-MIN_WIN_WIDTH) gh->x = gdispGetWidth()-MIN_WIN_WIDTH; - if (gh->y > gdispGetHeight()-MIN_WIN_HEIGHT) gh->y = gdispGetHeight()-MIN_WIN_HEIGHT; if (gh->width < MIN_WIN_WIDTH) { gh->width = MIN_WIN_WIDTH; } if (gh->height < MIN_WIN_HEIGHT) { gh->height = MIN_WIN_HEIGHT; } if (gh->x+gh->width > gdispGetWidth()) gh->width = gdispGetWidth() - gh->x; if (gh->y+gh->height > gdispGetHeight()) gh->height = gdispGetHeight() - gh->y; - - // Redraw the window + _gwm_redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); + } + static void _gwm_move(GHandle gh, coord_t x, coord_t y) { + gh->x = x; gh->y = y; + if (gh->x < 0) gh->x = 0; + if (gh->y < 0) gh->y = 0; + if (gh->x > gdispGetWidth()-MIN_WIN_WIDTH) gh->x = gdispGetWidth()-MIN_WIN_WIDTH; + if (gh->y > gdispGetHeight()-MIN_WIN_HEIGHT) gh->y = gdispGetHeight()-MIN_WIN_HEIGHT; + if (gh->x+gh->width > gdispGetWidth()) gh->width = gdispGetWidth() - gh->x; + if (gh->y+gh->height > gdispGetHeight()) gh->height = gdispGetHeight() - gh->y; _gwm_redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); } #endif @@ -86,20 +90,30 @@ static color_t defaultBgColor = Black; void _gwinInit(void) { + #if GWIN_NEED_WINDOWMANAGER + extern void _gwmInit(void); + + _gwmInit(); + #endif #if GWIN_NEED_WIDGET extern void _gwidgetInit(void); _gwidgetInit(); #endif - #if GWIN_NEED_WINDOWMANAGER - extern void _gwmInit(void); + #if GWIN_NEED_CONTAINERS + extern void _gcontainerInit(void); - _gwmInit(); + _gcontainerInit(); #endif } void _gwinDeinit(void) { + #if GWIN_NEED_CONTAINERS + extern void _gcontainerDeinit(void); + + _gcontainerDeinit(); + #endif #if GWIN_NEED_WIDGET extern void _gwidgetDeinit(void); @@ -132,6 +146,18 @@ GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit pgw->font = defaultFont; #endif + #if GWIN_NEED_CONTAINERS + if (pInit->parent) { + if (!(pInit->parent->flags & GWIN_FLG_CONTAINER) || pgw->display != pInit->parent->display) { + if ((pgw->flags & GWIN_FLG_DYNAMIC)) + gfxFree(pgw); + return 0; + } + pgw->parent = pInit->parent; + } else + pgw->parent = 0; + #endif + #if GWIN_NEED_WINDOWMANAGER if (!_GWINwm->vmt->Add(pgw, pInit)) { if ((pgw->flags & GWIN_FLG_DYNAMIC)) @@ -139,7 +165,15 @@ GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit return 0; } #else - _gwm_redim(pgw, pInit->x, pInit->y, pInit->width, pInit->height); + pgw->x = pgw->y = pgw->width = pgw->height = 0; + _gwm_move(pgw, pInit->x, pInit->y); + _gwm_resize(pgw, pInit->width, pInit->height); + #endif + + #if GWIN_NEED_CONTAINERS + // Notify the parent it has been added + if (pgw->parent && ((gcontainerVMT *)pgw->parent->vmt)->NotifyAdd) + ((gcontainerVMT *)pgw->parent->vmt)->NotifyAdd(pgw->parent, pgw); #endif return (GHandle)pgw; @@ -203,6 +237,12 @@ void gwinDestroy(GHandle gh) { // Make the window invisible gwinSetVisible(gh, FALSE); + #if GWIN_NEED_CONTAINERS + // Notify the parent it is about to be deleted + if (gh->parent && ((gcontainerVMT *)gh->parent->vmt)->NotifyDelete) + ((gcontainerVMT *)gh->parent->vmt)->NotifyDelete(gh->parent, gh); + #endif + // Remove from the window manager #if GWIN_NEED_WINDOWMANAGER _GWINwm->vmt->Delete(gh); @@ -224,48 +264,120 @@ const char *gwinGetClassName(GHandle gh) { return gh->vmt->classname; } -void gwinSetVisible(GHandle gh, bool_t visible) { - if (visible) { - if (!(gh->flags & GWIN_FLG_VISIBLE)) { - gh->flags |= GWIN_FLG_VISIBLE; - _gwm_redraw(gh, 0); +#if GWIN_NEED_CONTAINERS + // These two sub-functions set/clear system visibility recursively. + static bool_t setSysVisFlag(GHandle gh) { + // If we are now visible and our parent is visible + if ((gh->flags & GWIN_FLG_VISIBLE) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))) { + gh->flags |= GWIN_FLG_SYSVISIBLE; + return TRUE; } - } else { - if ((gh->flags & GWIN_FLG_VISIBLE)) { - gh->flags &= ~GWIN_FLG_VISIBLE; - _gwm_redraw(gh, 0); + return FALSE; + } + static bool_t clrSysVisFlag(GHandle gh) { + // If we are now not visible but our parent is visible + if (!(gh->flags & GWIN_FLG_VISIBLE) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))) { + gh->flags &= ~GWIN_FLG_SYSVISIBLE; + return TRUE; } + return FALSE; } -} + void gwinSetVisible(GHandle gh, bool_t visible) { + if (visible) { + if (!(gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags |= GWIN_FLG_VISIBLE; + _gwinRecurse(gh, setSysVisFlag); + _gwm_redraw(gh, 0); + } + } else { + if ((gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags &= ~GWIN_FLG_VISIBLE; + _gwinRecurse(gh, clrSysVisFlag); + _gwm_redraw(gh, 0); + } + } + } +#else + void gwinSetVisible(GHandle gh, bool_t visible) { + if (visible) { + if (!(gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); + _gwm_redraw(gh, 0); + } + } else { + if ((gh->flags & GWIN_FLG_VISIBLE)) { + gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); + _gwm_redraw(gh, 0); + } + } + } +#endif bool_t gwinGetVisible(GHandle gh) { - return (gh->flags & GWIN_FLG_VISIBLE) ? TRUE : FALSE; + return (gh->flags & GWIN_FLG_SYSVISIBLE) ? TRUE : FALSE; } -void gwinSetEnabled(GHandle gh, bool_t enabled) { - if (enabled) { - if (!(gh->flags & GWIN_FLG_ENABLED)) { - gh->flags |= GWIN_FLG_ENABLED; - _gwm_redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); +#if GWIN_NEED_CONTAINERS + // These two sub-functions set/clear system enable recursively. + static bool_t setSysEnaFlag(GHandle gh) { + // If we are now enabled and our parent is enabled + if ((gh->flags & GWIN_FLG_ENABLED) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { + gh->flags |= GWIN_FLG_SYSENABLED; + return TRUE; } - } else { - if ((gh->flags & GWIN_FLG_ENABLED)) { - gh->flags &= ~GWIN_FLG_ENABLED; - _gwm_redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); + return FALSE; + } + static bool_t clrSysEnaFlag(GHandle gh) { + // If we are now not enabled but our parent is enabled + if (!(gh->flags & GWIN_FLG_ENABLED) && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { + gh->flags &= ~GWIN_FLG_SYSENABLED; + return TRUE; } + return FALSE; } -} + void gwinSetEnabled(GHandle gh, bool_t enabled) { + if (enabled) { + if (!(gh->flags & GWIN_FLG_ENABLED)) { + gh->flags |= GWIN_FLG_ENABLED; + _gwinRecurse(gh, setSysEnaFlag); + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) + _gwm_redraw(gh, GWIN_WMFLG_PRESERVE); + } + } else { + if ((gh->flags & GWIN_FLG_ENABLED)) { + gh->flags &= ~GWIN_FLG_ENABLED; + _gwinRecurse(gh, clrSysEnaFlag); + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) + _gwm_redraw(gh, GWIN_WMFLG_PRESERVE); + } + } + } +#else + void gwinSetEnabled(GHandle gh, bool_t enabled) { + if (enabled) { + if (!(gh->flags & GWIN_FLG_ENABLED)) { + gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + _gwm_redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); + } + } else { + if ((gh->flags & GWIN_FLG_ENABLED)) { + gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + _gwm_redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); + } + } + } +#endif bool_t gwinGetEnabled(GHandle gh) { - return (gh->flags & GWIN_FLG_ENABLED) ? TRUE : FALSE; + return (gh->flags & GWIN_FLG_SYSENABLED) ? TRUE : FALSE; } void gwinMove(GHandle gh, coord_t x, coord_t y) { - _gwm_redim(gh, x, y, gh->width, gh->height); + _gwm_move(gh, x, y); } void gwinResize(GHandle gh, coord_t width, coord_t height) { - _gwm_redim(gh, gh->x, gh->y, width, height); + _gwm_resize(gh, width, height); } void gwinRedraw(GHandle gh) { @@ -284,23 +396,28 @@ void gwinClear(GHandle gh) { * still call the AfterClear() routine as some widgets will * need this to clear internal buffers or similar */ - if (!((gh->flags & GWIN_FLG_VISIBLE))) { + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) { if (gh->vmt->AfterClear) 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_CONTAINERS + for (gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh)) + gwinRedraw(gh); + #endif } void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -310,7 +427,7 @@ void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) { } void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -320,7 +437,7 @@ void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1) { } void gwinDrawBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -330,7 +447,7 @@ void gwinDrawBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { } void gwinFillArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -340,7 +457,7 @@ void gwinFillArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { } void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -351,7 +468,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor #if GDISP_NEED_CIRCLE void gwinDrawCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -361,7 +478,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinFillCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -373,7 +490,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor #if GDISP_NEED_ELLIPSE void gwinDrawEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -383,7 +500,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinFillEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -395,7 +512,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor #if GDISP_NEED_ARC void gwinDrawArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -405,7 +522,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -417,7 +534,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor #if GDISP_NEED_PIXELREAD color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return defaultBgColor; #if GDISP_NEED_CLIP @@ -429,7 +546,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor #if GDISP_NEED_TEXT void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c) { - if (!((gh->flags & GWIN_FLG_VISIBLE)) || !gh->font) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE) || !gh->font) return; #if GDISP_NEED_CLIP @@ -439,7 +556,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c) { - if (!((gh->flags & GWIN_FLG_VISIBLE)) || !gh->font) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE) || !gh->font) return; #if GDISP_NEED_CLIP @@ -449,7 +566,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinDrawString(GHandle gh, coord_t x, coord_t y, const char *str) { - if (!((gh->flags & GWIN_FLG_VISIBLE)) || !gh->font) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE) || !gh->font) return; #if GDISP_NEED_CLIP @@ -459,7 +576,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str) { - if (!((gh->flags & GWIN_FLG_VISIBLE)) || !gh->font) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE) || !gh->font) return; #if GDISP_NEED_CLIP @@ -469,7 +586,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinDrawStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { - if (!((gh->flags & GWIN_FLG_VISIBLE)) || !gh->font) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE) || !gh->font) return; #if GDISP_NEED_CLIP @@ -479,7 +596,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { - if (!((gh->flags & GWIN_FLG_VISIBLE)) || !gh->font) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE) || !gh->font) return; #if GDISP_NEED_CLIP @@ -491,7 +608,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor #if GDISP_NEED_CONVEX_POLYGON void gwinDrawPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -501,7 +618,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor } void gwinFillConvexPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return; #if GDISP_NEED_CLIP @@ -513,7 +630,7 @@ void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coor #if GDISP_NEED_IMAGE gdispImageError gwinDrawImage(GHandle gh, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - if (!((gh->flags & GWIN_FLG_VISIBLE))) + if (!(gh->flags & GWIN_FLG_SYSVISIBLE)) return GDISP_IMAGE_ERR_OK; #if GDISP_NEED_CLIP diff --git a/src/gwin/gwm.c b/src/gwin/gwm.c index 3140b03f..7611bc9d 100644 --- a/src/gwin/gwm.c +++ b/src/gwin/gwm.c @@ -28,7 +28,8 @@ static void WM_DeInit(void); static bool_t WM_Add(GHandle gh, const GWindowInit *pInit); static void WM_Delete(GHandle gh); static void WM_Redraw(GHandle gh, int flags); -static void WM_Redim(GHandle gh, coord_t x, coord_t y, coord_t w, coord_t h); +static void WM_Size(GHandle gh, coord_t w, coord_t h); +static void WM_Move(GHandle gh, coord_t x, coord_t y); static void WM_Raise(GHandle gh); static void WM_MinMax(GHandle gh, GWindowMinMax minmax); @@ -38,7 +39,8 @@ static const gwmVMT GNullWindowManagerVMT = { WM_Add, WM_Delete, WM_Redraw, - WM_Redim, + WM_Size, + WM_Move, WM_Raise, WM_MinMax, }; @@ -47,7 +49,7 @@ static const GWindowManager GNullWindowManager = { &GNullWindowManagerVMT, }; -gfxQueueASync _GWINList; +static gfxQueueASync _GWINList; GWindowManager * _GWINwm; #if GFX_USE_GTIMER static GTimer RedrawTimer; @@ -114,11 +116,9 @@ void gwinRedrawDisplay(GDisplay *g, bool_t preserve) { (void) param; #endif - const gfxQueueASyncItem * qi; GHandle gh; - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { - gh = QItem2GWindow(qi); + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if (!g || gh->display == g) _GWINwm->vmt->Redraw(gh, preserve ? (GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR|GWIN_WMFLG_NOZORDER) @@ -150,8 +150,11 @@ static bool_t WM_Add(GHandle gh, const GWindowInit *pInit) { // Put it on the end of the queue gfxQueueASyncPut(&_GWINList, &gh->wmq); - // Make sure the size is valid - WM_Redim(gh, pInit->x, pInit->y, pInit->width, pInit->height); + // Make sure the size/position is valid - prefer position over size. + gh->width = MIN_WIN_WIDTH; gh->height = MIN_WIN_HEIGHT; + gh->x = gh->y = 0; + WM_Move(gh, pInit->x, pInit->y); + WM_Size(gh, pInit->width, pInit->height); return TRUE; } @@ -161,15 +164,20 @@ static void WM_Delete(GHandle gh) { } static void WM_Redraw(GHandle gh, int flags) { - if ((gh->flags & GWIN_FLG_VISIBLE)) { + #if GWIN_NEED_CONTAINERS + redo_redraw: + #endif + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { if (gh->vmt->Redraw) { #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + if (!(flags & GWIN_WMFLG_KEEPCLIP)) + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif gh->vmt->Redraw(gh); } else if (!(flags & GWIN_WMFLG_PRESERVE)) { #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + if (!(flags & GWIN_WMFLG_KEEPCLIP)) + 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) @@ -184,42 +192,95 @@ static void WM_Redraw(GHandle gh, int flags) { } else if (!(flags & GWIN_WMFLG_NOBGCLEAR)) { #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + if (!(flags & GWIN_WMFLG_KEEPCLIP)) + gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); + #endif + #if GWIN_NEED_CONTAINERS + if (gh->parent) { + // Get the parent to redraw the area + gh = gh->parent; + flags |= GWIN_WMFLG_KEEPCLIP; + goto redo_redraw; + } #endif gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gwinGetDefaultBgColor()); } } -static void WM_Redim(GHandle gh, coord_t x, coord_t y, coord_t w, coord_t h) { - // This is the simplest way of doing it - just clip the the screen - // If it won't fit on the screen move it around until it does. - if (x < 0) { w += x; x = 0; } - if (y < 0) { h += y; y = 0; } - if (x > gdispGGetWidth(gh->display)-MIN_WIN_WIDTH) x = gdispGGetWidth(gh->display)-MIN_WIN_WIDTH; - if (y > gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT) y = gdispGGetHeight(gh->display)-MIN_WIN_HEIGHT; - if (w < MIN_WIN_WIDTH) { w = MIN_WIN_WIDTH; } - if (h < MIN_WIN_HEIGHT) { h = MIN_WIN_HEIGHT; } - if (x+w > gdispGGetWidth(gh->display)) w = gdispGGetWidth(gh->display) - x; - if (y+h > gdispGGetHeight(gh->display)) h = gdispGGetHeight(gh->display) - y; +static void WM_Size(GHandle gh, coord_t w, coord_t h) { + // Give it a minimum size + if (w < MIN_WIN_WIDTH) w = MIN_WIN_WIDTH; + if (h < MIN_WIN_HEIGHT) h = MIN_WIN_HEIGHT; + + #if GWIN_NEED_CONTAINERS + if (gh->parent) { + // Clip to the container + if (gh->x+w > gh->parent->x+gh->parent->width) w = gh->parent->x + gh->parent->width - gh->x; + if (gh->y+h > gh->parent->y+gh->parent->height) h = gh->parent->y + gh->parent->height - gh->y; + if (((const gcontainerVMT *)gh->parent->vmt)->AdjustSize) + ((const gcontainerVMT *)gh->parent->vmt)->AdjustSize(gh, &w, &h); + } + #endif + + // Clip to the screen + if (gh->x+w > gdispGGetWidth(gh->display)) w = gdispGGetWidth(gh->display) - gh->x; + if (gh->y+h > gdispGGetHeight(gh->display)) h = gdispGGetHeight(gh->display) - gh->y; // If there has been no resize just exit - if (gh->x == x && gh->y == y && gh->width == w && gh->height == h) + if (gh->width == w && gh->height == h) return; - // Clear the old area - if ((gh->flags & GWIN_FLG_VISIBLE)) { - #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, gwinGetDefaultBgColor()); + // Clear the old area and then redraw + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { + gh->flags &= ~GWIN_FLG_SYSVISIBLE; + WM_Redraw(gh, 0); + gh->width = w; gh->height = h; + gh->flags |= GWIN_FLG_SYSVISIBLE; + WM_Redraw(gh, 0); + } else { + gh->width = w; gh->height = h; } +} + +static void WM_Move(GHandle gh, coord_t x, coord_t y) { + #if GWIN_NEED_CONTAINERS + if (gh->parent) { + // Clip to the parent + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x > gh->parent->width-gh->width) x = gh->parent->width-gh->width; + if (y > gh->parent->height-gh->height) y = gh->parent->height-gh->height; + + // Allow the parent to adjust it + if (((const gcontainerVMT *)gh->parent->vmt)->AdjustPosition) + ((const gcontainerVMT *)gh->parent->vmt)->AdjustPosition(gh, &x, &y); + + // Convert to absolute position + x += gh->parent->x; + y += gh->parent->y; + } + #endif - // Set the new size - gh->x = x; gh->y = y; - gh->width = w; gh->height = h; + // Clip to the screen + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x > gdispGGetWidth(gh->display)-gh->width) x = gdispGGetWidth(gh->display)-gh->width; + if (y > gdispGGetHeight(gh->display)-gh->height) y = gdispGGetHeight(gh->display)-gh->height; - // Redraw the window (if possible) - WM_Redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); + // If there has been no move just exit + if (gh->x == x && gh->y == y) + return; + + // Clear the old area and then redraw + if ((gh->flags & GWIN_FLG_SYSVISIBLE)) { + gh->flags &= ~GWIN_FLG_SYSVISIBLE; + WM_Redraw(gh, 0); + gh->x = x; gh->y = y; + gh->flags |= GWIN_FLG_SYSVISIBLE; + WM_Redraw(gh, 0); + } else { + gh->x = x; gh->y = y; + } } static void WM_MinMax(GHandle gh, GWindowMinMax minmax) { @@ -230,6 +291,7 @@ static void WM_MinMax(GHandle gh, GWindowMinMax minmax) { static void WM_Raise(GHandle gh) { // Take it off the list and then put it back on top // The order of the list then reflects the z-order. + gfxQueueASyncRemove(&_GWINList, &gh->wmq); gfxQueueASyncPut(&_GWINList, &gh->wmq); @@ -237,5 +299,9 @@ static void WM_Raise(GHandle gh) { WM_Redraw(gh, GWIN_WMFLG_PRESERVE|GWIN_WMFLG_NOBGCLEAR); } +GHandle gwinGetNextWindow(GHandle gh) { + return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList); +} + #endif /* GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER */ /** @} */ diff --git a/src/gwin/label.c b/src/gwin/label.c index 2aa0c860..ffb9fa3d 100644 --- a/src/gwin/label.c +++ b/src/gwin/label.c @@ -147,7 +147,7 @@ static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param) { w = (gw->g.flags & GLABEL_FLG_WAUTO) ? getwidth(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.width; h = (gw->g.flags & GLABEL_FLG_HAUTO) ? getheight(gw->text, gw->g.font, gdispGGetWidth(gw->g.display) - gw->g.x) : gw->g.height; - c = (gw->g.flags & GWIN_FLG_ENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; + c = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; if (gw->g.width != w || gw->g.height != h) { gwinResize(&gw->g, w, h); @@ -167,7 +167,7 @@ static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param) { // render the border (if any) if (gw->g.flags & GLABEL_FLG_BORDER) - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, w, h, (gw->g.flags & GWIN_FLG_ENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, w, h, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); } #endif // GFX_USE_GWIN && GFX_NEED_LABEL diff --git a/src/gwin/list.c b/src/gwin/list.c index 2f16e50a..a960e3ed 100644 --- a/src/gwin/list.c +++ b/src/gwin/list.c @@ -107,7 +107,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param); } } - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); sendListEvent(gw, item); } @@ -136,14 +136,14 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param); gw2obj->top -= iheight; if (gw2obj->top < 0) gw2obj->top = 0; - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); } } else if (y >= gw->g.height - 2*ARROW) { if (gw2obj->top < gw2obj->cnt * iheight - pgsz) { gw2obj->top += iheight; if (gw2obj->top > gw2obj->cnt * iheight - pgsz) gw2obj->top = gw2obj->cnt * iheight - pgsz; - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); } } else if (y < gw->g.height/2) { if (gw2obj->top > 0) { @@ -151,7 +151,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param); gw2obj->top -= pgsz; else gw2obj->top = 0; - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); } } else { if (gw2obj->top < gw2obj->cnt * iheight - pgsz) { @@ -159,7 +159,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param); gw2obj->top += pgsz; else gw2obj->top = gw2obj->cnt * iheight - pgsz; - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); } } return; @@ -197,7 +197,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param); gw2obj->top = 0; gw2obj->last_mouse_y = y; if (oldtop != gw2obj->top) - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); } } #endif @@ -218,7 +218,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param); if (qix) { qi2li->flags &=~ GLIST_FLG_SELECTED; qix2li->flags |= GLIST_FLG_SELECTED; - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); } break; } @@ -235,7 +235,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param); if (qix) { qi2li->flags &=~ GLIST_FLG_SELECTED; qix2li->flags |= GLIST_FLG_SELECTED; - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); } break; } @@ -388,7 +388,7 @@ int gwinListAddItem(GHandle gh, const char* item_name, bool_t useAlloc) { // increment the total amount of entries in the list widget gh2obj->cnt++; - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); // return the position in the list (-1 because we start with index 0) return gh2obj->cnt-1; @@ -486,7 +486,7 @@ void gwinListDeleteAll(GHandle gh) { gh->flags &= ~GLIST_FLG_HASIMAGES; gh2obj->cnt = 0; gh2obj->top = 0; - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); } void gwinListItemDelete(GHandle gh, int item) { @@ -507,7 +507,7 @@ void gwinListItemDelete(GHandle gh, int item) { gfxFree((void *)qi); if (gh2obj->top >= item && gh2obj->top) gh2obj->top--; - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); break; } } @@ -620,7 +620,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) { if (!(gw->g.flags & GLIST_FLG_ENABLERENDER)) return; - ps = (gw->g.flags & GWIN_FLG_ENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; + ps = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING; x = 1; @@ -677,7 +677,7 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) { if (qi2li->pimg && gdispImageIsOpen(qi2li->pimg)) { // Calculate which image sy = (qi2li->flags & GLIST_FLG_SELECTED) ? 0 : (iheight-VERTICAL_PADDING); - if (!(gw->g.flags & GWIN_FLG_ENABLED)) + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) sy += 2*(iheight-VERTICAL_PADDING); while (sy > qi2li->pimg->height) sy -= iheight-VERTICAL_PADDING; diff --git a/src/gwin/progressbar.c b/src/gwin/progressbar.c index 524a5346..ca267f0c 100644 --- a/src/gwin/progressbar.c +++ b/src/gwin/progressbar.c @@ -128,7 +128,7 @@ void gwinProgressbarSetPosition(GHandle gh, int pos) { } ResetDisplayPos(gsw); - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); #undef gsw } @@ -159,7 +159,7 @@ void gwinProgressbarIncrement(GHandle gh) { gsw->pos = gsw->max; ResetDisplayPos(gsw); - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); #undef gsw } @@ -178,7 +178,7 @@ void gwinProgressbarDecrement(GHandle gh) { gsw->pos -= gsw->res; ResetDisplayPos(gsw); - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); #undef gsw } @@ -239,7 +239,7 @@ void gwinProgressbarDraw_Std(GWidgetObject *gw, void *param) { return; // get the colors right - if ((gw->g.flags & GWIN_FLG_ENABLED)) + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) pcol = &gw->pstyle->pressed; else pcol = &gw->pstyle->disabled; @@ -277,7 +277,7 @@ void gwinProgressbarDraw_Image(GWidgetObject *gw, void *param) { if (gw->g.vmt != (gwinVMT *)&progressbarVMT) return; - if ((gw->g.flags & GWIN_FLG_ENABLED)) + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) pcol = &gw->pstyle->pressed; else pcol = &gw->pstyle->disabled; diff --git a/src/gwin/radio.c b/src/gwin/radio.c index 0ae5546a..da6aa5ea 100644 --- a/src/gwin/radio.c +++ b/src/gwin/radio.c @@ -133,10 +133,10 @@ void gwinRadioPress(GHandle gh) { if ((gx = gwinRadioGetActive(((GRadioObject *)gh)->group))) { gx->flags &= ~GRADIO_FLG_PRESSED; - _gwidgetRedraw(gx); + _gwidgetUpdate(gx); } gh->flags |= GRADIO_FLG_PRESSED; - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); SendRadioEvent((GWidgetObject *)gh); } @@ -148,11 +148,9 @@ bool_t gwinRadioIsPressed(GHandle gh) { } GHandle gwinRadioGetActive(uint16_t group) { - const gfxQueueASyncItem * qi; - GHandle gh; + GHandle gh; - for(qi = gfxQueueASyncPeek(&_GWINList); qi; qi = gfxQueueASyncNext(qi)) { - gh = QItem2GWindow(qi); + for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if (gh->vmt == (gwinVMT *)&radioVMT && ((GRadioObject *)gh)->group == group && (gh->flags & GRADIO_FLG_PRESSED)) return gh; } @@ -164,7 +162,7 @@ GHandle gwinRadioGetActive(uint16_t group) { *----------------------------------------------------------*/ static const GColorSet *getDrawColors(GWidgetObject *gw) { - if (!(gw->g.flags & GWIN_FLG_ENABLED)) return &gw->pstyle->disabled; + if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled; if ((gw->g.flags & GRADIO_FLG_PRESSED)) return &gw->pstyle->pressed; return &gw->pstyle->enabled; } diff --git a/src/gwin/slider.c b/src/gwin/slider.c index 4c91ede6..3266e20a 100644 --- a/src/gwin/slider.c +++ b/src/gwin/slider.c @@ -74,7 +74,7 @@ static void ResetDisplayPos(GSliderObject *gsw) { if (x < 0 || x >= gh->width || y < 0 || y >= gh->height) { // No - restore the slider ResetDisplayPos(gsw); - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); return; } #endif @@ -97,7 +97,7 @@ static void ResetDisplayPos(GSliderObject *gsw) { } ResetDisplayPos(gsw); - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); // Generate the event SendSliderEvent(gw); @@ -127,7 +127,7 @@ static void ResetDisplayPos(GSliderObject *gsw) { } // Update the display - _gwidgetRedraw(&gw->g); + _gwidgetUpdate(&gw->g); #undef gsw } #endif @@ -169,7 +169,7 @@ static void ResetDisplayPos(GSliderObject *gsw) { gsw->pos = (uint16_t)((uint32_t)value*(gsw->max-gsw->min)/max + gsw->min); ResetDisplayPos(gsw); - _gwidgetRedraw((GHandle)gw); + _gwidgetUpdate((GHandle)gw); // Generate the event SendSliderEvent(gw); @@ -272,7 +272,7 @@ void gwinSliderSetPosition(GHandle gh, int pos) { else gsw->pos = pos; } ResetDisplayPos(gsw); - _gwidgetRedraw(gh); + _gwidgetUpdate(gh); #undef gsw } @@ -289,7 +289,7 @@ void gwinSliderDraw_Std(GWidgetObject *gw, void *param) { if (gw->g.vmt != (gwinVMT *)&sliderVMT) return; - if ((gw->g.flags & GWIN_FLG_ENABLED)) + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) pcol = &gw->pstyle->pressed; else pcol = &gw->pstyle->disabled; @@ -334,7 +334,7 @@ void gwinSliderDraw_Image(GWidgetObject *gw, void *param) { if (gw->g.vmt != (gwinVMT *)&sliderVMT) return; - if ((gw->g.flags & GWIN_FLG_ENABLED)) + if ((gw->g.flags & GWIN_FLG_SYSENABLED)) pcol = &gw->pstyle->pressed; else pcol = &gw->pstyle->disabled; diff --git a/src/gwin/sys_defs.h b/src/gwin/sys_defs.h index c632b069..91831cba 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. @@ -46,6 +49,9 @@ typedef struct GWindowObject { #if GDISP_NEED_TEXT font_t font; // @< The current font #endif + #if GWIN_NEED_CONTAINERS + GHandle parent; // @< The parent window + #endif } GWindowObject, * GHandle; /* @} */ @@ -62,9 +68,12 @@ typedef struct GWindowObject { * @{ */ typedef struct GWindowInit { - coord_t x, y; // @< The initial screen position + coord_t x, y; // @< The initial position relative to its parent coord_t width, height; // @< The initial dimension bool_t show; // @< Should the window be visible initially + #if GWIN_NEED_CONTAINERS + GHandle parent; // @< The parent - must be a container or NULL + #endif } GWindowInit; /* @} */ @@ -324,6 +333,9 @@ extern "C" { * as not visible, nothing is done to remove the window from the screen. * When there is a window manager, it is up to the window manager to * handle what happens. + * @note Even when you mark a window as visible, it may still not be displayed + * if it's parent is invisible. When the parent becomes visible this child + * will automatically be shown because it is already marked as visible. * * @api */ @@ -333,6 +345,9 @@ extern "C" { * @brief Gets the visibility of a window * @return TRUE if visible * + * @note It is possible for a child to be marked as visible by @p gwinSetVisible() + * but for this call to return FALSE if one of its parents are not visible. + * * @param[in] gh The window * * @api @@ -345,8 +360,10 @@ extern "C" { * @param[in] gh The window handle * @param[in] enabled Enable or disable the window * - * @note The window is automatically redrawn if it - * supports self-redrawing. + * @note The window is automatically redrawn if it supports self-redrawing. + * @note Even when you mark a window as enabled, it may still remain disabled + * if it's parent is disabled. When the parent becomes enabled this child + * will automatically be enabled because it is already marked as enabled. * * @api */ @@ -374,6 +391,9 @@ extern "C" { * @brief Gets the enabled state of a window * @return TRUE if enabled * + * @note It is possible for a child to be marked as enabled by @p gwinSetEnabled() + * but for this call to return FALSE if one of its parents are not enabled. + * * @param[in] gh The window * * @api @@ -491,6 +511,24 @@ extern "C" { * @api */ void gwinRaise(GHandle gh); + + /** + * @brief Get the next window in the z-order + * @return The next window or NULL if no more windows + * + * @param[in] gh The previous window or NULL to get the first window + * + * @note This returns the next window in the system from top to bottom. + * @note Where there are parent child relationships, this ignores them + * and will list all windows in the system. There is no defined + * order between children of siblings and they can in fact be mixed + * in order. The only relationship honored is that parents will be + * listed before their children. + * + * @api + */ + GHandle gwinGetNextWindow(GHandle gh); + #endif #if GDISP_NEED_TEXT || defined(__DOXYGEN__) @@ -880,7 +918,12 @@ extern "C" { #include "src/gwin/gwidget.h" #endif - /* Include extra window types */ + /* Include containers */ + #if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) + #include "src/gwin/gcontainer.h" + #endif + + /* Include vanilla window objects */ #if GWIN_NEED_CONSOLE || defined(__DOXYGEN__) #include "src/gwin/console.h" #endif diff --git a/src/gwin/sys_make.mk b/src/gwin/sys_make.mk index 4c670ea2..95a2ba84 100644 --- a/src/gwin/sys_make.mk +++ b/src/gwin/sys_make.mk @@ -11,4 +11,6 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin.c \ $(GFXLIB)/src/gwin/radio.c \ $(GFXLIB)/src/gwin/list.c \ $(GFXLIB)/src/gwin/progressbar.c \ + $(GFXLIB)/src/gwin/gcontainer.c \ + $(GFXLIB)/src/gwin/frame.c \ diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h index 41bc551f..39630de0 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_CONTAINERS + #define GWIN_NEED_CONTAINERS FALSE + #endif + /** * @brief Should widget functions be included. Needed for any widget (eg Buttons, Sliders etc) * @details Defaults to FALSE */ @@ -37,6 +44,20 @@ #define GWIN_NEED_WIDGET FALSE #endif /** + * @brief Should the simple container be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_CONTAINER + #define GWIN_NEED_CONTAINER 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 f9f00a48..ddfea3aa 100644 --- a/src/gwin/sys_rules.h +++ b/src/gwin/sys_rules.h @@ -28,6 +28,15 @@ #endif // Objects require their super-class + #if GWIN_NEED_FRAME || GWIN_NEED_CONTAINER + #if !GWIN_NEED_CONTAINERS + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GWIN_NEED_CONTAINERS is required when a container is enabled. It has been turned on for you." + #endif + #undef GWIN_NEED_CONTAINERS + #define GWIN_NEED_CONTAINERS 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 @@ -40,6 +49,15 @@ #endif // Rules for the super-classes + #if GWIN_NEED_CONTAINERS + #if !GWIN_NEED_WIDGET + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GWIN_NEED_WIDGET is required when GWIN_NEED_CONTAINERS is enabled. It has been turned on for you." + #endif + #undef GWIN_NEED_WIDGET + #define GWIN_NEED_WIDGET TRUE + #endif + #endif #if GWIN_NEED_WIDGET #if !GDISP_NEED_TEXT #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_WIDGET is TRUE." |