aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorinmarket <andrewh@inmarket.com.au>2015-10-13 00:58:31 +1000
committerinmarket <andrewh@inmarket.com.au>2015-10-13 00:58:31 +1000
commit5e8e0b7744a59c5bbf918a5133b55c3d1beef12f (patch)
treeb96c3e386979627f925d166b4f3ba5fe7c54ffd5 /src
parent5c615c3430cf1457d721f22a90808edc067fb6ea (diff)
downloaduGFX-5e8e0b7744a59c5bbf918a5133b55c3d1beef12f.tar.gz
uGFX-5e8e0b7744a59c5bbf918a5133b55c3d1beef12f.tar.bz2
uGFX-5e8e0b7744a59c5bbf918a5133b55c3d1beef12f.zip
Working TextEdit with on-screen keyboard (and real keyboard)
Diffstat (limited to 'src')
-rw-r--r--src/gwin/gwin_class.h2
-rw-r--r--src/gwin/gwin_textedit.c229
-rw-r--r--src/gwin/gwin_widget.c22
-rw-r--r--src/gwin/gwin_widget.h2
4 files changed, 135 insertions, 120 deletions
diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h
index 8aa5b9ff..7946a839 100644
--- a/src/gwin/gwin_class.h
+++ b/src/gwin/gwin_class.h
@@ -325,6 +325,8 @@ bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit);
/**
* @brief Move the focus off the current focus window.
*
+ * @note The focus can stay on the same window if there is no other focusable window
+ *
* @notapi
*/
void _gwinMoveFocus(void);
diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c
index c1373897..ee147c84 100644
--- a/src/gwin/gwin_textedit.c
+++ b/src/gwin/gwin_textedit.c
@@ -18,110 +18,109 @@
#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;
+#define TEXT_PADDING_LEFT 4
+#define CURSOR_PADDING_LEFT 0
+#define CURSOR_EXTRA_HEIGHT 1
// Macros to assist in data type conversions
#define gh2obj ((GTexteditObject *)gh)
#define gw2obj ((GTexteditObject *)gw)
-// 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++;
+static bool_t resizeText(GWidgetObject* gw, size_t pos, int32_t diff) {
+ char *p, *q;
+ size_t sz;
+
+ p = (char *)gw->text;
+ sz = strlen(p)+1;
+ if (diff < 0)
+ memcpy(p+pos, p+pos-diff, sz-pos+diff);
+ if (!(p = gfxRealloc(p, sz, sz+diff)))
+ return FALSE;
+ gw->text = p;
+ if (diff > 0) {
+ q = p + sz;
+ p += pos;
+ while(--q >= p)
+ q[diff] = q[0];
}
-
- // Shift
- memcpy(&buffer[index+1], &buffer[index], indexTerminator-index);
-
- // Fill the gap
- buffer[index] = fillChar;
+ return TRUE;
}
-
-#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
- static void TextEditKeyboard(GWidgetObject* gw, GEventKeyboard* pke)
- {
+#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD
+ static void TextEditKeyboard(GWidgetObject* gw, GEventKeyboard* pke) {
// Only react on KEYDOWN events. Ignore KEYUP events.
- if (pke->keystate & GKEYSTATE_KEYUP) {
+ if ((pke->keystate & GKEYSTATE_KEYUP) || !pke->bytecount)
return;
- }
// Is it a special key?
if (pke->keystate & GKEYSTATE_SPECIAL) {
+
// Arrow keys to move the cursor
switch ((uint8_t)pke->c[0]) {
case GKEY_LEFT:
- if (gw2obj->cursorPos > 0) {
- gw2obj->cursorPos--;
- }
+ if (!gw2obj->cursorPos)
+ return;
+ gw2obj->cursorPos--;
break;
-
case GKEY_RIGHT:
- if (gw2obj->cursorPos < strlen(gw2obj->textBuffer)) {
- gw2obj->cursorPos++;
- }
+ if (!gw->text[gw2obj->cursorPos])
+ return;
+ gw2obj->cursorPos++;
break;
-
-
- default:
+ case GKEY_HOME:
+ if (!gw2obj->cursorPos)
+ return;
+ gw2obj->cursorPos = 0;
break;
- }
- }
-
- // Parse the key press
- else if (pke->bytecount >= 1) {
- // Is it backspace?
- if (pke->c[0] == GKEY_BACKSPACE) {
- if (gw2obj->cursorPos == 0) {
+ case GKEY_END:
+ if (!gw->text[gw2obj->cursorPos])
return;
- }
- _shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos--);
+ gw2obj->cursorPos = strlen(gw->text);
+ break;
+ default:
+ return;
}
- // 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') {
+ } else {
+
+ // Normal key press
+ switch((uint8_t)pke->c[0]) {
+ case GKEY_BACKSPACE:
+ // Backspace
+ if (!gw2obj->cursorPos)
+ return;
+ gw2obj->cursorPos--;
+ resizeText(gw, gw2obj->cursorPos, -1);
+ break;
+ case GKEY_TAB:
+ case GKEY_LF:
+ case GKEY_CR:
+ // Move to the next field
+ _gwinMoveFocus();
+ return;
+ case GKEY_DEL:
+ // Delete
+ if (!gw->text[gw2obj->cursorPos])
+ return;
+ resizeText(gw, gw2obj->cursorPos, -1);
+ break;
+ default:
+ // Ignore any other control characters
+ if ((uint8_t)pke->c[0] < GKEY_SPACE)
return;
- }
- _shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos+1);
- }
- // Add a new character
- else {
- // Prevent buffer overflow
- if (gw2obj->cursorPos >= gw2obj->maxSize)
+ // Keep the edit length to less than the maximum
+ if (gw2obj->maxSize && gw2obj->cursorPos+pke->bytecount > 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]);
- }
+ // Make space
+ resizeText(gw, gw2obj->cursorPos, pke->bytecount);
- // Set the new text
- gwinSetText((GHandle)gw, gw2obj->textBuffer, FALSE);
+ // Insert the character
+ memcpy((char *)gw->text+gw2obj->cursorPos, pke->c, pke->bytecount);
+ gw2obj->cursorPos += pke->bytecount;
+ break;
+ }
}
_gwinUpdate((GHandle)gw);
@@ -141,12 +140,12 @@ static const gwidgetVMT texteditVMT = {
gwinTexteditDefaultDraw, // default drawing routine
#if GINPUT_NEED_MOUSE
{
- 0, // Process mose down events (NOT USED)
+ 0, // Process mouse 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
+ #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD
{
TextEditKeyboard // Process keyboard key down events
},
@@ -172,25 +171,25 @@ static const gwidgetVMT texteditVMT = {
GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize)
{
+ char *p;
+
// Create the underlying widget
if (!(wt = (GTexteditObject*)_gwidgetCreate(g, &wt->w, pInit, &texteditVMT)))
return 0;
- // 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';
+ // Reallocate the text (if necessary)
+ if (!(wt->w.g.flags & GWIN_FLG_ALLOCTXT)) {
+ if (!(p = gfxAlloc(wt->maxSize+1)))
+ return 0;
+ strncpy(p, wt->w.text, wt->maxSize);
+ wt->w.text = p;
+ wt->w.g.flags |= GWIN_FLG_ALLOCTXT;
}
// Set text and cursor position
- strncpy(wt->textBuffer, gwinGetText((GHandle)wt), wt->maxSize); // FixMe: pInit->text leads to a segfault
- wt->cursorPos = strlen(wt->textBuffer);
+ wt->cursorPos = strlen(wt->w.text);
gwinSetVisible(&wt->w.g, pInit->g.show);
@@ -199,43 +198,53 @@ GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit
static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param)
{
-
+ const char *p;
+ coord_t cpos, tpos;
+ color_t ccol, tcol;
(void)param;
// Is it a valid handle?
- if (gw->g.vmt != (gwinVMT*)&texteditVMT) {
+ 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;
+ tcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text;
+ ccol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge;
+
+ // Adjust the text position so the cursor fits in the window
+ p = gw->text;
+ if (!gw2obj->cursorPos)
+ tpos = 0;
+ else {
+ for(cpos = gw2obj->cursorPos; ; p++, cpos--) {
+ tpos = gdispGetStringWidthCount(p, gw->g.font, cpos);
+ if (tpos < gw->g.width-(TEXT_PADDING_LEFT+CURSOR_PADDING_LEFT))
+ break;
+ }
+ }
// Render background and string
- 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);
+ #if TEXT_PADDING_LEFT
+ gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, TEXT_PADDING_LEFT, gw->g.height, gw->pstyle->background);
+ #endif
+ gdispGFillStringBox(gw->g.display, gw->g.x + TEXT_PADDING_LEFT, gw->g.y, gw->g.width-TEXT_PADDING_LEFT, gw->g.height, p, gw->g.font, tcol, gw->pstyle->background, justifyLeft);
// Render cursor (if focused)
if (gwinGetFocus() == (GHandle)gw) {
// Calculate cursor stuff
- 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);
+ tpos += gw->g.x + CURSOR_PADDING_LEFT + TEXT_PADDING_LEFT + gdispGetFontMetric(gw->g.font, fontBaselineX)/2;
+ cpos = (gw->g.height - gdispGetFontMetric(gw->g.font, fontHeight))/2 - CURSOR_EXTRA_HEIGHT;
+ gdispGDrawLine(gw->g.display, tpos, gw->g.y + cpos, tpos, gw->g.y + gw->g.height - cpos, ccol);
}
+
+ // Render border
+ gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ccol);
+
+ // Render highlighted border of focused
+ _gwidgetDrawFocusRect(gw, 1, 1, gw->g.width-2, gw->g.height-2);
+
}
#undef gh2obj
diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c
index 162d41e3..1bf91b11 100644
--- a/src/gwin/gwin_widget.c
+++ b/src/gwin/gwin_widget.c
@@ -145,11 +145,10 @@ static void gwidgetEvent(void *param, GEvent *pe) {
if ((pme->buttons & GMETA_MOUSE_DOWN)) {
gh->flags |= GWIN_FLG_MOUSECAPTURE;
- #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD
+ #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_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);
+ // If we can't then we don't change the focus
+ gwinSetFocus(gh);
#endif
if (wvmt->MouseDown)
@@ -159,7 +158,7 @@ static void gwidgetEvent(void *param, GEvent *pe) {
break;
#endif
- #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD
+ #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD
case GEVENT_KEYBOARD:
// If Tab key pressed then set focus to next widget
if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) {
@@ -227,7 +226,7 @@ static void gwidgetEvent(void *param, GEvent *pe) {
#undef pde
}
-#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD
+#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD
GHandle gwinGetFocus(void) {
return _widgetInFocus;
}
@@ -254,13 +253,18 @@ static void gwidgetEvent(void *param, GEvent *pe) {
void _gwinMoveFocus(void) {
GHandle gh;
+ bool_t looponce;
// Find a new focus window (one may or may not exist).
- for(gh = gwinGetNextWindow(_widgetInFocus); gh && gh != _widgetInFocus; gh = gwinGetNextWindow(gh)) {
+ looponce = FALSE;
+ for(gh = gwinGetNextWindow(_widgetInFocus); ; gh = gwinGetNextWindow(gh)) {
+ if (!gh && !looponce) {
+ looponce = TRUE;
+ gh = gwinGetNextWindow(0);
+ }
if (gwinSetFocus(gh))
- return;
+ break;
}
- gwinSetFocus(0);
}
void _gwinFixFocus(GHandle gh) {
diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h
index 5239b6b8..732d7d1c 100644
--- a/src/gwin/gwin_widget.h
+++ b/src/gwin/gwin_widget.h
@@ -356,7 +356,7 @@ 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__)
+#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD || defined(__DOXYGEN__)
/**
* @brief Set the keyboard focus to a specific window
* @return Returns TRUE if the focus could be set to that window