diff options
34 files changed, 1140 insertions, 149 deletions
| 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 <joel@unormal.org> + * 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 <organization> 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 <COPYRIGHT HOLDER> 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 <joel@unormal.org> + * 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 <organization> 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 <COPYRIGHT HOLDER> 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 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/docs/releases.txt b/docs/releases.txt index b1540c26..7c49f185 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -10,6 +10,12 @@ FEATURE:	New board STM32F746G-Discovery  FEATURE:	New gdisp driver STM32LTDC  FEATURE:	Better support for Raw32 platforms  FEATURE:	Renaming GFX_NO_OS_INIT to GFX_OS_NO_INIT +FEATURE:    Implementing widget focusing. See gwinSetFocus() and gwinGetFocus() +FEATURE:    Adding more font metrics (BaselineX and BaselineY) +FEATURE:    Adding gdispGetStringWidthCount() +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 *** diff --git a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c index 9c2de092..0fb8ba5a 100644 --- a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c +++ b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c @@ -316,10 +316,8 @@ LLDSPEC	color_t gdisp_lld_get_pixel_color(GDisplay* g)  		switch(g->p.x) {  		case GDISP_CONTROL_POWER:  			// Don't do anything if it is the same power mode -			if (g->g.Powermode == (powermode_t)g->p.ptr) { +			if (g->g.Powermode == (powermode_t)g->p.ptr)  				return; -			} -  			switch((powermode_t)g->p.ptr) {  			default:  				return; @@ -329,11 +327,8 @@ LLDSPEC	color_t gdisp_lld_get_pixel_color(GDisplay* g)  			return;  		case GDISP_CONTROL_ORIENTATION: -			// Don't do anything if it is the same power mode -			if (g->g.Orientation == (orientation_t)g->p.ptr) { +			if (g->g.Orientation == (orientation_t)g->p.ptr)  				return; -			} -  			switch((orientation_t)g->p.ptr) {  				case GDISP_ROTATE_0:  				case GDISP_ROTATE_180: diff --git a/gfxconf.example.h b/gfxconf.example.h index 27c55a2a..d2582df9 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -167,6 +167,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 @@ -186,6 +187,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/gdisp/gdisp.c b/src/gdisp/gdisp.c index 377ccddb..0ddee1f6 100644 --- a/src/gdisp/gdisp.c +++ b/src/gdisp/gdisp.c @@ -3313,6 +3313,8 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co  		case fontCharPadding:		return 0;  		case fontMinWidth:			return font->min_x_advance;  		case fontMaxWidth:			return font->max_x_advance; +		case fontBaselineX:			return font->baseline_x; +		case fontBaselineY:			return font->baseline_y;  		}  		return 0;  	} @@ -3322,12 +3324,20 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co  		return mf_character_width(font, c);  	} -	coord_t gdispGetStringWidth(const char* str, font_t font) { +	coord_t gdispGetStringWidthCount(const char* str, font_t font, uint16_t count) {  		if (!str)  			return 0; -		/* No mutex required as we only read static data */ -		return mf_get_string_width(font, str, 0, 0); +		// No mutex required as we only read static data +		#if GDISP_NEED_TEXT_KERNING +			return mf_get_string_width(font, str, count, TRUE); +		#else +			return mf_get_string_width(font, str, count, FALSE); +		#endif +	} + +	coord_t gdispGetStringWidth(const char* str, font_t font) { +		return gdispGetStringWidthCount(str, font, 0);  	}  #endif diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index 4513f3f7..d0c31ebb 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -72,7 +72,9 @@ typedef enum fontmetric {  	fontLineSpacing,		/**< The line spacing */  	fontCharPadding,		/**< The char padding */  	fontMinWidth,			/**< The minimum width */ -	fontMaxWidth			/**< The maximum width */ +	fontMaxWidth,			/**< The maximum width */ +	fontBaselineX,			/**< The base line in x direction */ +	fontBaselineY			/**< The base line in y direction */  } fontmetric_t;  /** @@ -109,7 +111,7 @@ typedef enum powermode {  /*   * Our black box display structure.   */ -typedef struct GDisplay		GDisplay; +typedef struct GDisplay GDisplay;  /**   * @brief   The default screen to use for the gdispXXXX calls. @@ -976,7 +978,22 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co  	coord_t gdispGetCharWidth(char c, font_t font);  	/** -	 * @brief   Get the pixel width of a string. +	 * @brief   Get the pixel width of a string of a given character length. +	 * @return  The width of the string in pixels. +	 * @pre		GDISP_NEED_TEXT must be TRUE in your gfxconf.h +	 * +	 * @note	Passing 0 to count has the same effect as calling gdispGetStringWidt() +	 * +	 * @param[in] str     The string to measure +	 * @param[in] font    The font to use +	 * @param[in] count   The number of characters to take into account +	 * +	 * @api +	 */ +	coord_t gdispGetStringWidthCount(const char* str, font_t font, uint16_t count); + +	/** +	 * @brief   Get the pixel width of an entire string.  	 * @return  The width of the string in pixels.  	 * @pre		GDISP_NEED_TEXT must be TRUE in your gfxconf.h  	 * 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/ginput/ginput_keyboard_microcode.c b/src/ginput/ginput_keyboard_microcode.c index 167ed5a4..9db0459b 100644 --- a/src/ginput/ginput_keyboard_microcode.c +++ b/src/ginput/ginput_keyboard_microcode.c @@ -14,7 +14,7 @@  #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF -#include "keyboard_microcode.h" +#include "ginput_keyboard_microcode.h"  #if GKEYBOARD_LAYOUT_SCANCODE2_US diff --git a/src/gqueue/gqueue.h b/src/gqueue/gqueue.h index 924024b7..40a41bce 100644 --- a/src/gqueue/gqueue.h +++ b/src/gqueue/gqueue.h @@ -385,7 +385,7 @@ GDataBuffer *gfxBufferGetI(void);  /** @} */  /** - * @name		BufferRelease) Functions + * @name		BufferRelease() Functions   * @brief		Release a buffer back to the free list   *   * @param[in] pd		The buffer to put (back) on the free-list. diff --git a/src/gwin/gwin.h b/src/gwin/gwin.h index 4a7d02bf..cc0d44f1 100644 --- a/src/gwin/gwin.h +++ b/src/gwin/gwin.h @@ -38,19 +38,22 @@ typedef struct GWindowObject *GHandle;  typedef struct GWindowObject {  	#if GWIN_NEED_WINDOWMANAGER  		// This MUST be the first member of the structure -		gfxQueueASyncItem	wmq;				// @< The next window (for the window manager) +		gfxQueueASyncItem	wmq;				/**< The next window (for the window manager) */  	#endif -	const struct gwinVMT	*vmt;				// @< The VMT for this GWIN -	GDisplay *				display;			// @< The display this window is on. -	coord_t					x, y;				// @< Screen relative position -	coord_t					width, height;		// @< Dimensions of this window -	color_t					color, bgcolor;		// @< The current drawing colors -	uint32_t				flags;				// @< Window flags (the meaning is private to the GWIN class) +	const struct gwinVMT*	vmt;				/**< The VMT for this GWIN */ +	GDisplay *				display;			/**< The display this window is on */ +	coord_t					x;					/**< The position relative to the screen */ +	coord_t					y;					/**< The position relative to the screen */ +	coord_t					width;				/**< The width of this window */ +	coord_t					height;				/**< The height of this window */ +	color_t					color;				/**< The current foreground drawing color */ +	color_t					bgcolor;			/**< The current background drawing color */ +	uint32_t				flags;				/**< Window flags (the meaning is private to the GWIN class) */  	#if GDISP_NEED_TEXT -		font_t				font;				// @< The current font +		font_t				font;				/**< The current font */  	#endif  	#if GWIN_NEED_CONTAINERS -		GHandle				parent;				// @< The parent window +		GHandle				parent;				/**< The parent window */  	#endif  } GWindowObject, * GHandle;  /** @} */ @@ -68,11 +71,13 @@ typedef struct GWindowObject {   * @{   */  typedef struct GWindowInit { -	coord_t			x, y;							// @< The initial position relative to its parent -	coord_t			width, height;					// @< The initial dimension -	bool_t			show;							// @< Should the window be visible initially +	coord_t			x;								/**< The initial position relative to its parent */ +	coord_t			y;								/**< The initial position relative to its parent */ +	coord_t			width;							/**< The width */ +	coord_t			height;							/**< The height */ +	bool_t			show;							/**< Should the window be visible initially */  	#if GWIN_NEED_CONTAINERS -		GHandle		parent;							// @< The parent - must be a container or NULL +		GHandle		parent;							/**< The parent - must be a container or NULL */  	#endif  } GWindowInit;  /** @} */ 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..8aa5b9ff 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,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__)  		/** @@ -330,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_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 @@ -45,6 +45,13 @@  		#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..c1373897 --- /dev/null +++ b/src/gwin/gwin_textedit.c @@ -0,0 +1,244 @@ +/* + * 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 <string.h> + +// Some settings +const int TEXT_PADDING_LEFT			= 4; +const int FOCUS_BORDER_THICKNESS	= 3; +const int CURSOR_PADDING_LEFT		= 0; +const int CURSOR_EXTRA_HEIGHT		= 1; + +// Macros to assist in data type conversions +#define gh2obj ((GTexteditObject *)gh) +#define gw2obj ((GTexteditObject *)gw) + +// cursorPos is the position of the next character +// textBuffer[cursorPos++] = readKey(); + +static void _shiftTextLeft(char* buffer, size_t maxSize, size_t index) +{ +	// Find the end of the string +	size_t indexTerminator = index; +	while (buffer[indexTerminator] != '\0' && indexTerminator < maxSize-1) { +		indexTerminator++; +	} + +	// Shift +	memcpy(&buffer[index-1], &buffer[index], indexTerminator-index); + +	// Terminate the string +	buffer[indexTerminator-1] = '\0'; +} + +static void _shiftTextRight(char* buffer, size_t maxSize, size_t index, char fillChar) +{ +	// Find the end of the string +	size_t indexTerminator = index; +	while (buffer[indexTerminator] != '\0' && indexTerminator < maxSize-1) { +		indexTerminator++; +	} + +	// Shift +	memcpy(&buffer[index+1], &buffer[index], indexTerminator-index); + +	// Fill the gap +	buffer[index] = fillChar; +} + + +#if 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) { +			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 > 0) { +					gw2obj->cursorPos--; +				} +				break; + +			case GKEY_RIGHT: +				if (gw2obj->cursorPos < strlen(gw2obj->textBuffer)) { +					gw2obj->cursorPos++; +				} +				break; + + +			default: +				break; +			} +		} + +		// Parse the key press +		else if (pke->bytecount >= 1) { +			// Is it backspace? +			if (pke->c[0] == GKEY_BACKSPACE) { +				if (gw2obj->cursorPos == 0) { +					return; +				} +				_shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos--); +			} + +			// Is it delete? +			else if (pke->c[0] == GKEY_DEL) { +				// Check whether there is a character on the right side of the cursor +				if (gw2obj->textBuffer[gw2obj->cursorPos] == '\0') { +					return; +				} +				_shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos+1); +			} + +			// Add a new character +			else { +				// Prevent buffer overflow +				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->maxSize, gw2obj->cursorPos++, pke->c[0]); +			} + +			// Set the new text +			gwinSetText((GHandle)gw, gw2obj->textBuffer, FALSE); +		} + +		_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 mose down events (NOT USED) +			0,						// Process mouse up events (NOT USED) +			0,						// Process mouse move events (NOT USED) +		}, +	#endif +	#if 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) +{ +	// Create the underlying widget +	if (!(wt = (GTexteditObject*)_gwidgetCreate(g, &wt->w, pInit, &texteditVMT))) +		return 0; + +	// Allocate the text buffer +	wt->maxSize = maxSize; +	wt->textBuffer = gfxAlloc(wt->maxSize); +	if (wt->textBuffer == 0) +		return 0; + +	// Initialize the text buffer +	size_t i = 0; +	for (i = 0; i < wt->maxSize; i++) { +		wt->textBuffer[i] = '\0'; +	} + +	// Set text and cursor position +	strncpy(wt->textBuffer, gwinGetText((GHandle)wt), wt->maxSize);	// FixMe: pInit->text leads to a segfault +	wt->cursorPos = strlen(wt->textBuffer); + +	gwinSetVisible(&wt->w.g, pInit->g.show); + +	return (GHandle)wt; +} + +static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) +{ + +	(void)param; + +	// Is it a valid handle? +	if (gw->g.vmt != (gwinVMT*)&texteditVMT) { +		return; +	} + +	// Retrieve colors +	color_t textColor = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; +	color_t cursorColor = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge; + +	// Render background and string +	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 (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 +	_gwidgetDrawFocusRect(gw, 1, 1, gw->g.width-2, gw->g.height-2); + +	// Render cursor (if focused) +	if (gwinGetFocus() == (GHandle)gw) { +		// Calculate cursor stuff +		coord_t textWidth = gdispGetStringWidthCount(gw2obj->textBuffer, gw->g.font, gw2obj->cursorPos); +		coord_t cursorHeight = gdispGetFontMetric(gw->g.font, fontHeight); +		coord_t cursorSpacingTop = (gw->g.height - cursorHeight)/2 - CURSOR_EXTRA_HEIGHT; +		coord_t cursorSpacingBottom = (gw->g.height - cursorHeight)/2 - CURSOR_EXTRA_HEIGHT; + +		// Draw cursor +		coord_t lineX0 = gw->g.x + textWidth + CURSOR_PADDING_LEFT + TEXT_PADDING_LEFT + gdispGetFontMetric(gw->g.font, fontBaselineX)/2; +		coord_t lineX1 = lineX0; +		coord_t lineY0 = gw->g.y + cursorSpacingTop; +		coord_t lineY1 = gw->g.y + gw->g.height - cursorSpacingBottom; +		gdispGDrawLine(gw->g.display, lineX0, lineY0, lineX1, lineY1, cursorColor); +	} +} + +#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..f83233b4 --- /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. + * + * @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..162d41e3 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,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);  			} @@ -142,6 +159,21 @@ static void gwidgetEvent(void *param, GEvent *pe) {  		break;  	#endif +	#if GFX_USE_GINPUT && GINPUT_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 +223,103 @@ static void gwidgetEvent(void *param, GEvent *pe) {  	#undef pme  	#undef pte +	#undef pke  	#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;			- 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 +361,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 +397,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 +495,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 +531,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..5239b6b8 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) || 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);  			}  		} | 
