diff --git a/demos/applications/combo/main.c b/demos/applications/combo/main.c index 0d18d7bc..fba3d851 100644 --- a/demos/applications/combo/main.c +++ b/demos/applications/combo/main.c @@ -42,7 +42,7 @@ /* Our custom yellow style */ static const GWidgetStyle YellowWidgetStyle = { Yellow, // window background - //HTML2COLOR(0x800000), // focus - for text edit. + HTML2COLOR(0x800000), // focus - for text edit. // enabled color set { diff --git a/demos/modules/gwin/textedit/demo.mk b/demos/modules/gwin/textedit/demo.mk new file mode 100644 index 00000000..2cd40fd3 --- /dev/null +++ b/demos/modules/gwin/textedit/demo.mk @@ -0,0 +1,3 @@ +DEMODIR = $(GFXLIB)/demos/modules/gwin/textedit +GFXINC += $(DEMODIR) +GFXSRC += $(DEMODIR)/main.c diff --git a/demos/modules/gwin/textedit/gfxconf.h b/demos/modules/gwin/textedit/gfxconf.h new file mode 100644 index 00000000..4faaae82 --- /dev/null +++ b/demos/modules/gwin/textedit/gfxconf.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012, 2013, Joel Bodenmann aka Tectu + * Copyright (c) 2012, 2013, Andrew Hannam aka inmarket + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GFXCONF_H +#define _GFXCONF_H + +/* The operating system to use. One of these must be defined - preferably in your Makefile */ +//#define GFX_USE_OS_CHIBIOS FALSE +//#define GFX_USE_OS_WIN32 FALSE +//#define GFX_USE_OS_LINUX FALSE +//#define GFX_USE_OS_OSX FALSE + +/* GFX sub-systems to turn on */ +#define GFX_USE_GDISP TRUE +#define GFX_USE_GWIN TRUE +#define GFX_USE_GINPUT TRUE +#define GFX_USE_GEVENT TRUE +#define GFX_USE_GTIMER TRUE + +/* Features for the GDISP sub-system. */ +#define GDISP_NEED_VALIDATION TRUE +#define GDISP_NEED_CLIP TRUE +#define GDISP_NEED_TEXT TRUE +#define GDISP_NEED_TEXT_KERNING 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 + +/* Features for the GINPUT subsystem. */ +#define GINPUT_NEED_MOUSE TRUE + +/* One or both of these */ +#define GINPUT_NEED_KEYBOARD TRUE +#define GWIN_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/textedit/main.c b/demos/modules/gwin/textedit/main.c new file mode 100644 index 00000000..4072f983 --- /dev/null +++ b/demos/modules/gwin/textedit/main.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2012, 2013, Joel Bodenmann aka Tectu + * Copyright (c) 2012, 2013, Andrew Hannam aka inmarket + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gfx.h" + +static GHandle ghConsole; +static GHandle ghTextedit1; +static GHandle ghTextedit2; +static GHandle ghTextedit3; +static GListener gl; +#if GWIN_NEED_KEYBOARD + static GHandle ghKeyboard; +#endif + +static void guiCreate(void) +{ + GWidgetInit wi; + gwinWidgetClearInit(&wi); + + // Console + wi.g.show = TRUE; + wi.g.x = 0; + wi.g.y = 0; + wi.g.width = gdispGetWidth()/2; + wi.g.height = gdispGetHeight(); + ghConsole = gwinConsoleCreate(0, &wi.g); + gwinSetColor(ghConsole, Yellow); + gwinSetBgColor(ghConsole, Black); + gwinSetFont(ghConsole, gdispOpenFont("UI2")); + gwinClear(ghConsole); + + // TextEdit1 + wi.g.show = TRUE; + wi.g.x = gdispGetWidth()/2 + 10; + wi.g.y = 20; + wi.g.width = 200; + wi.g.height = 35; + wi.text = "Use the TAB key"; + ghTextedit1 = gwinTexteditCreate(0, &wi, 100); + + // TextEdit2 + wi.g.show = TRUE; + wi.g.x = gdispGetWidth()/2 + 10; + wi.g.y = 100; + wi.g.width = 200; + wi.g.height = 35; + wi.text = "to switch between"; + ghTextedit2 = gwinTexteditCreate(0, &wi, 20); + //gwinTexteditSetBorder(ghTextedit2, FALSE); + + // TextEdit3 + wi.g.show = TRUE; + wi.g.x = gdispGetWidth()/2 + 10; + wi.g.y = 180; + wi.g.width = 200; + wi.g.height = 35; + wi.text = "the different widgets"; + ghTextedit3 = gwinTexteditCreate(0, &wi, 100); + //gwinTexteditSetBorder(ghTextedit3, TRUE); + + // Virtual keyboard +#if GWIN_NEED_KEYBOARD + wi.g.show = TRUE; + wi.g.x = 0; + wi.g.y = gdispGetHeight()*3/4; + wi.g.width = gdispGetWidth(); + wi.g.height = gdispGetHeight()/4; + ghKeyboard = gwinKeyboardCreate(0, &wi); +#endif + +} + +int main(void) { + GEventKeyboard* pk; + unsigned i; + + gfxInit(); + + gdispClear(Silver); + gwinSetDefaultFont(gdispOpenFont("DejaVuSans16")); + gwinSetDefaultStyle(&WhiteWidgetStyle, FALSE); + gwinSetDefaultColor(Black); + gwinSetDefaultBgColor(White); + + geventListenerInit(&gl); + geventAttachSource(&gl, ginputGetKeyboard(0), GLISTEN_KEYTRANSITIONS|GLISTEN_KEYUP); + + guiCreate(); + + gwinPrintf(ghConsole, "Keyboard Monitor...\n"); + + while(1) { + // Get an Event + pk = (GEventKeyboard *)geventEventWait(&gl, TIME_INFINITE); + + if (pk->type != GEVENT_KEYBOARD) + continue; + + gwinPrintf(ghConsole, "KEYSTATE: 0x%04X [ %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s]", + pk->keystate, + (!pk->keystate ? "NONE " : ""), + ((pk->keystate & GKEYSTATE_KEYUP) ? "KEYUP " : ""), + ((pk->keystate & GKEYSTATE_REPEAT) ? "REPEAT " : ""), + ((pk->keystate & GKEYSTATE_SPECIAL) ? "SPECIAL " : ""), + ((pk->keystate & GKEYSTATE_RAW) ? "RAW " : ""), + ((pk->keystate & GKEYSTATE_SHIFT_L) ? "LSHIFT " : ""), + ((pk->keystate & GKEYSTATE_SHIFT_R) ? "RSHIFT " : ""), + ((pk->keystate & GKEYSTATE_CTRL_L) ? "LCTRL " : ""), + ((pk->keystate & GKEYSTATE_CTRL_R) ? "RCTRL " : ""), + ((pk->keystate & GKEYSTATE_ALT_L) ? "LALT " : ""), + ((pk->keystate & GKEYSTATE_ALT_R) ? "RALT " : ""), + ((pk->keystate & GKEYSTATE_FN) ? "FN " : ""), + ((pk->keystate & GKEYSTATE_COMPOSE) ? "COMPOSE " : ""), + ((pk->keystate & GKEYSTATE_WINKEY) ? "WINKEY " : ""), + ((pk->keystate & GKEYSTATE_CAPSLOCK) ? "CAPSLOCK " : ""), + ((pk->keystate & GKEYSTATE_NUMLOCK) ? "NUMLOCK " : ""), + ((pk->keystate & GKEYSTATE_SCROLLLOCK) ? "SCROLLLOCK " : "") + ); + if (pk->bytecount) { + gwinPrintf(ghConsole, " Keys:"); + for (i = 0; i < pk->bytecount; i++) + gwinPrintf(ghConsole, " 0x%02X", (uint8_t)pk->c[i]); + gwinPrintf(ghConsole, " ["); + for (i = 0; i < pk->bytecount; i++) + gwinPrintf(ghConsole, "%c", pk->c[i] >= ' ' && pk->c[i] <= '~' ? pk->c[i] : ' '); + gwinPrintf(ghConsole, "]"); + } + gwinPrintf(ghConsole, "\n"); + } +} + diff --git a/demos/modules/gwin/widgets/main.c b/demos/modules/gwin/widgets/main.c index 35a6c1fd..8f5a6329 100644 --- a/demos/modules/gwin/widgets/main.c +++ b/demos/modules/gwin/widgets/main.c @@ -49,6 +49,7 @@ /* Our custom yellow style */ static const GWidgetStyle YellowWidgetStyle = { Yellow, // window background + HTML2COLOR(0x800000), // focus // enabled color set { diff --git a/docs/releases.txt b/docs/releases.txt index 07fa4f4d..c4e1073e 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -13,6 +13,10 @@ FEATURE: Renaming GFX_NO_OS_INIT to GFX_OS_NO_INIT FEATURE: New demo applications/combo FEATURE: Adding more font metrics (BaselineX and BaselineY) FEATURE: Adding gdispGetStringWidthCount() +FEATURE: Implementing widget focusing. See gwinSetFocus() and gwinGetFocus() +FEATURE: Adding TextEdit widget +FEATURE: Added color to widget style for focused widgets +FEATURE: Added GWIN_FOCUS_HIGHLIGHT_WIDTH as an option in the configuration file *** Release 2.3 *** FEATURE: Added more events to the slider widget diff --git a/gfxconf.example.h b/gfxconf.example.h index 0753cf64..cb6cda67 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -169,6 +169,7 @@ //#define GWIN_NEED_GL3D FALSE //#define GWIN_NEED_WIDGET FALSE +//#define GWIN_FOCUS_HIGHLIGHT_WIDTH 1 // #define GWIN_NEED_LABEL FALSE // #define GWIN_LABEL_ATTRIBUTE FALSE // #define GWIN_NEED_BUTTON FALSE @@ -188,6 +189,7 @@ // #define GWIN_NEED_KEYBOARD FALSE // #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1 // #define GWIN_NEED_KEYBOARD_ENGLISH1 TRUE +// #define GWIN_NEED_TEXTEDIT FALSE // #define GWIN_FLAT_STYLING FALSE // #define GWIN_WIDGET_TAGS FALSE diff --git a/src/ginput/ginput_keyboard.h b/src/ginput/ginput_keyboard.h index 1349092f..40691a0d 100644 --- a/src/ginput/ginput_keyboard.h +++ b/src/ginput/ginput_keyboard.h @@ -164,13 +164,11 @@ typedef struct GEventKeyboard_t { #define GLISTEN_KEYTRANSITIONS 0x0008 // Return transitions to the key state #define GLISTEN_KEYRAW 0x0010 // Return raw scan-codes. This turns off normal character processing. -#endif - -#if GINPUT_NEED_KEYBOARD || defined(__DOXYGEN__) - // All keyboards #define GKEYBOARD_ALL_INSTANCES ((unsigned)-1) +#endif + /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ @@ -188,33 +186,34 @@ extern "C" { */ GSourceHandle ginputGetKeyboard(unsigned instance); - /** - * @brief Get the current keyboard status - * - * @param[in] instance The ID of the keyboard input instance - * @param[in] pkeyboard The keyboard event struct - * - * @return Returns FALSE on an error (eg invalid instance) - */ - bool_t ginputGetKeyboardStatus(unsigned instance, GEventKeyboard *pkeyboard); + #if GINPUT_NEED_KEYBOARD || defined(__DOXYGEN__) - #if !GKEYBOARD_LAYOUT_OFF || defined(__DOXYGEN__) /** - * @brief Set the keyboard layout + * @brief Get the current keyboard status * * @param[in] instance The ID of the keyboard input instance - * @param[in] pLayout The keyboard layout micro-code. Passing NULL defaults to the driver's default layout. + * @param[in] pkeyboard The keyboard event struct * * @return Returns FALSE on an error (eg invalid instance) */ - bool_t ginputSetKeyboardLayout(unsigned instance, const void *pLayout); - #endif + bool_t ginputGetKeyboardStatus(unsigned instance, GEventKeyboard *pkeyboard); + + #if !GKEYBOARD_LAYOUT_OFF || defined(__DOXYGEN__) + /** + * @brief Set the keyboard layout + * + * @param[in] instance The ID of the keyboard input instance + * @param[in] pLayout The keyboard layout micro-code. Passing NULL defaults to the driver's default layout. + * + * @return Returns FALSE on an error (eg invalid instance) + */ + bool_t ginputSetKeyboardLayout(unsigned instance, const void *pLayout); + #endif + #endif /* GINPUT_NEED_KEYBOARD */ #ifdef __cplusplus } #endif -#endif /* GINPUT_NEED_KEYBOARD */ - #endif /* _GINPUT_KEYBOARD_H */ /** @} */ diff --git a/src/gwin/gwin.mk b/src/gwin/gwin.mk index b4357328..d735c3e1 100644 --- a/src/gwin/gwin.mk +++ b/src/gwin/gwin.mk @@ -21,6 +21,7 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin.c \ $(GFXLIB)/src/gwin/gwin_tabset.c \ $(GFXLIB)/src/gwin/gwin_gl3d.c \ $(GFXLIB)/src/gwin/gwin_keyboard.c \ - $(GFXLIB)/src/gwin/gwin_keyboard_layout.c + $(GFXLIB)/src/gwin/gwin_keyboard_layout.c \ + $(GFXLIB)/src/gwin/gwin_textedit.c GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include diff --git a/src/gwin/gwin_button.c b/src/gwin/gwin_button.c index 4066884c..4d83348e 100644 --- a/src/gwin/gwin_button.c +++ b/src/gwin/gwin_button.c @@ -50,6 +50,24 @@ } #endif +#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + static void ButtonKeyboard(GWidgetObject* gw, GEventKeyboard* pke) + { + // ENTER and SPACE keys to press the button + if (pke->c[0] == GKEY_ENTER || pke->c[0] == GKEY_SPACE) { + + // Press or release event? + if (pke->keystate & GKEYSTATE_KEYUP) { + gw->g.flags &= ~GBUTTON_FLG_PRESSED; + } else { + gw->g.flags |= GBUTTON_FLG_PRESSED; + } + } + + _gwinUpdate((GHandle)gw); + } +#endif + #if GINPUT_NEED_TOGGLE // A toggle off has occurred static void ButtonToggleOff(GWidgetObject *gw, uint16_t role) { @@ -95,6 +113,11 @@ static const gwidgetVMT buttonVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + ButtonKeyboard // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 1, // 1 toggle role diff --git a/src/gwin/gwin_checkbox.c b/src/gwin/gwin_checkbox.c index 11e08804..d477420b 100644 --- a/src/gwin/gwin_checkbox.c +++ b/src/gwin/gwin_checkbox.c @@ -55,6 +55,23 @@ static void SendCheckboxEvent(GWidgetObject *gw) { } #endif +#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + static void CheckboxKeyboard(GWidgetObject* gw, GEventKeyboard* pke) + { + // Only react on KEYDOWN events. Ignore KEYUP events. + if (pke->keystate & GKEYSTATE_KEYUP) { + break; + } + + // ENTER and SPACE keys to check/uncheck the checkbox + if (pke->c[0] == GKEY_ENTER || pke->c[0] == GKEY_SPACE) { + gw->g.flags ^= GCHECKBOX_FLG_CHECKED; + } + + _gwinUpdate((GHandle)gw); + } +#endif + #if GINPUT_NEED_TOGGLE static void CheckboxToggleOn(GWidgetObject *gw, uint16_t role) { (void) role; @@ -91,6 +108,11 @@ static const gwidgetVMT checkboxVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + CheckboxKeyboard // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 1, // 1 toggle role diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h index 01b6e596..7946a839 100644 --- a/src/gwin/gwin_class.h +++ b/src/gwin/gwin_class.h @@ -31,25 +31,25 @@ * @brief The predefined flags for a Window * @{ */ -#define GWIN_FIRST_CONTROL_FLAG 0x00000001 // @< 8 bits free for the control to use -#define GWIN_LAST_CONTROL_FLAG 0x00000080 // @< 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_NEEDREDRAW 0x00004000 // @< Redraw is needed but has been delayed -#define GWIN_FLG_BGREDRAW 0x00008000 // @< On redraw, if not visible redraw the revealed under-side -#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_FLG_MOUSECAPTURE 0x00400000 // @< The window has captured the mouse -#define GWIN_FLG_FLASHING 0x00800000 // @< The window is flashing - see the _gwinFlashState boolean -#define GWIN_FIRST_WM_FLAG 0x01000000 // @< 8 bits free for the window manager to use -#define GWIN_LAST_WM_FLAG 0x80000000 // @< 8 bits free for the window manager to use +#define GWIN_FIRST_CONTROL_FLAG 0x00000001 /**< 8 bits free for the control to use */ +#define GWIN_LAST_CONTROL_FLAG 0x00000080 /**< 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_NEEDREDRAW 0x00004000 /**< Redraw is needed but has been delayed */ +#define GWIN_FLG_BGREDRAW 0x00008000 /**< On redraw, if not visible redraw the revealed under-side */ +#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_FLG_MOUSECAPTURE 0x00400000 /**< The window has captured the mouse */ +#define GWIN_FLG_FLASHING 0x00800000 /**< The window is flashing - see the _gwinFlashState boolean */ +#define GWIN_FIRST_WM_FLAG 0x01000000 /**< 8 bits free for the window manager to use */ +#define GWIN_LAST_WM_FLAG 0x80000000 /**< 8 bits free for the window manager to use */ /** @} */ /** @@ -57,11 +57,11 @@ * @{ */ typedef struct gwinVMT { - const char * classname; // @< The GWIN classname (mandatory) - size_t size; // @< The size of the class object - void (*Destroy) (GWindowObject *gh); // @< The GWIN destroy function (optional) - void (*Redraw) (GWindowObject *gh); // @< The GWIN redraw routine (optional) - void (*AfterClear) (GWindowObject *gh); // @< The GWIN after-clear function (optional) + const char * classname; /**< The GWIN classname (mandatory) */ + size_t size; /**< The size of the class object */ + void (*Destroy) (GWindowObject *gh); /**< The GWIN destroy function (optional) */ + void (*Redraw) (GWindowObject *gh); /**< The GWIN redraw routine (optional) */ + void (*AfterClear) (GWindowObject *gh); /**< The GWIN after-clear function (optional) */ } gwinVMT; /** @} */ @@ -87,30 +87,35 @@ typedef struct gwinVMT { * @{ */ typedef struct gwidgetVMT { - struct gwinVMT g; // @< This is still a GWIN - void (*DefaultDraw) (GWidgetObject *gw, void *param); // @< The default drawing routine (mandatory) + struct gwinVMT g; /**< This is still a GWIN */ + void (*DefaultDraw) (GWidgetObject *gw, void *param); /**< The default drawing routine (mandatory) */ #if GINPUT_NEED_MOUSE struct { - void (*MouseDown) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse down events (optional) - void (*MouseUp) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse up events (optional) - void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse move events (optional) + void (*MouseDown) (GWidgetObject *gw, coord_t x, coord_t y); /**< Process mouse down events (optional) */ + void (*MouseUp) (GWidgetObject *gw, coord_t x, coord_t y); /**< Process mouse up events (optional) */ + void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); /**< Process mouse move events (optional) */ + }; + #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + struct { + void (*KeyboardEvent) (GWidgetObject *gw, GEventKeyboard *pke); /**< Process keyboard events (optional) */ }; #endif #if GINPUT_NEED_TOGGLE struct { - uint16_t toggleroles; // @< The roles supported for toggles (0->toggleroles-1) - void (*ToggleAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Assign a toggle to a role (optional) - uint16_t (*ToggleGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) - void (*ToggleOff) (GWidgetObject *gw, uint16_t role); // @< Process toggle off events (optional) - void (*ToggleOn) (GWidgetObject *gw, uint16_t role); // @< Process toggle on events (optional) + uint16_t toggleroles; /**< The roles supported for toggles (0->toggleroles-1) */ + void (*ToggleAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); /**< Assign a toggle to a role (optional) */ + uint16_t (*ToggleGet) (GWidgetObject *gw, uint16_t role); /**< Return the instance for a particular role (optional) */ + void (*ToggleOff) (GWidgetObject *gw, uint16_t role); /**< Process toggle off events (optional) */ + void (*ToggleOn) (GWidgetObject *gw, uint16_t role); /**< Process toggle on events (optional) */ }; #endif #if GINPUT_NEED_DIAL struct { - uint16_t dialroles; // @< The roles supported for dials (0->dialroles-1) - void (*DialAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Test the role and save the dial instance handle (optional) - uint16_t (*DialGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) - void (*DialMove) (GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max); // @< Process dial move events (optional) + uint16_t dialroles; /**< The roles supported for dials (0->dialroles-1) */ + void (*DialAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); /**< Test the role and save the dial instance handle (optional) */ + uint16_t (*DialGet) (GWidgetObject *gw, uint16_t role); /**< Return the instance for a particular role (optional) */ + void (*DialMove) (GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max); /**< Process dial move events (optional) */ }; #endif } gwidgetVMT; @@ -130,12 +135,12 @@ typedef struct gwinVMT { */ typedef struct gcontainerVMT { gwidgetVMT gw; - coord_t (*LeftBorder) (GHandle gh); // @< The size of the left border (mandatory) - coord_t (*TopBorder) (GHandle gh); // @< The size of the top border (mandatory) - coord_t (*RightBorder) (GHandle gh); // @< The size of the right border (mandatory) - coord_t (*BottomBorder) (GHandle gh); // @< The size of the bottom border (mandatory) - 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) + coord_t (*LeftBorder) (GHandle gh); /**< The size of the left border (mandatory) */ + coord_t (*TopBorder) (GHandle gh); /**< The size of the top border (mandatory) */ + coord_t (*RightBorder) (GHandle gh); /**< The size of the right border (mandatory) */ + coord_t (*BottomBorder) (GHandle gh); /**< The size of the bottom border (mandatory) */ + 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 @@ -151,22 +156,22 @@ typedef struct gwinVMT { * @{ */ typedef struct gwmVMT { - void (*Init) (void); // @< The window manager has just been set as the current window manager - void (*DeInit) (void); // @< The window manager has just been removed as the current window manager - 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); // @< A window needs to be redraw (or undrawn) - 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 + void (*Init) (void); /**< The window manager has just been set as the current window manager */ + void (*DeInit) (void); /**< The window manager has just been removed as the current window manager */ + 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); /**< A window needs to be redraw (or undrawn) */ + 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 current window manager */ - extern GWindowManager * _GWINwm; + extern GWindowManager *_GWINwm; extern bool_t _gwinFlashState; #endif @@ -251,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 */ @@ -317,6 +321,52 @@ 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. + * + * @note The focus can stay on the same window if there is no other focusable 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__) /** @@ -330,6 +380,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_container.c b/src/gwin/gwin_container.c index b84c96ae..89d09e9a 100644 --- a/src/gwin/gwin_container.c +++ b/src/gwin/gwin_container.c @@ -110,6 +110,11 @@ static const gcontainerVMT containerVMT = { 0, 0, 0, // No mouse }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, 0, 0, 0, 0, // No toggles diff --git a/src/gwin/gwin_frame.c b/src/gwin/gwin_frame.c index b29c4ffc..7400b0f5 100644 --- a/src/gwin/gwin_frame.c +++ b/src/gwin/gwin_frame.c @@ -178,6 +178,11 @@ static const gcontainerVMT frameVMT = { 0, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // 1 toggle role diff --git a/src/gwin/gwin_keyboard.c b/src/gwin/gwin_keyboard.c index e9f5f860..70771d9b 100644 --- a/src/gwin/gwin_keyboard.c +++ b/src/gwin/gwin_keyboard.c @@ -30,6 +30,8 @@ typedef uint32_t utf32; // A character code - note this is not UTF-32 but a representation of the UTF-8 code stream for a single character. typedef uint32_t ucode; +static GSourceHandle AllKeyboards; + // Get the length of a UTF-8 string static int UTF8StrLen(const utf8 *s) { int len; @@ -164,6 +166,11 @@ static void SendKeyboardEventToListener(GSourceListener *psl, GKeyboardObject *g static void SendKeyboardEvent(GKeyboardObject *gk) { GSourceListener *psl; + // Send to the "All Keyboards" source listeners + psl = 0; + while ((psl = geventGetSourceListener(AllKeyboards, psl))) + SendKeyboardEventToListener(psl, gk); + // Send to the keyboard specific source listeners psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)gk, psl))) @@ -313,6 +320,11 @@ static const gwidgetVMT keyboardVMT = { KeyMouseMove, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // No toggle roles @@ -339,6 +351,10 @@ GHandle gwinGKeyboardCreate(GDisplay *g, GKeyboardObject *gk, const GWidgetInit gk->keytable = &GWIN_KEYBOARD_DEFAULT_LAYOUT; gk->keyset = gk->keytable->ksets[0]; gk->lastkeyrow = gk->lastkeycol = gk->keyrow = gk->keycol = GKEY_BAD_ROWCOL; + + if (!AllKeyboards) + AllKeyboards = ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES); + gwinSetVisible((GHandle)gk, pInit->g.show); return (GHandle)gk; } @@ -468,4 +484,12 @@ void gwinKeyboardDraw_Normal(GWidgetObject *gw, void *param) { #undef gk } +#if !(GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) + GSourceHandle ginputGetKeyboard(unsigned instance) { + if (instance == GKEYBOARD_ALL_INSTANCES) + return (GSourceHandle)&AllKeyboards; + return 0; + } +#endif + #endif /* GFX_USE_GWIN && GWIN_NEED_KEYBOARD */ diff --git a/src/gwin/gwin_label.c b/src/gwin/gwin_label.c index 4e4d799b..b78e4b3b 100644 --- a/src/gwin/gwin_label.c +++ b/src/gwin/gwin_label.c @@ -58,6 +58,11 @@ static const gwidgetVMT labelVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard key down events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // No toggle role diff --git a/src/gwin/gwin_list.c b/src/gwin/gwin_list.c index 7ca300a9..88d3028a 100644 --- a/src/gwin/gwin_list.c +++ b/src/gwin/gwin_list.c @@ -279,6 +279,11 @@ static const gwidgetVMT listVMT = { ListMouseMove, }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 2, // two toggle roles diff --git a/src/gwin/gwin_mk.c b/src/gwin/gwin_mk.c index 981eb6d2..2af501b5 100644 --- a/src/gwin/gwin_mk.c +++ b/src/gwin/gwin_mk.c @@ -24,3 +24,4 @@ #include "gwin_gl3d.c" #include "gwin_keyboard.c" #include "gwin_keyboard_layout.c" +#include "gwin_textedit.c" diff --git a/src/gwin/gwin_options.h b/src/gwin/gwin_options.h index b69796c3..45dfcd36 100644 --- a/src/gwin/gwin_options.h +++ b/src/gwin/gwin_options.h @@ -44,6 +44,13 @@ #ifndef GWIN_NEED_WIDGET #define GWIN_NEED_WIDGET FALSE #endif + /** + * @brief The width of the rectangle that highlights a widget that is focused + * @details Defaults to 1 + */ + #ifndef GWIN_FOCUS_HIGHLIGHT_WIDTH + #define GWIN_FOCUS_HIGHLIGHT_WIDTH 1 + #endif /** * @brief Should the simple container be included. * @details Defaults to FALSE @@ -142,6 +149,13 @@ #ifndef GWIN_NEED_KEYBOARD #define GWIN_NEED_KEYBOARD FALSE #endif + /** + * @brief Should the textedit widget be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_TEXTEDIT + #define GWIN_NEED_TEXTEDIT FALSE + #endif /** * @} * diff --git a/src/gwin/gwin_progressbar.c b/src/gwin/gwin_progressbar.c index e275b9e4..0de69867 100644 --- a/src/gwin/gwin_progressbar.c +++ b/src/gwin/gwin_progressbar.c @@ -51,6 +51,11 @@ static const gwidgetVMT progressbarVMT = { 0, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // 1 toggle role diff --git a/src/gwin/gwin_radio.c b/src/gwin/gwin_radio.c index c7d5cf5c..470a1f2a 100644 --- a/src/gwin/gwin_radio.c +++ b/src/gwin/gwin_radio.c @@ -92,6 +92,11 @@ static const gwidgetVMT radioVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 1, // 1 toggle role diff --git a/src/gwin/gwin_rules.h b/src/gwin/gwin_rules.h index 7678ab7c..ea35a426 100644 --- a/src/gwin/gwin_rules.h +++ b/src/gwin/gwin_rules.h @@ -38,7 +38,7 @@ #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 || GWIN_NEED_KEYBOARD + GWIN_NEED_IMAGE || GWIN_NEED_CHECKBOX || GWIN_NEED_PROGRESSBAR || GWIN_NEED_KEYBOARD || GWIN_NEED_TEXTEDIT #if !GWIN_NEED_WIDGET #if GFX_DISPLAY_RULE_WARNINGS #warning "GWIN: GWIN_NEED_WIDGET is required when a widget is used. It has been turned on for you." @@ -123,6 +123,14 @@ #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." #endif #endif + #if GWIN_NEED_TEXTEDIT + #if !GDISP_NEED_TEXT + #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_TEXTEDIT is TRUE." + #endif + #if !(GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD) + #error "GWIN: GINPUT_NEED_KEYBOARD or GWIN_NEED_KEYBOARD is required if GWIN_NEED_TEXTEDIT is TRUE." + #endif + #endif #endif #endif /* _GWIN_RULES_H */ diff --git a/src/gwin/gwin_slider.c b/src/gwin/gwin_slider.c index c29d5996..c8f47f95 100644 --- a/src/gwin/gwin_slider.c +++ b/src/gwin/gwin_slider.c @@ -250,6 +250,11 @@ static const gwidgetVMT sliderVMT = { SliderMouseMove, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 2, // 1 toggle role diff --git a/src/gwin/gwin_tabset.c b/src/gwin/gwin_tabset.c index f1bcd374..d059256d 100644 --- a/src/gwin/gwin_tabset.c +++ b/src/gwin/gwin_tabset.c @@ -77,6 +77,11 @@ static const gcontainerVMT tabpageVMT = { 0, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // 1 toggle role diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c new file mode 100644 index 00000000..ee147c84 --- /dev/null +++ b/src/gwin/gwin_textedit.c @@ -0,0 +1,253 @@ +/* + * 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/gwin_textedit.c + * @brief GWIN TextEdit widget header file + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_TEXTEDIT + +#include "gwin_class.h" +#include + +// Some settings +#define TEXT_PADDING_LEFT 4 +#define CURSOR_PADDING_LEFT 0 +#define CURSOR_EXTRA_HEIGHT 1 + +// Macros to assist in data type conversions +#define gh2obj ((GTexteditObject *)gh) +#define gw2obj ((GTexteditObject *)gw) + +static bool_t resizeText(GWidgetObject* gw, size_t pos, int32_t diff) { + char *p, *q; + size_t sz; + + p = (char *)gw->text; + sz = strlen(p)+1; + if (diff < 0) + memcpy(p+pos, p+pos-diff, sz-pos+diff); + if (!(p = gfxRealloc(p, sz, sz+diff))) + return FALSE; + gw->text = p; + if (diff > 0) { + q = p + sz; + p += pos; + while(--q >= p) + q[diff] = q[0]; + } + return TRUE; +} + +#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD + static void TextEditKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { + // Only react on KEYDOWN events. Ignore KEYUP events. + if ((pke->keystate & GKEYSTATE_KEYUP) || !pke->bytecount) + return; + + // Is it a special key? + if (pke->keystate & GKEYSTATE_SPECIAL) { + + // Arrow keys to move the cursor + switch ((uint8_t)pke->c[0]) { + case GKEY_LEFT: + if (!gw2obj->cursorPos) + return; + gw2obj->cursorPos--; + break; + case GKEY_RIGHT: + if (!gw->text[gw2obj->cursorPos]) + return; + gw2obj->cursorPos++; + break; + case GKEY_HOME: + if (!gw2obj->cursorPos) + return; + gw2obj->cursorPos = 0; + break; + case GKEY_END: + if (!gw->text[gw2obj->cursorPos]) + return; + gw2obj->cursorPos = strlen(gw->text); + break; + default: + return; + } + + } else { + + // Normal key press + switch((uint8_t)pke->c[0]) { + case GKEY_BACKSPACE: + // Backspace + if (!gw2obj->cursorPos) + return; + gw2obj->cursorPos--; + resizeText(gw, gw2obj->cursorPos, -1); + break; + case GKEY_TAB: + case GKEY_LF: + case GKEY_CR: + // Move to the next field + _gwinMoveFocus(); + return; + case GKEY_DEL: + // Delete + if (!gw->text[gw2obj->cursorPos]) + return; + resizeText(gw, gw2obj->cursorPos, -1); + break; + default: + // Ignore any other control characters + if ((uint8_t)pke->c[0] < GKEY_SPACE) + return; + + // Keep the edit length to less than the maximum + if (gw2obj->maxSize && gw2obj->cursorPos+pke->bytecount > gw2obj->maxSize) + return; + + // Make space + resizeText(gw, gw2obj->cursorPos, pke->bytecount); + + // Insert the character + memcpy((char *)gw->text+gw2obj->cursorPos, pke->c, pke->bytecount); + gw2obj->cursorPos += pke->bytecount; + break; + } + } + + _gwinUpdate((GHandle)gw); + } +#endif + +static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param); + +static const gwidgetVMT texteditVMT = { + { + "TextEdit", // The class name + sizeof(GTexteditObject), // The object size + _gwidgetDestroy, // The destroy routine + _gwidgetRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinTexteditDefaultDraw, // default drawing routine + #if GINPUT_NEED_MOUSE + { + 0, // Process mouse down events (NOT USED) + 0, // Process mouse up events (NOT USED) + 0, // Process mouse move events (NOT USED) + }, + #endif + #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD + { + TextEditKeyboard // Process keyboard key down events + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, // No toggle role + 0, // Assign Toggles (NOT USED) + 0, // Get Toggles (NOT USED) + 0, // Process toggle off event (NOT USED) + 0, // Process toggle on event (NOT USED) + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // No dial roles + 0, // Assign Dials (NOT USED) + 0, // Get Dials (NOT USED) + 0, // Procees dial move events (NOT USED) + }, + #endif +}; + +GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize) +{ + char *p; + + // Create the underlying widget + if (!(wt = (GTexteditObject*)_gwidgetCreate(g, &wt->w, pInit, &texteditVMT))) + return 0; + + wt->maxSize = maxSize; + + // Reallocate the text (if necessary) + if (!(wt->w.g.flags & GWIN_FLG_ALLOCTXT)) { + if (!(p = gfxAlloc(wt->maxSize+1))) + return 0; + strncpy(p, wt->w.text, wt->maxSize); + wt->w.text = p; + wt->w.g.flags |= GWIN_FLG_ALLOCTXT; + } + + // Set text and cursor position + wt->cursorPos = strlen(wt->w.text); + + gwinSetVisible(&wt->w.g, pInit->g.show); + + return (GHandle)wt; +} + +static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) +{ + const char *p; + coord_t cpos, tpos; + color_t ccol, tcol; + (void)param; + + // Is it a valid handle? + if (gw->g.vmt != (gwinVMT*)&texteditVMT) + return; + + // Retrieve colors + tcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; + ccol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge; + + // Adjust the text position so the cursor fits in the window + p = gw->text; + if (!gw2obj->cursorPos) + tpos = 0; + else { + for(cpos = gw2obj->cursorPos; ; p++, cpos--) { + tpos = gdispGetStringWidthCount(p, gw->g.font, cpos); + if (tpos < gw->g.width-(TEXT_PADDING_LEFT+CURSOR_PADDING_LEFT)) + break; + } + } + + // Render background and string + #if TEXT_PADDING_LEFT + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, TEXT_PADDING_LEFT, gw->g.height, gw->pstyle->background); + #endif + gdispGFillStringBox(gw->g.display, gw->g.x + TEXT_PADDING_LEFT, gw->g.y, gw->g.width-TEXT_PADDING_LEFT, gw->g.height, p, gw->g.font, tcol, gw->pstyle->background, justifyLeft); + + // Render cursor (if focused) + if (gwinGetFocus() == (GHandle)gw) { + // Calculate cursor stuff + + // Draw cursor + tpos += gw->g.x + CURSOR_PADDING_LEFT + TEXT_PADDING_LEFT + gdispGetFontMetric(gw->g.font, fontBaselineX)/2; + cpos = (gw->g.height - gdispGetFontMetric(gw->g.font, fontHeight))/2 - CURSOR_EXTRA_HEIGHT; + gdispGDrawLine(gw->g.display, tpos, gw->g.y + cpos, tpos, gw->g.y + gw->g.height - cpos, ccol); + } + + // Render border + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ccol); + + // Render highlighted border of focused + _gwidgetDrawFocusRect(gw, 1, 1, gw->g.width-2, gw->g.height-2); + +} + +#undef gh2obj +#undef gw2obj + +#endif // GFX_USE_GWIN && GWIN_NEED_TEXTEDIT diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h new file mode 100644 index 00000000..f39f0c2a --- /dev/null +++ b/src/gwin/gwin_textedit.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/gwin_textedit.h + * @brief GWIN textedit widget header file + * + * @defgroup TextEdit TextEdit + * @ingroup Widgets + * + * @details A GWIN TextEdit widget allows user input. + * + * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GDISP_NEED_TEXT must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_TEXTEDIT must be set to TRUE in your gfxconf.h + * @pre The fonts you want to use must be enabled in your gfxconf.h + * + * @{ + */ + +#ifndef _GWIN_TEXTEDIT_H +#define _GWIN_TEXTEDIT_H + +// This file is included within "src/gwin/gwin_widget.h" + +// A TextEdit widget +typedef struct GTexteditObject { + GWidgetObject w; + + char* textBuffer; + size_t maxSize; + uint16_t cursorPos; +} GTexteditObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a TextEdit widget + * @details A TextEdit widget is a rectangular box which allows the user to input data through a keyboard. + * The keyboard can either be a physical one or a virtual on-screen keyboard as the keyboard driver + * is abstracted through the GINPUT module. + * + * @param[in] g The GDisplay on which the textedit should be displayed + * @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] maxSize The maximum number of characters the TextEdit widget can hold. (0 means unlimited). + * + * @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* wt, GWidgetInit* pInit, size_t maxSize); +#define gwinTexteditCreate(wt, pInit, maxSize) gwinGTexteditCreate(GDISP, wt, pInit, maxSize) + +#ifdef __cplusplus +} +#endif + +#endif // _GWIN_TEXTEDIT_H +/** @} */ diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index bfc5a48f..1bf91b11 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -18,19 +18,25 @@ #include "gwin_class.h" -/* Our listener for events for widgets */ -static GListener gl; +// Our listener for events for widgets +static GListener gl; -/* Our default style - a white background theme */ +#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 + HTML2COLOR(0x2A8FCD), // focused // enabled color set { HTML2COLOR(0x000000), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xE0E0E0), // progress - inactive area + HTML2COLOR(0xE0E0E0) // progress - inactive area }, // disabled color set @@ -38,7 +44,7 @@ const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0x808080), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xC0E0C0), // progress - active area + HTML2COLOR(0xC0E0C0) // progress - active area }, // pressed color set @@ -46,20 +52,21 @@ const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0x404040), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0x808080), // fill - HTML2COLOR(0x00E000), // progress - active area - }, + HTML2COLOR(0x00E000) // progress - active area + } }; /* Our black style */ const GWidgetStyle BlackWidgetStyle = { HTML2COLOR(0x000000), // window background + HTML2COLOR(0x2A8FCD), // focused // enabled color set { HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0xC0C0C0), // edge HTML2COLOR(0x606060), // fill - HTML2COLOR(0x404040), // progress - inactive area + HTML2COLOR(0x404040) // progress - inactive area }, // disabled color set @@ -67,7 +74,7 @@ const GWidgetStyle BlackWidgetStyle = { HTML2COLOR(0x808080), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0x404040), // fill - HTML2COLOR(0x004000), // progress - active area + HTML2COLOR(0x004000) // progress - active area }, // pressed color set @@ -75,19 +82,20 @@ const GWidgetStyle BlackWidgetStyle = { HTML2COLOR(0xFFFFFF), // text HTML2COLOR(0xC0C0C0), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0x008000), // progress - active area - }, + HTML2COLOR(0x008000) // progress - active area + } }; static const GWidgetStyle * defaultStyle = &BlackWidgetStyle; -/* We use these everywhere in this file */ +// We use these everywhere in this file #define gw ((GWidgetObject *)gh) #define wvmt ((gwidgetVMT *)gh->vmt) -/* Process an event */ +// Process an event static void gwidgetEvent(void *param, GEvent *pe) { #define pme ((GEventMouse *)pe) + #define pke ((GEventKeyboard *)pe) #define pte ((GEventToggle *)pe) #define pde ((GEventDial *)pe) @@ -105,7 +113,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_MOUSE: case GEVENT_TOUCH: // Cycle through all windows - for(gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) { + for (gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) { // The window must be on this display and visible to be relevant if (h->display != pme->display || !(h->flags & GWIN_FLG_SYSVISIBLE)) @@ -123,6 +131,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { // There is only ever one captured mouse. Prevent normal mouse processing if there is a captured mouse gh = 0; + break; } @@ -135,6 +144,13 @@ 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) || GWIN_NEED_KEYBOARD + // We should try and capture the focus on this window. + // If we can't then we don't change the focus + gwinSetFocus(gh); + #endif + if (wvmt->MouseDown) wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); } @@ -142,6 +158,21 @@ static void gwidgetEvent(void *param, GEvent *pe) { break; #endif + #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD + case GEVENT_KEYBOARD: + // If Tab key pressed then set focus to next widget + if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { + if (!(pke->keystate & GKEYSTATE_KEYUP)) + _gwinMoveFocus(); + break; + } + + // Otherwise, send keyboard events only to widget in focus + if (_widgetInFocus) + ((gwidgetVMT*)_widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)_widgetInFocus, pke); + break; + #endif + #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE case GEVENT_TOGGLE: // Cycle through all windows @@ -191,9 +222,108 @@ static void gwidgetEvent(void *param, GEvent *pe) { #undef pme #undef pte + #undef pke #undef pde } +#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_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; + bool_t looponce; + + // Find a new focus window (one may or may not exist). + looponce = FALSE; + for(gh = gwinGetNextWindow(_widgetInFocus); ; gh = gwinGetNextWindow(gh)) { + if (!gh && !looponce) { + looponce = TRUE; + gh = gwinGetNextWindow(0); + } + if (gwinSetFocus(gh)) + break; + } + } + + 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; - FIX LATER + return; + } + } + + // No-one has the right to the focus + _widgetInFocus = 0; + } + + void _gwidgetDrawFocusRect(GWidgetObject *gx, coord_t x, coord_t y, coord_t cx, coord_t cy) { + // Don't do anything if we don't have the focus + if (&gx->g != _widgetInFocus) + return; + + // Use the very simplest possible focus rectangle for now + uint16_t i = 0; + for (i = 0; i < GWIN_FOCUS_HIGHLIGHT_WIDTH; i++) { + gdispGDrawBox(gx->g.display, gx->g.x+x+i, gx->g.y+y+i, cx-2*i, cy-2*i, gx->pstyle->focus); + } + } + +#endif + #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE static GHandle FindToggleUser(uint16_t instance) { GHandle gh; @@ -235,6 +365,10 @@ void _gwidgetInit(void) geventListenerInit(&gl); geventRegisterCallback(&gl, gwidgetEvent, 0); geventAttachSource(&gl, ginputGetMouse(GMOUSE_ALL_INSTANCES), GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); + + #if GINPUT_NEED_KEYBOARD + geventAttachSource(&gl, ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES), GLISTEN_KEYUP); + #endif } void _gwidgetDeinit(void) @@ -267,6 +401,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; @@ -361,7 +499,6 @@ const GWidgetStyle *gwinGetDefaultStyle(void) { return defaultStyle; } - void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { if (!(gh->flags & GWIN_FLG_WIDGET)) return; @@ -398,12 +535,22 @@ const char *gwinGetText(GHandle gh) { return gw->text; } +bool_t gwinIsWidget(GHandle gh) { + if (gh->flags & GWIN_FLG_WIDGET) { + return TRUE; + } + + return FALSE; +} + void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle) { if (!(gh->flags & GWIN_FLG_WIDGET)) return; + gw->pstyle = pstyle ? pstyle : defaultStyle; gh->bgcolor = pstyle->background; gh->color = pstyle->enabled.text; + _gwinUpdate(gh); } diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index 0ed80a84..732d7d1c 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -33,10 +33,10 @@ struct GWidgetObject; * @{ */ typedef struct GColorSet { - color_t text; // @< The text color - color_t edge; // @< The edge color - color_t fill; // @< The fill color - color_t progress; // @< The color of progress bars + color_t text; /**< The text color */ + color_t edge; /**< The edge color */ + color_t fill; /**< The fill color */ + color_t progress; /**< The color of progress bars */ } GColorSet; /** @} */ @@ -48,10 +48,11 @@ typedef struct GColorSet { * @{ */ typedef struct GWidgetStyle { - color_t background; // @< The window background color - GColorSet enabled; // @< The colors when enabled - GColorSet disabled; // @< The colors when disabled - GColorSet pressed; // @< The colors when pressed + color_t background; /**< The window background color */ + color_t focus; /**< The color when a widget is focused */ + GColorSet enabled; /**< The colors when enabled */ + GColorSet disabled; /**< The colors when disabled */ + GColorSet pressed; /**< The colors when pressed */ } GWidgetStyle; /** @} */ @@ -92,13 +93,13 @@ typedef uint16_t WidgetTag; * @{ */ typedef struct GWidgetInit { - GWindowInit g; // @< The GWIN initializer - const char * text; // @< The initial text - CustomWidgetDrawFunction customDraw; // @< A custom draw function - use NULL for the standard - void * customParam; // @< A parameter for the custom draw function (default = NULL) - const GWidgetStyle * customStyle; // @< A custom style to use - use NULL for the default style + GWindowInit g; /**< The GWIN initializer */ + const char * text; /**< The initial text */ + CustomWidgetDrawFunction customDraw; /**< A custom draw function - use NULL for the standard */ + void * customParam; /**< A parameter for the custom draw function (default = NULL) */ + const GWidgetStyle * customStyle; /**< A custom style to use - use NULL for the default style */ #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) - WidgetTag tag; // @< The tag to associate with the widget + WidgetTag tag; /**< The tag to associate with the widget */ #endif } GWidgetInit; /** @} */ @@ -113,13 +114,13 @@ typedef struct GWidgetInit { * @{ */ typedef struct GWidgetObject { - GWindowObject g; // @< This is still a GWIN - const char * text; // @< The widget text - CustomWidgetDrawFunction fnDraw; // @< The current draw function - void * fnParam; // @< A parameter for the current draw function - const GWidgetStyle * pstyle; // @< The current widget style colors + GWindowObject g; /**< This is still a GWIN */ + const char * text; /**< The widget text */ + CustomWidgetDrawFunction fnDraw; /**< The current draw function */ + void * fnParam; /**< A parameter for the current draw function */ + const GWidgetStyle * pstyle; /**< The current widget style colors */ #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) - WidgetTag tag; // @< The widget tag + WidgetTag tag; /**< The widget tag */ #endif } GWidgetObject; /** @} */ @@ -144,10 +145,10 @@ typedef struct GWidgetObject { * @{ */ typedef struct GEventGWin { - GEventType type; // The type of this event - GHandle gwin; // The gwin window handle + GEventType type; /**< The type of this event */ + GHandle gwin; /**< The gwin window handle */ #if GWIN_NEED_WIDGET && GWIN_WIDGET_TAGS - WidgetTag tag; // The tag (if applicable) + WidgetTag tag; /**< The tag (if applicable) */ #endif } GEventGWin; /** @} */ @@ -228,6 +229,17 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc); */ const char *gwinGetText(GHandle gh); +/** + * @brief Check whether a handles is a widget handle or not + * + * @param[in] gh The handle to check. + * + * @return TRUE if the passed handle is a widget handle. FALSE otherwise. + * + * @api + */ +bool_t gwinIsWidget(GHandle gh); + #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) /** * @brief Set the tag of a widget. @@ -344,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) || GWIN_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 @@ -381,5 +423,9 @@ bool_t gwinAttachListener(GListener *pl); #include "gwin_keyboard.h" #endif +#if GWIN_NEED_TEXTEDIT || defined(__DOXYGEN__) + #include "gwin_textedit.h" +#endif + #endif /* _GWIDGET_H */ /** @} */ diff --git a/src/gwin/gwin_wm.c b/src/gwin/gwin_wm.c index 00365c36..ef7333fa 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 gfxSem gwinsem; static gfxQueueASync _GWINList; #if GWIN_NEED_FLASHING @@ -331,14 +330,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 @@ -346,6 +354,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; @@ -455,6 +467,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(); } @@ -462,6 +478,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(); } @@ -482,6 +502,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); } } @@ -495,6 +519,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); } } @@ -506,11 +534,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); } }