aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorinmarket <andrewh@inmarket.com.au>2015-04-12 17:07:38 +1000
committerinmarket <andrewh@inmarket.com.au>2015-04-12 17:07:38 +1000
commit1ce3f20fa49830fee1a03d1682214dda81e8548a (patch)
tree9342474d224298087a57d94a644d6b793181df17
parentb04148ca2ba1d31a333473459ccca3ea056e43b2 (diff)
downloaduGFX-1ce3f20fa49830fee1a03d1682214dda81e8548a.tar.gz
uGFX-1ce3f20fa49830fee1a03d1682214dda81e8548a.tar.bz2
uGFX-1ce3f20fa49830fee1a03d1682214dda81e8548a.zip
Add GWIN virtual keyboard widget
-rw-r--r--demos/modules/gwin/keyboard/demo.mk3
-rw-r--r--demos/modules/gwin/keyboard/gfxconf.h55
-rw-r--r--demos/modules/gwin/keyboard/main.c140
-rw-r--r--demos/modules/gwin/keyboard/readme.txt1
-rw-r--r--docs/releases.txt1
-rw-r--r--gfxconf.example.h3
-rw-r--r--src/ginput/ginput_keyboard.h6
-rw-r--r--src/gwin/gwin.mk2
-rw-r--r--src/gwin/gwin_keyboard.c471
-rw-r--r--src/gwin/gwin_keyboard.h111
-rw-r--r--src/gwin/gwin_keyboard_layout.c45
-rw-r--r--src/gwin/gwin_keyboard_layout.h69
-rw-r--r--src/gwin/gwin_options.h28
-rw-r--r--src/gwin/gwin_rules.h2
-rw-r--r--src/gwin/gwin_widget.h4
15 files changed, 939 insertions, 2 deletions
diff --git a/demos/modules/gwin/keyboard/demo.mk b/demos/modules/gwin/keyboard/demo.mk
new file mode 100644
index 00000000..ae1bf7c1
--- /dev/null
+++ b/demos/modules/gwin/keyboard/demo.mk
@@ -0,0 +1,3 @@
+DEMODIR = $(GFXLIB)/demos/modules/gwin/keyboard
+GFXINC += $(DEMODIR)
+GFXSRC += $(DEMODIR)/main.c
diff --git a/demos/modules/gwin/keyboard/gfxconf.h b/demos/modules/gwin/keyboard/gfxconf.h
new file mode 100644
index 00000000..bfae0e7c
--- /dev/null
+++ b/demos/modules/gwin/keyboard/gfxconf.h
@@ -0,0 +1,55 @@
+/**
+ * This file has a different license to the rest of the uGFX system.
+ * You can copy, modify and distribute this file as you see fit.
+ * You do not need to publish your source modifications to this file.
+ * The only thing you are not permitted to do is to relicense it
+ * under a different license.
+ */
+
+/**
+ * Copy this file into your project directory and rename it as gfxconf.h
+ * Edit your copy to turn on the uGFX features you want to use.
+ * The values below are the defaults. You should delete anything
+ * you are leaving as default.
+ *
+ * Please use spaces instead of tabs in this file.
+ */
+
+#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 TRUE
+//#define GFX_USE_OS_WIN32 TRUE
+//#define GFX_USE_OS_LINUX TRUE
+//#define GFX_USE_OS_OSX TRUE
+
+#define GFX_USE_GDISP TRUE
+
+#define GDISP_NEED_VALIDATION TRUE
+#define GDISP_NEED_CLIP TRUE
+//#define GDISP_NEED_SCROLL TRUE
+#define GDISP_NEED_TEXT TRUE
+ #define GDISP_INCLUDE_FONT_UI2 TRUE
+
+//#define GDISP_NEED_CONTROL TRUE
+//#define GDISP_DEFAULT_ORIENTATION GDISP_ROTATE_LANDSCAPE
+#define GDISP_NEED_MULTITHREAD TRUE
+
+#define GFX_USE_GWIN TRUE
+#define GWIN_NEED_WINDOWMANAGER TRUE
+#define GWIN_NEED_CONSOLE TRUE
+#define GWIN_NEED_WIDGET TRUE
+ #define GWIN_NEED_KEYBOARD TRUE
+
+#define GFX_USE_GEVENT TRUE
+#define GFX_USE_GTIMER TRUE
+
+#define GFX_USE_GQUEUE TRUE
+#define GQUEUE_NEED_ASYNC TRUE
+
+#define GFX_USE_GINPUT TRUE
+#define GINPUT_NEED_MOUSE TRUE
+//#define GINPUT_NEED_KEYBOARD TRUE
+
+#endif /* _GFXCONF_H */
diff --git a/demos/modules/gwin/keyboard/main.c b/demos/modules/gwin/keyboard/main.c
new file mode 100644
index 00000000..47a10fc0
--- /dev/null
+++ b/demos/modules/gwin/keyboard/main.c
@@ -0,0 +1,140 @@
+/*
+ * 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"
+
+/* The variables we need */
+static font_t font;
+static GListener gl;
+static GHandle ghConsole;
+static GHandle ghKeyboard;
+
+
+/**
+ * Create the widgets.
+ */
+static void createWidgets(void) {
+ GWidgetInit wi;
+
+ gwinWidgetClearInit(&wi);
+
+ // Create the console - set colors before making it visible
+ wi.g.show = FALSE;
+ wi.g.x = 0; wi.g.y = 0;
+ wi.g.width = gdispGetWidth(); wi.g.height = gdispGetHeight()/2;
+ ghConsole = gwinConsoleCreate(0, &wi.g);
+ gwinSetColor(ghConsole, Black);
+ gwinSetBgColor(ghConsole, HTML2COLOR(0xF0F0F0));
+ gwinShow(ghConsole);
+ gwinClear(ghConsole);
+
+ // Create the keyboard
+ wi.g.show = TRUE;
+ wi.g.x = 0; wi.g.y = gdispGetHeight()/2;
+ wi.g.width = gdispGetWidth(); wi.g.height = gdispGetHeight()/2;
+ ghKeyboard = gwinKeyboardCreate(0, &wi);
+}
+
+int main(void) {
+ GEvent * pe;
+ GEventKeyboard * pk;
+ unsigned i;
+
+ // Initialize the display
+ gfxInit();
+
+ // Set the widget defaults
+ font = gdispOpenFont("*"); // Get the first defined font.
+ gwinSetDefaultFont(font);
+ gwinSetDefaultStyle(&WhiteWidgetStyle, FALSE);
+ gdispClear(White);
+
+ // Create the gwin windows/widgets
+ createWidgets();
+
+ // We want to listen for widget events
+ geventListenerInit(&gl);
+ gwinAttachListener(&gl);
+
+ // We also want to listen to keyboard events from the virtual keyboard
+ geventAttachSource(&gl, gwinKeyboardGetEventSource(ghKeyboard), GLISTEN_KEYTRANSITIONS|GLISTEN_KEYUP);
+
+ while(1) {
+ // Get an Event
+ pe = geventEventWait(&gl, TIME_INFINITE);
+
+ switch(pe->type) {
+ case GEVENT_GWIN_KEYBOARD:
+ // This is a widget event generated on the standard gwin event source
+ gwinPrintf(ghConsole, "Keyboard visibility has changed\n");
+ break;
+
+ case GEVENT_KEYBOARD:
+ // This is a keyboard event from a keyboard source which must be separately listened to.
+ // It is not sent on the gwin event source even though in this case it was generated by a gwin widget.
+ pk = (GEventKeyboard *)pe;
+
+ 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");
+ break;
+
+ default:
+ gwinPrintf(ghConsole, "Unknown %d\n", pe->type);
+ break;
+ }
+ }
+ return 0;
+}
+
diff --git a/demos/modules/gwin/keyboard/readme.txt b/demos/modules/gwin/keyboard/readme.txt
new file mode 100644
index 00000000..e960716d
--- /dev/null
+++ b/demos/modules/gwin/keyboard/readme.txt
@@ -0,0 +1 @@
+This demo demonstrates the virtual keyboard.
diff --git a/docs/releases.txt b/docs/releases.txt
index 548386dc..8331c6b3 100644
--- a/docs/releases.txt
+++ b/docs/releases.txt
@@ -36,6 +36,7 @@ FEATURE: Added SSD1331 gdisp driver
FEATURE: Added arduino as a GOS supported operating system
FEATURE: Added additional pixel format's
FIX: Color components fixed for some strange compilers
+FEATURE: Add GWIN virtual keyboard widget
*** Release 2.2 ***
FEATURE: Added nested containers demo
diff --git a/gfxconf.example.h b/gfxconf.example.h
index 2d3568d1..6b2f6d9d 100644
--- a/gfxconf.example.h
+++ b/gfxconf.example.h
@@ -180,6 +180,9 @@
// #define GWIN_NEED_LIST_IMAGES FALSE
// #define GWIN_NEED_PROGRESSBAR FALSE
// #define GWIN_PROGRESSBAR_AUTO FALSE
+// #define GWIN_NEED_KEYBOARD FALSE
+// #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1
+// #define GWIN_NEED_KEYBOARD_ENGLISH1 TRUE
// #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 ea218af7..1349092f 100644
--- a/src/ginput/ginput_keyboard.h
+++ b/src/ginput/ginput_keyboard.h
@@ -17,7 +17,7 @@
#ifndef _GINPUT_KEYBOARD_H
#define _GINPUT_KEYBOARD_H
-#if GINPUT_NEED_KEYBOARD || defined(__DOXYGEN__)
+#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD || defined(__DOXYGEN__)
/*===========================================================================*/
/* Type definitions */
@@ -164,6 +164,10 @@ 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)
diff --git a/src/gwin/gwin.mk b/src/gwin/gwin.mk
index 23993346..32a0ef95 100644
--- a/src/gwin/gwin.mk
+++ b/src/gwin/gwin.mk
@@ -15,5 +15,7 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin.c \
$(GFXLIB)/src/gwin/gwin_frame.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
GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include
diff --git a/src/gwin/gwin_keyboard.c b/src/gwin/gwin_keyboard.c
new file mode 100644
index 00000000..6b9ab524
--- /dev/null
+++ b/src/gwin/gwin_keyboard.c
@@ -0,0 +1,471 @@
+/*
+ * 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_keyboard.c
+ * @brief GWIN sub-system virtual keyboard code
+ */
+
+#include "gfx.h"
+
+#if GFX_USE_GWIN && GWIN_NEED_KEYBOARD
+
+#include "gwin_class.h"
+#include "gwin_keyboard_layout.h"
+
+
+#define GKEYBOARD_FLG_REVERTSET (GWIN_FIRST_CONTROL_FLAG<<0)
+#define GKEYBOARD_FLG_QUICKUPDATE (GWIN_FIRST_CONTROL_FLAG<<1)
+
+#define BAD_ROWCOL 255
+
+typedef uint8_t utf8;
+typedef uint16_t utf16;
+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;
+
+// Get the length of a UTF-8 string
+static int UTF8StrLen(const utf8 *s) {
+ int len;
+
+ len = 0;
+ if (s) {
+ while (*s) {
+ len++;
+ if (!(s[0] & 0x80))
+ s++;
+ else if ((s[0] & 0xE0) == 0xC0 && (s[1] & 0xC0) == 0x80)
+ s+=2;
+ else if ((s[0] & 0xF0) == 0xE0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80)
+ s+=3;
+ else if ((s[0] & 0xF8) == 0xF0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80)
+ s+=4;
+ else
+ // Invalid UTF-8 sequence - assume a single byte
+ s++;
+ }
+ }
+ return len;
+}
+
+// Return the nth character of a UTF8 string
+static ucode UTF8CharAt(const utf8 *s, int n) {
+ ucode u;
+
+ u = 0;
+ if (!s) return 0;
+
+ while(*s) {
+ if (!(s[0] & 0x80)) {
+ u = s[0];
+ s++;
+ } else if ((s[0] & 0xE0) == 0xC0 && (s[1] & 0xC0) == 0x80) {
+ u = s[1] | ((ucode)s[0] << 8);
+ s+=2;
+ } else if ((s[0] & 0xF0) == 0xE0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) {
+ u = s[2] | ((ucode)s[1] << 8) | ((ucode)s[0] << 16);
+ s+=3;
+ } else if ((s[0] & 0xF8) == 0xF0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) {
+ u = s[3] | ((ucode)s[2] << 8) | ((ucode)s[1] << 16) | ((ucode)s[0] << 24);
+ s+=4;
+ } else {
+ // Invalid UTF-8 sequence - assume a single byte
+ u = s[0];
+ s++;
+ }
+ if (--n < 0)
+ return u;
+ }
+ return 0;
+}
+
+// Convert a ucode to a UTF8 string (with no NULL on the end). Returns the number of bytes.
+static unsigned UCode2UTF8(utf8 *dst, ucode u) {
+ if (!(u & 0xFFFFFF00)) {
+ dst[0] = u;
+ return 1;
+ }
+ if (!(u & 0xFFFF0000)) {
+ dst[0] = u >> 8;
+ dst[1] = u;
+ return 2;
+ }
+ if (!(u & 0xFF000000)) {
+ dst[0] = u >> 16;
+ dst[1] = u >> 8;
+ dst[2] = u;
+ return 3;
+ }
+ dst[0] = u >> 24;
+ dst[1] = u >> 16;
+ dst[2] = u >> 8;
+ dst[3] = u;
+ return 4;
+}
+
+static int NumKeyRows(const char **keyset) {
+ int len;
+
+ len = 0;
+ while(*keyset++)
+ len++;
+ return len;
+}
+
+static void SendKeyboardEventToListener(GSourceListener *psl, GKeyboardObject *gk) {
+ GEventKeyboard *pe;
+ const GVSpecialKey *skey;
+ unsigned i;
+
+ // If there is no event buffer just mark a missed event
+ if (!(pe = (GEventKeyboard *)geventGetEventBuffer(psl))) {
+ // This listener is missing - save the meta events that have happened
+ psl->srcflags |= GKEYSTATE_MISSED_EVENT;
+ return;
+ }
+
+ // The virtual keyboard can't generate repeats
+ //if ((psl->listenflags & GLISTEN_KEYREPEATSOFF) && (k->keystate & GKEYSTATE_REPEAT))
+ // return;
+
+ // The virtual keyboard can't generate special keys
+ //if ((psl->listenflags & GLISTEN_KEYNOSPECIALS) && (k->keystate & GKEYSTATE_SPECIAL))
+ // return;
+
+ // The virtual keyboard treats a key release as a keydown
+ //if (!(psl->listenflags & GLISTEN_KEYUP) && (k->keystate & GKEYSTATE_KEYUP))
+ // k->cntc = 0;
+
+ // The virtual keyboard has no transitions
+ //if (!(psl->listenflags & GLISTEN_KEYTRANSITIONS) && !k->cntc)
+ // return;
+
+ pe->type = GEVENT_KEYBOARD;
+ if (gk->key < 0x20) {
+ skey = &gk->keytable->skeys[gk->key-1];
+ for(i=0; skey->sendkey[i]; i++)
+ pe->c[i] = skey->sendkey[i];
+ } else
+ i = UCode2UTF8((utf8 *)pe->c, gk->key);
+ pe->bytecount = i;
+ for(; i < 8; i++)
+ pe->c[i] = 0;
+ pe->keystate = psl->srcflags;
+ psl->srcflags = 0;
+ geventSendEvent(psl);
+}
+
+static void SendKeyboardEvent(GKeyboardObject *gk) {
+ GSourceListener *psl;
+
+ // Send to the keyboard specific source listeners
+ psl = 0;
+ while ((psl = geventGetSourceListener((GSourceHandle)gk, psl)))
+ SendKeyboardEventToListener(psl, gk);
+}
+
+
+#if GINPUT_NEED_MOUSE
+ // Find the key from the keyset and the x, y position
+ static void FindKey(GKeyboardObject *gk, coord_t x, coord_t y) {
+ const utf8 *krow;
+ fixed f;
+ int idx;
+
+ if (x < 0 || y < 0 || x >= gk->w.g.width || y >= gk->w.g.height) {
+ gk->keyrow = gk->keycol = BAD_ROWCOL;
+ return;
+ }
+
+ // Get the y parameters
+ f = FIXED(gk->w.g.height) / NumKeyRows(gk->keyset);
+ gk->keyrow = FIXED(y) / f;
+ gk->keyy = NONFIXED(f * gk->keyrow + FIXED0_5);
+ gk->keycy = NONFIXED(f * (gk->keyrow+1) + FIXED0_5) - gk->keyy;
+
+ // Get the current row
+ krow = (const utf8 *)gk->keyset[gk->keyrow];
+
+ // Get the x parameters
+ f = FIXED(gk->w.g.width) / UTF8StrLen(krow);
+ gk->keycol = FIXED(x) / f;
+
+ // Get the key
+ gk->key = UTF8CharAt(krow, gk->keycol);
+
+ // Amalgamate identical keys into one big key
+ idx = gk->keycol;
+ while(gk->keycol > 0 && UTF8CharAt(krow, gk->keycol-1) == gk->key)
+ gk->keycol--;
+ while(UTF8CharAt(krow, ++idx) == gk->key);
+ gk->keyx = NONFIXED(f * gk->keycol + FIXED0_5);
+ gk->keycx = NONFIXED(f * idx + FIXED0_5) - gk->keyx;
+ }
+
+ // A mouse up has occurred (it may or may not be over the button)
+ static void MouseUp(GWidgetObject *gw, coord_t x, coord_t y) {
+ #define gk ((GKeyboardObject *)gw)
+
+ FindKey(gk, x, y);
+
+ // Do we have a valid key?
+ if (gk->keyrow == BAD_ROWCOL) {
+ if (gk->lastkeyrow != BAD_ROWCOL) {
+ gw->g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
+ _gwinUpdate((GHandle)gw);
+ }
+ return;
+ }
+
+ // We are turning off the display of the key
+ gk->keyrow = gk->keycol = BAD_ROWCOL;
+
+ // Is this one of the special keys
+ if (gk->key < 0x20) {
+ // This is a special key
+ const GVSpecialKey *skey;
+
+ skey = &gk->keytable->skeys[gk->key - 1];
+
+ if ((skey->flags & GVKEY_SINGLESET)) {
+ // Single character switch to a new layout
+ gk->keyset = gk->keytable->ksets[skey->newset];
+ gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
+ gk->w.g.flags |= GKEYBOARD_FLG_REVERTSET;
+
+ } else if ((skey->flags & GVKEY_LOCKSET)) {
+ // Locked switch to a new layout
+ gk->keyset = gk->keytable->ksets[skey->newset];
+ gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
+
+ } else if ((gk->w.g.flags & GKEYBOARD_FLG_REVERTSET)) {
+ // Revert to default layout
+ gk->keyset = gk->keytable->ksets[0];
+ gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
+
+ } else {
+ // Just turning off a key
+ gw->g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
+ }
+
+ // Send the key if required
+ if (skey->sendkey && skey->sendkey[0])
+ SendKeyboardEvent(gk);
+
+ // Update the display
+ _gwinUpdate((GHandle)gw);
+
+ return;
+ }
+
+ // Do we need to revert to the standard layout?
+ if ((gk->w.g.flags & GKEYBOARD_FLG_REVERTSET)) {
+ gk->keyset = gk->keytable->ksets[0];
+ gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
+ } else {
+ gw->g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
+ }
+
+ // Send the key
+ SendKeyboardEvent(gk);
+
+ // Update the display
+ _gwinUpdate((GHandle)gw);
+ }
+
+ // A mouse move has occurred (it may or may not be over the button)
+ static void MouseMove(GWidgetObject *gw, coord_t x, coord_t y) {
+ #define gk ((GKeyboardObject *)gw)
+
+ FindKey(gk, x, y);
+
+ if (gk->keyrow != gk->lastkeyrow || gk->keycol != gk->lastkeycol) {
+ gk->w.g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
+ _gwinUpdate((GHandle)gw);
+ }
+ #undef gk
+ }
+#endif
+
+extern GVKeyTable GWIN_KEYBOARD_DEFAULT_LAYOUT;
+void gwinKeyboardDraw_Normal(GWidgetObject *gw, void *param);
+
+// The button VMT table
+static const gwidgetVMT keyboardVMT = {
+ {
+ "VKeyboard", // The classname
+ sizeof(GKeyboardObject), // The object size
+ _gwidgetDestroy, // The destroy routine
+ _gwidgetRedraw, // The redraw routine
+ 0, // The after-clear routine
+ },
+ gwinKeyboardDraw_Normal, // The default drawing routine
+ #if GINPUT_NEED_MOUSE
+ {
+ MouseMove, // Process mouse down events
+ MouseUp, // Process mouse up events
+ MouseMove, // Process mouse move events
+ },
+ #endif
+ #if GINPUT_NEED_TOGGLE
+ {
+ 0, // No toggle roles
+ 0, // Assign Toggles
+ 0, // Get Toggles
+ 0, // Process toggle off events
+ 0, // Process toggle on events
+ },
+ #endif
+ #if GINPUT_NEED_DIAL
+ {
+ 0, // No dial roles
+ 0, // Assign Dials (NOT USED)
+ 0, // Get Dials (NOT USED)
+ 0, // Process dial move events (NOT USED)
+ },
+ #endif
+};
+
+GHandle gwinGKeyboardCreate(GDisplay *g, GKeyboardObject *gk, const GWidgetInit *pInit) {
+ if (!(gk = (GKeyboardObject *)_gwidgetCreate(g, &gk->w, pInit, &keyboardVMT)))
+ return 0;
+
+ gk->keytable = &GWIN_KEYBOARD_DEFAULT_LAYOUT;
+ gk->keyset = gk->keytable->ksets[0];
+ gk->lastkeyrow = gk->lastkeycol = gk->keyrow = gk->keycol = BAD_ROWCOL;
+ gwinSetVisible((GHandle)gk, pInit->g.show);
+ return (GHandle)gk;
+}
+
+GSourceHandle gwinKeyboardGetEventSource(GHandle gh) {
+ if (gh->vmt != (gwinVMT *)&keyboardVMT)
+ return 0;
+ return (GSourceHandle)gh;
+}
+
+void gwinKeyboardSetLayout(GHandle gh, struct GVKeyTable *layout) {
+ #define gk ((GKeyboardObject *)gh)
+
+ if (gh->vmt != (gwinVMT *)&keyboardVMT)
+ return;
+
+ if (!layout)
+ layout = &GWIN_KEYBOARD_DEFAULT_LAYOUT;
+ gk->keytable = layout;
+ gk->keyset = gk->keytable->ksets[0];
+ gk->lastkeyrow = gk->lastkeycol = gk->keyrow = gk->keycol = BAD_ROWCOL;
+ gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
+ gwinRedraw(gh);
+ #undef gk
+}
+
+/*----------------------------------------------------------
+ * Custom Draw Routines
+ *----------------------------------------------------------*/
+
+/*
+static const GColorSet *getDrawColors(GWidgetObject *gw) {
+ if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled;
+ if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed;
+ return &gw->pstyle->enabled;
+}
+*/
+
+void gwinKeyboardDraw_Normal(GWidgetObject *gw, void *param) {
+ #define gk ((GKeyboardObject *)gw)
+
+ char cap[5];
+ const char *pcap;
+ const utf8 *krow;
+ coord_t x, y, cx, cy;
+ uint8_t rows, cols, row, col, kcols;
+ ucode key;
+ fixed fx, fy;
+ const GColorSet *pcol;
+ (void) param;
+
+ if (gw->g.vmt != (gwinVMT *)&keyboardVMT) return;
+
+ // Get the y parameters
+ rows = NumKeyRows(gk->keyset);
+ fy = FIXED(gk->w.g.height) / rows;
+ for(row = 0; row < rows; row++) {
+ y = NONFIXED(fy * row + FIXED0_5);
+ cy = NONFIXED(fy * (row+1) + FIXED0_5) - y;
+
+ // Get the current row
+ krow = (const utf8 *)gk->keyset[row];
+
+ // Get the x parameters
+ cols = UTF8StrLen(krow);
+ fx = FIXED(gk->w.g.width) / cols;
+ for(col = 0; col < cols; col=kcols) {
+
+ // Choose the color
+ if (!(gk->w.g.flags & GWIN_FLG_SYSENABLED))
+ pcol = &gk->w.pstyle->disabled;
+ else if (gk->keyrow == row && gk->keycol == col)
+ pcol = &gk->w.pstyle->pressed;
+ else
+ pcol = &gk->w.pstyle->enabled;
+
+ // Get the key
+ key = UTF8CharAt(krow, col);
+
+ // Amalgamate identical keys into one big key
+ kcols = col+1;
+ while(UTF8CharAt(krow, kcols) == key)
+ kcols++;
+ x = NONFIXED(fx * col + FIXED0_5);
+ cx = NONFIXED(fx * kcols + FIXED0_5) - x;
+
+ if (key < 0x20) {
+ pcap = gk->keytable->skeys[key-1].keycap;
+ } else {
+ cap[UCode2UTF8((utf8 *)cap, key)] = 0;
+ pcap = cap;
+ }
+ switch(*pcap) {
+ case '\001': // Shift (up arrow)
+ gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2, gw->g.y+y+1, gw->g.x+x+1, gw->g.y+y+cy-1, pcol->text);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2, gw->g.y+y+1, gw->g.x+x+cx-1, gw->g.y+y+cy-1, pcol->text);
+ break;
+ case '\002': // Shift locked (up arrow - bold)
+ gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2, gw->g.y+y, gw->g.x+x+1, gw->g.y+y+cy-1, pcol->text);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2, gw->g.y+y, gw->g.x+x+cx-1, gw->g.y+y+cy-1, pcol->text);
+ gdispGDrawBox(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->edge);
+ break;
+ case '\t':
+ gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+1, gw->g.y+y+1, gw->g.x+x+cx-1, gw->g.y+y+cy/2, pcol->text);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+1, gw->g.y+y+cy-1, gw->g.x+x+cx-1, gw->g.y+y+cy/2, pcol->text);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+cx-1, gw->g.y+y+1, gw->g.x+x+cx-1, gw->g.y+y+cy-1, pcol->text);
+ break;
+ case '\b':
+ gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+1, gw->g.y+y+cy/2, gw->g.x+x+cx-1, gw->g.y+y+1, pcol->text);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+1, gw->g.y+y+cy/2, gw->g.x+x+cx-1, gw->g.y+y+cy-1, pcol->text);
+ break;
+ case '\r':
+ gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+1, gw->g.y+y+cy/2, gw->g.x+x+cx-1, gw->g.y+y+cy/2, pcol->text);
+ gdispGDrawLine(gw->g.display, gw->g.x+x+cx-1, gw->g.y+y+cy/2, gw->g.x+x+cx-1, gw->g.y+y+1, pcol->text);
+ break;
+ default:
+ gdispGFillStringBox(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcap, gw->g.font, pcol->text, pcol->fill, justifyCenter);
+ }
+ }
+ }
+
+ #undef gk
+}
+
+#endif /* GFX_USE_GWIN && GWIN_NEED_KEYBOARD */
diff --git a/src/gwin/gwin_keyboard.h b/src/gwin/gwin_keyboard.h
new file mode 100644
index 00000000..7dcff723
--- /dev/null
+++ b/src/gwin/gwin_keyboard.h
@@ -0,0 +1,111 @@
+/*
+ * 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_button.h
+ * @brief GWIN Graphic window subsystem header file.
+ *
+ * @defgroup Button Button
+ * @ingroup Widgets
+ *
+ * @details GWIN allows it to easily create buttons with different styles
+ * and check for different meta states such as: PRESSED, CLICKED,
+ * RELEASED etc.
+ *
+ * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h
+ * @pre GWIN_NEED_BUTTON must be set to TRUE in your gfxconf.h
+ * @{
+ */
+
+#ifndef _GWIN_KEYBOARD_H
+#define _GWIN_KEYBOARD_H
+
+/* This file is included within "src/gwin/gwin_widget.h" */
+
+/**
+ * @brief The Event Type for a Button Event
+ */
+#define GEVENT_GWIN_KEYBOARD (GEVENT_GWIN_CTRL_FIRST+6)
+
+/**
+ * @brief A Keyboard Event
+ * @note There are currently no GEventGWinButton listening flags - use 0 as the flags to @p gwinAttachListener()
+ */
+typedef GEventGWin GEventGWinKeyboard;
+
+struct keyinfo {
+};
+
+/**
+ * @brief The keyboard widget structure
+ * @note Do not use the members directly - treat it as a black-box.
+ */
+typedef struct GKeyboardObject {
+ GWidgetObject w;
+ const struct GVKeyTable *keytable;
+ const char **keyset;
+ coord_t keyx, keyy;
+ coord_t keycx, keycy;
+ uint8_t lastkeyrow, lastkeycol;
+ uint8_t keyrow, keycol;
+ uint32_t key;
+} GKeyboardObject;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Create a keyboard widget.
+ * @return NULL if there is no resultant drawing area, otherwise a window handle.
+ *
+ * @param[in] g The GDisplay to display this window on
+ * @param[in] gb The GKeyboardObject structure to initialise. If this is NULL the structure is dynamically allocated.
+ * @param[in] pInit The initialisation parameters
+ *
+ * @note The drawing color and the background color get set to the current defaults. If you haven't called
+ * @p gwinSetDefaultColor() or @p gwinSetDefaultBgColor() then these are White and Black respectively.
+ * @note The font gets set to the current default font. If you haven't called @p gwinSetDefaultFont() then there
+ * is no default font and text drawing operations will no nothing.
+ * @note A keyboard remembers its normal drawing state. If there is a window manager then it is automatically
+ * redrawn if the window is moved or its visibility state is changed.
+ * @note A keyboard supports mouse input.
+ *
+ * @api
+ */
+GHandle gwinGKeyboardCreate(GDisplay *g, GKeyboardObject *gb, const GWidgetInit *pInit);
+#define gwinKeyboardCreate(gb, pInit) gwinGKeyboardCreate(GDISP, gb, pInit)
+
+/**
+ * @brief Get the keyboard event source for a GWIN virtual keyboard
+ * @return The event source handle or NULL if this is not a virtual keyboard
+ *
+ * @param[in] gh The GWIN virtual keyboard
+ *
+ * @note Normal GINPUT Keyboard events are returned by this event source.
+ */
+GSourceHandle gwinKeyboardGetEventSource(GHandle gh);
+
+/**
+ * @brief Set the layout for the virtual keyboard
+ *
+ * @param[in] gh The GWIN virtual keyboard
+ * @param[in] layout The keyboard layout to use (described by gwin_keyboard_layout.h)
+ *
+ * @note Changing the layout resets the keyboard to key set 0 of the keyboard and cancels any
+ * pending shifts.
+ */
+void gwinKeyboardSetLayout(GHandle gh, struct GVKeyTable *layout);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GWIN_KEYBOARD_H */
+/** @} */
diff --git a/src/gwin/gwin_keyboard_layout.c b/src/gwin/gwin_keyboard_layout.c
new file mode 100644
index 00000000..05e08165
--- /dev/null
+++ b/src/gwin/gwin_keyboard_layout.c
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#include "gfx.h"
+
+#if GFX_USE_GWIN && GWIN_NEED_KEYBOARD
+
+#include "gwin_keyboard_layout.h"
+
+#if GWIN_NEED_KEYBOARD_ENGLISH1
+ /* For this keyboard mapping we use:
+ * Set 0 = Lowercase letters
+ * Set 1 = Uppercase letters (transient)
+ * Set 2 = Uppercase letters (locked)
+ * Set 3 = Numbers
+ * Set 4 = Symbols
+ */
+ static const GVSpecialKey Eng1SKeys[] = {
+ { "\001", 0, GVKEY_SINGLESET, 1 }, // \001 (1) = Shift Lower to Upper
+ { "\001", 0, GVKEY_INVERT|GVKEY_LOCKSET, 2 }, // \002 (2) = Shift Upper to Upper Lock
+ { "\002", 0, GVKEY_INVERT|GVKEY_LOCKSET, 0 }, // \003 (3) = Shift Upper Lock to Lower
+ { "123", 0, GVKEY_LOCKSET, 3 }, // \004 (4) = Change to Numbers
+ { "\010", "\b", 0, 0 }, // \005 (5) = Backspace
+ { "\015", "\r", 0, 0 }, // \006 (6) = Enter 1
+ { "\015", "\r", 0, 0 }, // \007 (7) = Enter 2 (Short keycap)
+ { "Sym", 0, GVKEY_LOCKSET, 4 }, // \010 (8) = Change to Symbols
+ { "aA", 0, GVKEY_LOCKSET, 0 }, // \011 (9) = Change to Lower Alpha
+ };
+ static const char Eng1Set0Row3[] = "\004 .\006\006";
+ static const char Eng1Set1Row0[] = "QWERTYUIOP";
+ static const char Eng1Set1Row1[] = "ASDFGHJKL";
+ static const char *Eng1Set0[] = { "qwertyuiop", "asdfghjkl", "\001zxcvbnm\005", Eng1Set0Row3, 0 };
+ static const char *Eng1Set1[] = { Eng1Set1Row0, Eng1Set1Row1, "\002ZXCVBNM\005", Eng1Set0Row3, 0 };
+ static const char *Eng1Set2[] = { Eng1Set1Row0, Eng1Set1Row1, "\003ZXCVBNM\005", Eng1Set0Row3, 0 };
+ static const char *Eng1Set3[] = { "+-*/", "@789", "\007456", "\010123", "\01100.", 0 };
+ static const char *Eng1Set4[] = { "#$%^&*()", "~`:;\"'{}", "<>?/\\|[]", "\011\004,! .@", 0 };
+ static const GVKeySet Eng1Sets[] = { Eng1Set0, Eng1Set1, Eng1Set2, Eng1Set3, Eng1Set4, 0 };
+ const GVKeyTable VirtualKeyboard_English1 = { Eng1SKeys, Eng1Sets };
+#endif // GWIN_NEED_KEYBOARD_ENGLISH1
+
+#endif // GFX_USE_GWIN && GWIN_NEED_KEYBOARD
diff --git a/src/gwin/gwin_keyboard_layout.h b/src/gwin/gwin_keyboard_layout.h
new file mode 100644
index 00000000..320a503e
--- /dev/null
+++ b/src/gwin/gwin_keyboard_layout.h
@@ -0,0 +1,69 @@
+/*
+ * 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_keyboard_layout.h
+ * @brief GWIN Virtual Keyboard Layout structures.
+ */
+
+#ifndef _GWIN_KEYBOARD_LAYOUT_H
+#define _GWIN_KEYBOARD_LAYOUT_H
+
+/**
+ * A GVKeyTable is a set of definitions that define how the keyboard lays out
+ * its keys. A GVKeyTable consists of a number of GVKeySets and a special key table.
+ *
+ * A GVKeySet is a set of keys that make up the currently visible keyboard.
+ * Special keys in the GVKeySet can be used to switch between GVKeySets within
+ * the GVKeyTable. An example is a shift key which switches between the GVKeySet of
+ * lower case keys and the GVKeySet of upper case keys. GVKeySet number 0 is special
+ * in that it is the default GVKeySet when the keyboard is first displayed.
+ *
+ * A GVKeySet is made up of GVKeyRow's. Each GVKeyRow describes the keys on one row
+ * of the keyboard.
+ *
+ * Each GVKeyRow covers a number of key columns. Different rows can have different numbers of columns.
+ * eg. 'Q' -> 'P' has 10 keys while 'A' to 'L' has 9. Additionally each key can cover more than one
+ * column position eg a wide space bar.
+ *
+ * Each GVKeyRow is just a string. Each character is the caption for one key. Using the same
+ * character for two or more adjacent keys merges the keys into one big key covering multiple key columns.
+ * Characters \001 to \037 (1 to 31) are special keys. How to handle and draw those is described by the
+ * special key structure array. Special keys do things like changing keysets, returning characters less than 32,
+ * have multiple character keycaps.
+ *
+ * Note: keycaps from the special key table with a single character from 1 to 31 in them may invoke special drawn
+ * symbols eg. character 13 may cause a special symbol to be drawn for the enter key. Other than those characters
+ * which are drawn as symbols by the keyboard draw function, all other characters for keycaps are drawn using the
+ * current widget font.
+ *
+ * Special keycaps handled by the standard draw:
+ * \001 (1) - Shift (up arrow)
+ * \002 (2) - Shift locked (up arrow - bold)
+ * \010 (8) - Tab (right arrow)
+ * \011 (9) - BackSpace (left arrow)
+ * \015 (13) - Carriage Return (hooked left arrow)
+ */
+
+typedef struct GVSpecialKey {
+ const char const *keycap; // The caption on the key
+ const char const *sendkey; // The key to send (NULL means none)
+ uint8_t flags; // Flags
+ #define GVKEY_INVERT 0x01 // Invert the color
+ #define GVKEY_SINGLESET 0x02 // Change set when this key is pressed but only for a single keystroke
+ #define GVKEY_LOCKSET 0x04 // Change set when this key is pressed but stay there until the set is changed by the user
+ uint8_t newset; // The new set to change to
+ } GVSpecialKey;
+
+typedef const char **GVKeySet; // Array of Rows - Null indicates the end
+typedef struct GVKeyTable {
+ const GVSpecialKey *skeys; // Array of Special Key structures
+ const GVKeySet *ksets; // Array of KeySets - Null indicates the end
+ } GVKeyTable;
+
+#endif /* _GWIN_KEYBOARD_LAYOUT_H */
+/** @} */
diff --git a/src/gwin/gwin_options.h b/src/gwin/gwin_options.h
index 106c5e06..b69796c3 100644
--- a/src/gwin/gwin_options.h
+++ b/src/gwin/gwin_options.h
@@ -135,6 +135,13 @@
#ifndef GWIN_NEED_TABSET
#define GWIN_NEED_TABSET FALSE
#endif
+ /**
+ * @brief Should the virtual keyboard be included.
+ * @details Defaults to FALSE
+ */
+ #ifndef GWIN_NEED_KEYBOARD
+ #define GWIN_NEED_KEYBOARD FALSE
+ #endif
/**
* @}
*
@@ -343,6 +350,27 @@
#ifndef GWIN_FLASHING_PERIOD
#define GWIN_FLASHING_PERIOD 250
#endif
+ /**
+ * @brief The default keyboard layout for the virtual gwin keyboard
+ * @details Defaults to VirtualKeyboardLayout_English1
+ */
+ #ifndef GWIN_KEYBOARD_DEFAULT_LAYOUT
+ #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1
+ #endif
+/**
+ * @}
+ *
+ * @name GWIN Virtual Keyboard Layouts
+ * @brief One or more of these may be defined. They will only be created if GWIN_NEED_KEYBOARD is TRUE.
+ * @{
+ */
+ /**
+ * @brief The default keyboard layout for the virtual gwin keyboard
+ * @details Defaults to VirtualKeyboardLayout_English1
+ */
+ #ifndef GWIN_NEED_KEYBOARD_ENGLISH1
+ #define GWIN_NEED_KEYBOARD_ENGLISH1 TRUE
+ #endif
/** @} */
#endif /* _GWIN_OPTIONS_H */
diff --git a/src/gwin/gwin_rules.h b/src/gwin/gwin_rules.h
index 8d8ef2cc..7678ab7c 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_IMAGE || GWIN_NEED_CHECKBOX || GWIN_NEED_PROGRESSBAR || GWIN_NEED_KEYBOARD
#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."
diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h
index 63d73e77..0ed80a84 100644
--- a/src/gwin/gwin_widget.h
+++ b/src/gwin/gwin_widget.h
@@ -377,5 +377,9 @@ bool_t gwinAttachListener(GListener *pl);
#include "gwin_progressbar.h"
#endif
+#if GWIN_NEED_KEYBOARD || defined(__DOXYGEN__)
+ #include "gwin_keyboard.h"
+#endif
+
#endif /* _GWIDGET_H */
/** @} */