Updates to focus.

ugfx_release_2.6
inmarket 2015-08-16 21:53:47 +10:00
parent 377fe644d1
commit 15e7342fd7
9 changed files with 261 additions and 184 deletions

View File

@ -48,13 +48,15 @@
#define GDISP_NEED_CLIP TRUE #define GDISP_NEED_CLIP TRUE
#define GDISP_NEED_TEXT TRUE #define GDISP_NEED_TEXT TRUE
#define GDISP_NEED_TEXT_KERNING TRUE #define GDISP_NEED_TEXT_KERNING TRUE
#define GDISP_NEED_STARTUP_LOGO TRUE #define GDISP_NEED_MULTITHREAD TRUE
/* GDISP fonts to include */ /* GDISP fonts to include */
#define GDISP_INCLUDE_FONT_UI2 TRUE #define GDISP_INCLUDE_FONT_UI2 TRUE
#define GDISP_INCLUDE_FONT_DEJAVUSANS16 TRUE #define GDISP_INCLUDE_FONT_DEJAVUSANS16 TRUE
/* Features for the GWIN subsystem. */ /* Features for the GWIN subsystem. */
#define GWIN_NEED_WINDOWMANAGER TRUE
#define GWIN_NEED_WIDGET TRUE
#define GWIN_NEED_CONSOLE TRUE #define GWIN_NEED_CONSOLE TRUE
#define GWIN_NEED_TEXTEDIT TRUE #define GWIN_NEED_TEXTEDIT TRUE
#define GWIN_NEED_BUTTON TRUE #define GWIN_NEED_BUTTON TRUE
@ -63,5 +65,9 @@
#define GINPUT_NEED_MOUSE TRUE #define GINPUT_NEED_MOUSE TRUE
#define GINPUT_NEED_KEYBOARD TRUE #define GINPUT_NEED_KEYBOARD TRUE
/* Features for the GQUEUE subsystem. */
#define GFX_USE_GQUEUE TRUE
#define GQUEUE_NEED_ASYNC TRUE
#endif /* _GFXCONF_H */ #endif /* _GFXCONF_H */

View File

@ -49,13 +49,14 @@
/* Our custom yellow style */ /* Our custom yellow style */
static const GWidgetStyle YellowWidgetStyle = { static const GWidgetStyle YellowWidgetStyle = {
Yellow, // window background Yellow, // window background
HTML2COLOR(0x800000), // focus
// enabled color set // enabled color set
{ {
HTML2COLOR(0x0000FF), // text HTML2COLOR(0x0000FF), // text
HTML2COLOR(0x404040), // edge HTML2COLOR(0x404040), // edge
HTML2COLOR(0xE0E0E0), // fill HTML2COLOR(0xE0E0E0), // fill
HTML2COLOR(0xE0E0E0), // progress - inactive area HTML2COLOR(0xE0E0E0) // progress - inactive area
}, },
// disabled color set // disabled color set
@ -63,7 +64,7 @@ static const GWidgetStyle YellowWidgetStyle = {
HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0xC0C0C0), // text
HTML2COLOR(0x808080), // edge HTML2COLOR(0x808080), // edge
HTML2COLOR(0xE0E0E0), // fill HTML2COLOR(0xE0E0E0), // fill
HTML2COLOR(0xC0E0C0), // progress - active area HTML2COLOR(0xC0E0C0) // progress - active area
}, },
// pressed color set // pressed color set
@ -72,7 +73,7 @@ static const GWidgetStyle YellowWidgetStyle = {
HTML2COLOR(0x404040), // edge HTML2COLOR(0x404040), // edge
HTML2COLOR(0x808080), // fill HTML2COLOR(0x808080), // fill
HTML2COLOR(0x00E000), // progress - active area HTML2COLOR(0x00E000), // progress - active area
}, }
}; };
/* The variables we need */ /* The variables we need */

View File

@ -552,31 +552,6 @@ extern "C" {
*/ */
GHandle gwinGetNextWindow(GHandle gh); 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 * @brief Set a window or widget to flash
* *

View File

@ -256,9 +256,8 @@ void _gwinDrawEnd(GHandle gh);
* @param[in] gh The window * @param[in] gh The window
* @param[in] how Do we wait for the lock? * @param[in] how Do we wait for the lock?
* *
* @note This call will delete the window. If called without the * @note If called without the drawing lock 'how' must be REDRAW_WAIT.
* drawing lock 'how' must be REDRAW_WAIT. If called with the drawing * If called with the drawing lock 'how' must be REDRAW_INSESSION.
* lock 'how' must be REDRAW_INSESSION.
* *
* @notapi * @notapi
*/ */
@ -322,6 +321,50 @@ bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit);
*/ */
void _gwinSendEvent(GHandle gh, GEventType type); 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__) #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); const GColorSet *_gwinGetFlashedColor(GWidgetObject *gw, const GColorSet *pcol, bool_t flashOffState);
#endif #endif
#else
#define _gwinFixFocus(gh)
#endif #endif
#if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) #if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__)

View File

@ -23,9 +23,6 @@ const int FOCUS_BORDER_THICKNESS = 3;
const int CURSOR_PADDING_LEFT = 0; const int CURSOR_PADDING_LEFT = 0;
const int CURSOR_EXTRA_HEIGHT = 1; const int CURSOR_EXTRA_HEIGHT = 1;
// Some flags
#define GTEXTEDIT_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 0)
// Macros to assist in data type conversions // Macros to assist in data type conversions
#define gh2obj ((GTexteditObject *)gh) #define gh2obj ((GTexteditObject *)gh)
#define gw2obj ((GTexteditObject *)gw) #define gw2obj ((GTexteditObject *)gw)
@ -65,7 +62,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char
#if GINPUT_NEED_KEYBOARD #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. // Only react on KEYDOWN events. Ignore KEYUP events.
if (pke->keystate & GKEYSTATE_KEYUP) { 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 // Add a new character
else { else {
// Prevent buffer overflow // Prevent buffer overflow
if (gw2obj->cursorPos >= gw2obj->bufferSize) { if (gw2obj->cursorPos >= gw2obj->maxSize)
return; return;
}
// Shift everything right from the cursor by one character. This includes the '\0'. Then inser the new character. // 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]); _shiftTextRight(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos++, pke->c[0]);
@ -152,7 +148,7 @@ static const gwidgetVMT texteditVMT = {
#endif #endif
#if GINPUT_NEED_KEYBOARD #if GINPUT_NEED_KEYBOARD
{ {
_keyboardEvent // Process keyboard key down events TextEditKeyboard // Process keyboard key down events
}, },
#endif #endif
#if GINPUT_NEED_TOGGLE #if GINPUT_NEED_TOGGLE
@ -174,50 +170,33 @@ static const gwidgetVMT texteditVMT = {
#endif #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; uint16_t flags = 0;
// Create the underlying widget // Create the underlying widget
if (!(widget = (GTexteditObject*)_gwidgetCreate(g, &widget->w, pInit, &texteditVMT))) { if (!(wt = (GTexteditObject*)_gwidgetCreate(g, &wt->w, pInit, &texteditVMT)))
return 0; return 0;
}
// Allocate the text buffer // Allocate the text buffer
widget->bufferSize = bufSize; wt->maxSize = maxSize;
widget->textBuffer = gfxAlloc(widget->bufferSize); wt->textBuffer = gfxAlloc(widget->maxSize);
if (widget->textBuffer == 0) { if (wt->textBuffer == 0)
return 0; return 0;
}
// Initialize the text buffer // Initialize the text buffer
size_t i = 0; size_t i = 0;
for (i = 0; i < bufSize; i++) { for (i = 0; i < bufSize; i++) {
widget->textBuffer[i] = '\0'; wt->textBuffer[i] = '\0';
} }
// Set text and cursor position // Set text and cursor position
strncpy(widget->textBuffer, gwinGetText((GHandle)widget), widget->bufferSize); // FixMe: pInit->text leads to a segfault strncpy(wt->textBuffer, gwinGetText((GHandle)wt), wt->maxSize); // FixMe: pInit->text leads to a segfault
widget->cursorPos = strlen(widget->textBuffer); wt->cursorPos = strlen(wt->textBuffer);
widget->w.g.flags |= flags | GTEXTEDIT_FLG_BORDER;; gwinSetVisible(&wt->w.g, pInit->g.show);
gwinSetVisible(&widget->w.g, pInit->g.show);
return (GHandle)widget; return (GHandle)wt;
}
void gwinTexteditSetBorder(GHandle gh, bool_t border)
{
// Is it a valid handle?
if (gh->vmt != (gwinVMT*)&texteditVMT) {
return;
}
if (border) {
gh2obj->w.g.flags |= GTEXTEDIT_FLG_BORDER;
} else {
gh2obj->w.g.flags &=~ GTEXTEDIT_FLG_BORDER;
}
} }
static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) 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); 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); 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) // Render border (always)
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);
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 // Render highlighted border of focused
if (gwinGetFocus() == (GHandle)gw) { _gwidgetDrawFocusRect(gw, 0, 0, gw->g.width-1, gw->g.height-1);
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);
}
}
// Render cursor (if focused) // Render cursor (if focused)
if (gwinGetFocus() == (GHandle)gw) { if (gwinGetFocus() == (GHandle)gw) {

View File

@ -33,7 +33,7 @@ typedef struct GTexteditObject {
GWidgetObject w; GWidgetObject w;
char* textBuffer; char* textBuffer;
size_t bufferSize; size_t maxSize;
uint16_t cursorPos; uint16_t cursorPos;
} GTexteditObject; } GTexteditObject;
@ -48,28 +48,17 @@ extern "C" {
* is abstracted through the GINPUT module. * is abstracted through the GINPUT module.
* *
* @param[in] g The GDisplay on which the textedit should be displayed * @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] 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 * @api
*/ */
GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize); GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize);
#define gwinTexteditCreate(w, pInit, bufSize) gwinGTexteditCreate(GDISP, w, pInit, bufSize) #define gwinTexteditCreate(wt, pInit, maxSize) gwinGTexteditCreate(GDISP, wt, pInit, maxSize)
/**
* @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);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -21,6 +21,11 @@
// Our listener for events for widgets // Our listener for events for widgets
static GListener gl; 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 // Our default style - a white background theme
const GWidgetStyle WhiteWidgetStyle = { const GWidgetStyle WhiteWidgetStyle = {
HTML2COLOR(0xFFFFFF), // window background 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 (gh && (gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) == (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) {
if ((pme->buttons & GMETA_MOUSE_DOWN)) { if ((pme->buttons & GMETA_MOUSE_DOWN)) {
gh->flags |= GWIN_FLG_MOUSECAPTURE; 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) if (wvmt->MouseDown)
wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); 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: case GEVENT_KEYBOARD:
// If Tab key pressed then set focus to next widget // If Tab key pressed then set focus to next widget
if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) {
if (!(pke->keystate & GKEYSTATE_KEYUP))
// Only react on KEYDOWN events. Ignore KEYUP events. _gwinMoveFocus();
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);
break; break;
} }
// Otherise, send keyboard events only to widget in focus // Otherwise, send keyboard events only to widget in focus
GHandle widgetInFocus = gwinGetFocus(); if (_widgetInFocus)
if (widgetInFocus != 0) { ((gwidgetVMT*)_widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)_widgetInFocus, pke);
// 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);
}
break; break;
#endif #endif
@ -278,6 +226,96 @@ static void gwidgetEvent(void *param, GEvent *pe) {
#undef pde #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 #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
static GHandle FindToggleUser(uint16_t instance) { static GHandle FindToggleUser(uint16_t instance) {
GHandle gh; GHandle gh;
@ -355,6 +393,10 @@ void _gwidgetDestroy(GHandle gh) {
uint16_t role, instance; uint16_t role, instance;
#endif #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) // Deallocate the text (if necessary)
if ((gh->flags & GWIN_FLG_ALLOCTXT)) { if ((gh->flags & GWIN_FLG_ALLOCTXT)) {
gh->flags &= ~GWIN_FLG_ALLOCTXT; gh->flags &= ~GWIN_FLG_ALLOCTXT;

View File

@ -356,6 +356,36 @@ bool_t gwinAttachListener(GListener *pl);
bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance); bool_t gwinAttachDial(GHandle gh, uint16_t role, uint16_t instance);
#endif #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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -162,7 +162,6 @@
extern const GWindowManager GNullWindowManager; extern const GWindowManager GNullWindowManager;
GWindowManager * _GWINwm; GWindowManager * _GWINwm;
bool_t _gwinFlashState; bool_t _gwinFlashState;
static GHandle _widgetInFocus;
static gfxSem gwinsem; static gfxSem gwinsem;
static gfxQueueASync _GWINList; static gfxQueueASync _GWINList;
#if GWIN_NEED_FLASHING #if GWIN_NEED_FLASHING
@ -333,14 +332,23 @@ void _gwinUpdate(GHandle gh) {
if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) { if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) {
// We have been made visible // We have been made visible
gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
// Do we want to grab the focus
_gwinFixFocus(gh);
RedrawPending |= DOREDRAW_VISIBLES; RedrawPending |= DOREDRAW_VISIBLES;
} }
break; break;
case (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE): case (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE):
if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE))
break; break;
// Parent has been made invisible // Parent has been made invisible
gh->flags &= ~GWIN_FLG_SYSVISIBLE; gh->flags &= ~GWIN_FLG_SYSVISIBLE;
// No focus for us anymore
_gwinFixFocus(gh);
break; break;
case GWIN_FLG_SYSVISIBLE: case GWIN_FLG_SYSVISIBLE:
// We have been made invisible // We have been made invisible
@ -348,6 +356,10 @@ void _gwinUpdate(GHandle gh) {
if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) { if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) {
// The parent is visible so we must clear the area we took // The parent is visible so we must clear the area we took
gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
// No focus for us anymore
_gwinFixFocus(gh);
RedrawPending |= DOREDRAW_INVISIBLES; RedrawPending |= DOREDRAW_INVISIBLES;
} }
break; break;
@ -457,6 +469,10 @@ void gwinRedraw(GHandle gh) {
if (visible) { if (visible) {
if (!(gh->flags & GWIN_FLG_VISIBLE)) { if (!(gh->flags & GWIN_FLG_VISIBLE)) {
gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); 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; RedrawPending |= DOREDRAW_VISIBLES;
TriggerRedraw(); TriggerRedraw();
} }
@ -464,6 +480,10 @@ void gwinRedraw(GHandle gh) {
if ((gh->flags & GWIN_FLG_VISIBLE)) { if ((gh->flags & GWIN_FLG_VISIBLE)) {
gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE);
gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW);
// No focus for us anymore
_gwinFixFocus(gh);
RedrawPending |= DOREDRAW_INVISIBLES; RedrawPending |= DOREDRAW_INVISIBLES;
TriggerRedraw(); TriggerRedraw();
} }
@ -484,6 +504,10 @@ void gwinRedraw(GHandle gh) {
for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(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))) { 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 gh->flags |= GWIN_FLG_SYSENABLED; // Fix it
// Do we want to grab the focus
_gwinFixFocus(gh);
_gwinUpdate(gh); _gwinUpdate(gh);
} }
} }
@ -497,6 +521,10 @@ void gwinRedraw(GHandle gh) {
for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(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)))) { 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 gh->flags &= ~GWIN_FLG_SYSENABLED; // Fix it
// No focus for us anymore
_gwinFixFocus(gh);
_gwinUpdate(gh); _gwinUpdate(gh);
} }
} }
@ -508,11 +536,19 @@ void gwinRedraw(GHandle gh) {
if (enabled) { if (enabled) {
if (!(gh->flags & GWIN_FLG_ENABLED)) { if (!(gh->flags & GWIN_FLG_ENABLED)) {
gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
// Do we want to grab the focus
_gwinFixFocus(gh);
_gwinUpdate(gh); _gwinUpdate(gh);
} }
} else { } else {
if ((gh->flags & GWIN_FLG_ENABLED)) { if ((gh->flags & GWIN_FLG_ENABLED)) {
gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED);
// No focus for us anymore
_gwinFixFocus(gh);
_gwinUpdate(gh); _gwinUpdate(gh);
} }
} }
@ -573,25 +609,6 @@ GHandle gwinGetNextWindow(GHandle gh) {
return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList); 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 #if GWIN_NEED_FLASHING
static void FlashTimerFn(void *param) { static void FlashTimerFn(void *param) {
GHandle gh; GHandle gh;