diff options
-rw-r--r-- | demos/modules/gwin/textedit/gfxconf.h | 8 | ||||
-rw-r--r-- | demos/modules/gwin/widgets/main.c | 7 | ||||
-rw-r--r-- | src/gwin/gwin.h | 25 | ||||
-rw-r--r-- | src/gwin/gwin_class.h | 51 | ||||
-rw-r--r-- | src/gwin/gwin_textedit.c | 60 | ||||
-rw-r--r-- | src/gwin/gwin_textedit.h | 25 | ||||
-rw-r--r-- | src/gwin/gwin_widget.c | 182 | ||||
-rw-r--r-- | src/gwin/gwin_widget.h | 30 | ||||
-rw-r--r-- | src/gwin/gwin_wm.c | 57 |
9 files changed, 261 insertions, 184 deletions
diff --git a/demos/modules/gwin/textedit/gfxconf.h b/demos/modules/gwin/textedit/gfxconf.h index 4e518774..3c5d7b96 100644 --- a/demos/modules/gwin/textedit/gfxconf.h +++ b/demos/modules/gwin/textedit/gfxconf.h @@ -48,13 +48,15 @@ #define GDISP_NEED_CLIP TRUE #define GDISP_NEED_TEXT TRUE #define GDISP_NEED_TEXT_KERNING TRUE -#define GDISP_NEED_STARTUP_LOGO TRUE +#define GDISP_NEED_MULTITHREAD TRUE /* GDISP fonts to include */ #define GDISP_INCLUDE_FONT_UI2 TRUE #define GDISP_INCLUDE_FONT_DEJAVUSANS16 TRUE /* Features for the GWIN subsystem. */ +#define GWIN_NEED_WINDOWMANAGER TRUE +#define GWIN_NEED_WIDGET TRUE #define GWIN_NEED_CONSOLE TRUE #define GWIN_NEED_TEXTEDIT TRUE #define GWIN_NEED_BUTTON TRUE @@ -63,5 +65,9 @@ #define GINPUT_NEED_MOUSE TRUE #define GINPUT_NEED_KEYBOARD TRUE +/* Features for the GQUEUE subsystem. */ +#define GFX_USE_GQUEUE TRUE +#define GQUEUE_NEED_ASYNC TRUE + #endif /* _GFXCONF_H */ diff --git a/demos/modules/gwin/widgets/main.c b/demos/modules/gwin/widgets/main.c index d5bfb6d2..8f5a6329 100644 --- a/demos/modules/gwin/widgets/main.c +++ b/demos/modules/gwin/widgets/main.c @@ -49,13 +49,14 @@ /* Our custom yellow style */ static const GWidgetStyle YellowWidgetStyle = { Yellow, // window background + HTML2COLOR(0x800000), // focus // enabled color set { HTML2COLOR(0x0000FF), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xE0E0E0), // progress - inactive area + HTML2COLOR(0xE0E0E0) // progress - inactive area }, // disabled color set @@ -63,7 +64,7 @@ static const GWidgetStyle YellowWidgetStyle = { HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0x808080), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xC0E0C0), // progress - active area + HTML2COLOR(0xC0E0C0) // progress - active area }, // pressed color set @@ -72,7 +73,7 @@ static const GWidgetStyle YellowWidgetStyle = { HTML2COLOR(0x404040), // edge HTML2COLOR(0x808080), // fill HTML2COLOR(0x00E000), // progress - active area - }, + } }; /* The variables we need */ diff --git a/src/gwin/gwin.h b/src/gwin/gwin.h index 4b29f1ee..cc0d44f1 100644 --- a/src/gwin/gwin.h +++ b/src/gwin/gwin.h @@ -553,31 +553,6 @@ extern "C" { GHandle gwinGetNextWindow(GHandle gh); /** - * @brief Set the focus to a specific widget - * - * @details The widget that is currently in focus is the widget that - * receives mouse and keyboard events. - * Passing NULL will remove the focus from any widget. - * - * @param[in] gh The widget handle. Non-widget handles will be ignored. - * - * @api - */ - void gwinSetFocus(GHandle gh); - - /** - * @brief Get the widget that is currently in focus - * - * @details The widget that is currently in focus is the widget that - * receives mouse and keyboard events. - * - * @return The handle of the widget that is currently in focus. May be NULL. - * - * @api - */ - GHandle gwinGetFocus(void); - - /** * @brief Set a window or widget to flash * * @param[in] gh The window handle diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h index 03d2d506..ad6df423 100644 --- a/src/gwin/gwin_class.h +++ b/src/gwin/gwin_class.h @@ -256,9 +256,8 @@ void _gwinDrawEnd(GHandle gh); * @param[in] gh The window * @param[in] how Do we wait for the lock? * - * @note This call will delete the window. If called without the - * drawing lock 'how' must be REDRAW_WAIT. If called with the drawing - * lock 'how' must be REDRAW_INSESSION. + * @note If called without the drawing lock 'how' must be REDRAW_WAIT. + * If called with the drawing lock 'how' must be REDRAW_INSESSION. * * @notapi */ @@ -322,6 +321,50 @@ bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit); */ void _gwinSendEvent(GHandle gh, GEventType type); + #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || defined(__DOXYGEN__) + /** + * @brief Move the focus off the current focus window. + * + * @notapi + */ + void _gwinMoveFocus(void); + + /** + * @brief Do focus fixup's after a change of state for a window. + * @details If a focus window has become invisible or disabled then + * the focus must be taken away from it. If there is no focus + * window and this window is eligible then this window becomes + * the focus. + * + * @param[in] gh The window + * + * @note This routine does not actually do a redraw. It assumes that surrounding code + * will because of the change of state that lead to this being called. + * + * @notapi + */ + void _gwinFixFocus(GHandle gh); + + /** + * @brief Draw a simple focus rectangle in the default style. + * + * @param[in] gw The widget + * @param[in] x, y The start x, y position (relative to the window) + * @param[in] cx, cy The width & height of the rectangle + * + * @note Assumes the widget is in a state where it can draw. + * @note Nothing is drawn if the window doesn't have focus. + * @note The focus rectangle may be more than one pixel thick and may + * not be a continuous line. + * + * @notapi + */ + void _gwidgetDrawFocusRect(GWidgetObject *gw, coord_t x, coord_t y, coord_t cx, coord_t cy); + + #else + #define _gwinFixFocus(gh) + #define _gwidgetDrawFocusRect(gh,x,y,cx,cy) + #endif #if GWIN_NEED_FLASHING || defined(__DOXYGEN__) /** @@ -335,6 +378,8 @@ bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit); */ const GColorSet *_gwinGetFlashedColor(GWidgetObject *gw, const GColorSet *pcol, bool_t flashOffState); #endif +#else + #define _gwinFixFocus(gh) #endif #if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 99708a0a..5ea8936a 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -23,9 +23,6 @@ const int FOCUS_BORDER_THICKNESS = 3; const int CURSOR_PADDING_LEFT = 0; const int CURSOR_EXTRA_HEIGHT = 1; -// Some flags -#define GTEXTEDIT_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 0) - // Macros to assist in data type conversions #define gh2obj ((GTexteditObject *)gh) #define gw2obj ((GTexteditObject *)gw) @@ -65,7 +62,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char #if GINPUT_NEED_KEYBOARD - static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) + static void TextEditKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { // Only react on KEYDOWN events. Ignore KEYUP events. if (pke->keystate & GKEYSTATE_KEYUP) { @@ -116,9 +113,8 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char // Add a new character else { // Prevent buffer overflow - if (gw2obj->cursorPos >= gw2obj->bufferSize) { + if (gw2obj->cursorPos >= gw2obj->maxSize) return; - } // Shift everything right from the cursor by one character. This includes the '\0'. Then inser the new character. _shiftTextRight(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos++, pke->c[0]); @@ -152,7 +148,7 @@ static const gwidgetVMT texteditVMT = { #endif #if GINPUT_NEED_KEYBOARD { - _keyboardEvent // Process keyboard key down events + TextEditKeyboard // Process keyboard key down events }, #endif #if GINPUT_NEED_TOGGLE @@ -174,50 +170,33 @@ static const gwidgetVMT texteditVMT = { #endif }; -GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize) +GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize) { uint16_t flags = 0; // Create the underlying widget - if (!(widget = (GTexteditObject*)_gwidgetCreate(g, &widget->w, pInit, &texteditVMT))) { + if (!(wt = (GTexteditObject*)_gwidgetCreate(g, &wt->w, pInit, &texteditVMT))) return 0; - } // Allocate the text buffer - widget->bufferSize = bufSize; - widget->textBuffer = gfxAlloc(widget->bufferSize); - if (widget->textBuffer == 0) { + wt->maxSize = maxSize; + wt->textBuffer = gfxAlloc(widget->maxSize); + if (wt->textBuffer == 0) return 0; - } // Initialize the text buffer size_t i = 0; for (i = 0; i < bufSize; i++) { - widget->textBuffer[i] = '\0'; + wt->textBuffer[i] = '\0'; } // Set text and cursor position - strncpy(widget->textBuffer, gwinGetText((GHandle)widget), widget->bufferSize); // FixMe: pInit->text leads to a segfault - widget->cursorPos = strlen(widget->textBuffer); + strncpy(wt->textBuffer, gwinGetText((GHandle)wt), wt->maxSize); // FixMe: pInit->text leads to a segfault + wt->cursorPos = strlen(wt->textBuffer); - widget->w.g.flags |= flags | GTEXTEDIT_FLG_BORDER;; - gwinSetVisible(&widget->w.g, pInit->g.show); - - return (GHandle)widget; -} - -void gwinTexteditSetBorder(GHandle gh, bool_t border) -{ - // Is it a valid handle? - if (gh->vmt != (gwinVMT*)&texteditVMT) { - return; - } + gwinSetVisible(&wt->w.g, pInit->g.show); - if (border) { - gh2obj->w.g.flags |= GTEXTEDIT_FLG_BORDER; - } else { - gh2obj->w.g.flags &=~ GTEXTEDIT_FLG_BORDER; - } + return (GHandle)wt; } static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) @@ -238,18 +217,11 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); gdispGFillStringBox(gw->g.display, gw->g.x + TEXT_PADDING_LEFT, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font, textColor, gw->pstyle->background, justifyLeft); - // Render border (if supposed to) - if (gw2obj->w.g.flags & GTEXTEDIT_FLG_BORDER) { - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); - } + // Render border (always) + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); // Render highlighted border of focused - if (gwinGetFocus() == (GHandle)gw) { - int i = 0; - for (i = 0; i < FOCUS_BORDER_THICKNESS; i++) { - gdispGDrawBox(gw->g.display, gw->g.x+i, gw->g.y+i, gw->g.width-2*i, gw->g.height-2*i, gw->pstyle->focus); - } - } + _gwidgetDrawFocusRect(gw, 0, 0, gw->g.width-1, gw->g.height-1); // Render cursor (if focused) if (gwinGetFocus() == (GHandle)gw) { diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h index 5b0cea85..f83233b4 100644 --- a/src/gwin/gwin_textedit.h +++ b/src/gwin/gwin_textedit.h @@ -33,7 +33,7 @@ typedef struct GTexteditObject { GWidgetObject w; char* textBuffer; - size_t bufferSize; + size_t maxSize; uint16_t cursorPos; } GTexteditObject; @@ -48,28 +48,17 @@ extern "C" { * is abstracted through the GINPUT module. * * @param[in] g The GDisplay on which the textedit should be displayed - * @param[in] widget The TextEdit structure to initialise. If this is NULL, the structure is dynamically allocated. + * @param[in] wt The TextEdit structure to initialise. If this is NULL, the structure is dynamically allocated. * @param[in] pInit The initialisation parameters to use. - * @param[in] bufSize The maximum number of characters the TextEdit widget can hold. + * @param[in] maxSize The maximum number of characters the TextEdit widget can hold. * - * @return NULL if there is no resultat drawing area, otherwise the widget handle. + * @return NULL if there is no resultant drawing area, otherwise the widget handle. * + * @note If the initial text set is larger than maxSize then the text is truncated at maxSize characters. * @api */ -GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize); -#define gwinTexteditCreate(w, pInit, bufSize) gwinGTexteditCreate(GDISP, w, pInit, bufSize) - -/** - * @brief Border settings for the default rendering routine - * - * @note Border is enabled by default. - * - * @param[in] gh The widget handle (must be a TextEdit handle) - * @param[in] border Shall a border be rendered? - * - * @api - */ -void gwinTexteditSetBorder(GHandle gh, bool_t border); +GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize); +#define gwinTexteditCreate(wt, pInit, maxSize) gwinGTexteditCreate(GDISP, wt, pInit, maxSize) #ifdef __cplusplus } diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 47e60e82..6ef148a6 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -21,6 +21,11 @@ // Our listener for events for widgets static GListener gl; +#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD + // Our current focus window + static GHandle _widgetInFocus; +#endif + // Our default style - a white background theme const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xFFFFFF), // window background @@ -139,6 +144,14 @@ static void gwidgetEvent(void *param, GEvent *pe) { if (gh && (gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) == (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) { if ((pme->buttons & GMETA_MOUSE_DOWN)) { gh->flags |= GWIN_FLG_MOUSECAPTURE; + + #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD + // We should try and capture the focus on this window. + // If we can't no window should have the focus + if (!gwinSetFocus(gh)) + gwinSetFocus(0); + #endif + if (wvmt->MouseDown) wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); } @@ -150,79 +163,14 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_KEYBOARD: // If Tab key pressed then set focus to next widget if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { - - // Only react on KEYDOWN events. Ignore KEYUP events. - if (pke->keystate & GKEYSTATE_KEYUP) - break; - - // Get the next widget - bool_t foundWidget = FALSE; - bool_t endOfListDetected = FALSE; - GHandle nextWidget = gwinGetFocus(); - GHandle prevWidget = gwinGetFocus(); - do { - nextWidget = gwinGetNextWindow(nextWidget); - foundWidget = TRUE; - - // Begin with the first one if this is the last one - if (nextWidget == 0) { - foundWidget = FALSE; - // We go through the list twice - just to be sure - if (endOfListDetected) - break; - endOfListDetected = TRUE; - continue; - } - - // Check whether this is a window or a widget - if (!gwinIsWidget(nextWidget)) { - foundWidget = FALSE; - continue; - } - - // Only focus on a widget that is visible and enabled - if (!(nextWidget->flags & GWIN_FLG_SYSVISIBLE) || !(nextWidget->flags & GWIN_FLG_ENABLED)) { - foundWidget = FALSE; - continue; - } - - // When using the TAB key we only focus on widgets that process keyboard events - if (((gwidgetVMT*)nextWidget->vmt)->KeyboardEvent == 0) { - foundWidget = FALSE; - continue; - } - - } while (foundWidget == FALSE); - gwinSetFocus(nextWidget); - - // Redraw the new and the previous focused widget because they usually render differently when - // they are not focused anymore (eg. no blinking cursor) - if (prevWidget != 0) - gwinRedraw(prevWidget); - if (nextWidget != 0) - gwinRedraw(nextWidget); - + if (!(pke->keystate & GKEYSTATE_KEYUP)) + _gwinMoveFocus(); break; } - // Otherise, send keyboard events only to widget in focus - GHandle widgetInFocus = gwinGetFocus(); - if (widgetInFocus != 0) { - // Make sure that it is a widget - if (!gwinIsWidget(widgetInFocus)) - break; - - // Make sure that the widget is enabled and visible - if (!(widgetInFocus->flags & GWIN_FLG_SYSVISIBLE) || !(widgetInFocus->flags & GWIN_FLG_ENABLED)) - break; - - // Check whether this widget provides a method for handling keyboard events - if (((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent == 0) - break; - - // If we got this far we can finally pass the event - ((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)widgetInFocus, pke); - } + // Otherwise, send keyboard events only to widget in focus + if (_widgetInFocus) + ((gwidgetVMT*)_widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)_widgetInFocus, pke); break; #endif @@ -278,6 +226,96 @@ static void gwidgetEvent(void *param, GEvent *pe) { #undef pde } +#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD + GHandle gwinGetFocus(void) { + return _widgetInFocus; + } + + bool_t gwinSetFocus(GHandle gh) { + GHandle oldFocus; + + // Do we already have the focus? + if (gh == _widgetInFocus) + return TRUE; + + // The new window must be NULLL or a visible enabled widget with a keyboard handler + if (!gh || ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) + && ((gwidgetVMT*)gh->vmt)->KeyboardEvent)) { + // Move the current focus + oldFocus = _widgetInFocus; + _widgetInFocus = gh; + if (oldFocus) _gwinUpdate(oldFocus); + if (gh) _gwinUpdate(gh); + return TRUE; + } + return FALSE; + } + + void _gwinMoveFocus(void) { + GHandle gh; + + // Find a new focus window (one may or may not exist). + for(gh = gwinGetNextWindow(_widgetInFocus); gh && gh != _widgetInFocus; gh = gwinGetNextWindow(gh)) { + if (gwinSetFocus(gh)) + return; + } + gwinSetFocus(0); + } + + void _gwinFixFocus(GHandle gh) { + GHandle oldFocus; + + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) + && ((gwidgetVMT*)gh->vmt)->KeyboardEvent) { + + // We are a candidate to be able to claim the focus + + // Claim the focus if no-one else has + if (!_widgetInFocus) + _widgetInFocus = gh; + + return; + } + + // We have lost any right to the focus + + // Did we have the focus + if (gh != _widgetInFocus) + return; + + // We did - we need to find a new focus window + oldFocus = _widgetInFocus; + for(gh = gwinGetNextWindow(oldFocus); gh && gh != oldFocus; gh = gwinGetNextWindow(gh)) { + + // Must be a visible enabled widget with a keyboard handler + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) + && ((gwidgetVMT*)gh->vmt)->KeyboardEvent) { + + // Grab the focus for the new window + _widgetInFocus = gh; + + // This new window still needs to be marked for redraw (but don't actually do it yet). + gh->flags |= GWIN_FLG_NEEDREDRAW; + RedrawPending |= DOREDRAW_VISIBLES; + return; + } + } + + // No-one has the right to the focus + _widgetInFocus = 0; + } + + void _gwidgetDrawFocusRect(GWidgetObject *gw, coord_t x, coord_t y, coord_t cx, coord_t cy) { + // Don't do anything if we don't have the focus + if (&gw->g != _widgetInFocus) + return; + + // Use the very simplest possible focus rectangle for now. + gdispGDrawBox(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, gw->pstyle.focus); + } + +#endif + #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE static GHandle FindToggleUser(uint16_t instance) { GHandle gh; @@ -355,6 +393,10 @@ void _gwidgetDestroy(GHandle gh) { uint16_t role, instance; #endif + // Make the window is invisible so it is not eligible for focus + gh->flags &= ~GWIN_FLG_VISIBLE; + _gwinFixFocus(gh); + // Deallocate the text (if necessary) if ((gh->flags & GWIN_FLG_ALLOCTXT)) { gh->flags &= ~GWIN_FLG_ALLOCTXT; diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index 3aa1b9f1..5239b6b8 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -356,6 +356,36 @@ bool_t gwinAttachListener(GListener *pl); bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance); #endif +#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || defined(__DOXYGEN__) + /** + * @brief Set the keyboard focus to a specific window + * @return Returns TRUE if the focus could be set to that window + * + * @param[in] gh The window + * + * @note Passing NULL will remove the focus from any window. + * @note Only visible enabled widgets are capable of getting the focus. + * + * @api + */ + bool_t gwinSetFocus(GHandle gh); + + /** + * @brief Get the widget that is currently in focus + * + * @details The widget that is currently in focus is the widget that + * receives mouse and keyboard events. + * + * @return The handle of the widget that is currently in focus. May be NULL. + * + * @api + */ + GHandle gwinGetFocus(void); +#else + #define gwinGetFocus() (0) + #define gwinSetFocus(gh) (FALSE) +#endif + #ifdef __cplusplus } #endif diff --git a/src/gwin/gwin_wm.c b/src/gwin/gwin_wm.c index 52f7a1aa..41fc385d 100644 --- a/src/gwin/gwin_wm.c +++ b/src/gwin/gwin_wm.c @@ -162,7 +162,6 @@ extern const GWindowManager GNullWindowManager; GWindowManager * _GWINwm; bool_t _gwinFlashState; -static GHandle _widgetInFocus; static gfxSem gwinsem; static gfxQueueASync _GWINList; #if GWIN_NEED_FLASHING @@ -333,14 +332,23 @@ void _gwinUpdate(GHandle gh) { if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) { // We have been made visible gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // Do we want to grab the focus + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_VISIBLES; } break; case (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE): if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) break; + // Parent has been made invisible gh->flags &= ~GWIN_FLG_SYSVISIBLE; + + // No focus for us anymore + _gwinFixFocus(gh); + break; case GWIN_FLG_SYSVISIBLE: // We have been made invisible @@ -348,6 +356,10 @@ void _gwinUpdate(GHandle gh) { if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) { // The parent is visible so we must clear the area we took gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // No focus for us anymore + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_INVISIBLES; } break; @@ -457,6 +469,10 @@ void gwinRedraw(GHandle gh) { if (visible) { if (!(gh->flags & GWIN_FLG_VISIBLE)) { gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // Do we want to grab the focus + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_VISIBLES; TriggerRedraw(); } @@ -464,6 +480,10 @@ void gwinRedraw(GHandle gh) { if ((gh->flags & GWIN_FLG_VISIBLE)) { gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // No focus for us anymore + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_INVISIBLES; TriggerRedraw(); } @@ -484,6 +504,10 @@ void gwinRedraw(GHandle gh) { for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if ((gh->flags & (GWIN_FLG_SYSENABLED|GWIN_FLG_ENABLED)) == GWIN_FLG_ENABLED && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { gh->flags |= GWIN_FLG_SYSENABLED; // Fix it + + // Do we want to grab the focus + _gwinFixFocus(gh); + _gwinUpdate(gh); } } @@ -497,6 +521,10 @@ void gwinRedraw(GHandle gh) { for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if ((gh->flags & GWIN_FLG_SYSENABLED) && (!(gh->flags & GWIN_FLG_ENABLED) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSENABLED)))) { gh->flags &= ~GWIN_FLG_SYSENABLED; // Fix it + + // No focus for us anymore + _gwinFixFocus(gh); + _gwinUpdate(gh); } } @@ -508,11 +536,19 @@ void gwinRedraw(GHandle gh) { if (enabled) { if (!(gh->flags & GWIN_FLG_ENABLED)) { gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + + // Do we want to grab the focus + _gwinFixFocus(gh); + _gwinUpdate(gh); } } else { if ((gh->flags & GWIN_FLG_ENABLED)) { gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + + // No focus for us anymore + _gwinFixFocus(gh); + _gwinUpdate(gh); } } @@ -573,25 +609,6 @@ GHandle gwinGetNextWindow(GHandle gh) { return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList); } -void gwinSetFocus(GHandle gh) { - // Passing NULL removes the focus from any widget - if (gh == 0) { - _widgetInFocus = 0; - return; - } - - // Only accept widgets - if (!gwinIsWidget(gh)) { - return; - } - - _widgetInFocus = gh; -} - -GHandle gwinGetFocus(void) { - return _widgetInFocus; -} - #if GWIN_NEED_FLASHING static void FlashTimerFn(void *param) { GHandle gh; |