Add GWIN virtual keyboard widget

This commit is contained in:
inmarket 2015-04-12 17:07:38 +10:00
parent b04148ca2b
commit 1ce3f20fa4
15 changed files with 939 additions and 2 deletions

View File

@ -0,0 +1,3 @@
DEMODIR = $(GFXLIB)/demos/modules/gwin/keyboard
GFXINC += $(DEMODIR)
GFXSRC += $(DEMODIR)/main.c

View File

@ -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 */

View File

@ -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;
}

View File

@ -0,0 +1 @@
This demo demonstrates the virtual keyboard.

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

471
src/gwin/gwin_keyboard.c Normal file
View File

@ -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 */

111
src/gwin/gwin_keyboard.h Normal file
View File

@ -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 */
/** @} */

View File

@ -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

View File

@ -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 */
/** @} */

View File

@ -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 */

View File

@ -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."

View File

@ -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 */
/** @} */