aboutsummaryrefslogtreecommitdiffstats
path: root/src/gwin/gwin_textedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gwin/gwin_textedit.c')
-rw-r--r--src/gwin/gwin_textedit.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c
new file mode 100644
index 00000000..ee147c84
--- /dev/null
+++ b/src/gwin/gwin_textedit.c
@@ -0,0 +1,253 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gwin/gwin_textedit.c
+ * @brief GWIN TextEdit widget header file
+ */
+
+#include "gfx.h"
+
+#if GFX_USE_GWIN && GWIN_NEED_TEXTEDIT
+
+#include "gwin_class.h"
+#include <string.h>
+
+// Some settings
+#define TEXT_PADDING_LEFT 4
+#define CURSOR_PADDING_LEFT 0
+#define CURSOR_EXTRA_HEIGHT 1
+
+// Macros to assist in data type conversions
+#define gh2obj ((GTexteditObject *)gh)
+#define gw2obj ((GTexteditObject *)gw)
+
+static bool_t resizeText(GWidgetObject* gw, size_t pos, int32_t diff) {
+ char *p, *q;
+ size_t sz;
+
+ p = (char *)gw->text;
+ sz = strlen(p)+1;
+ if (diff < 0)
+ memcpy(p+pos, p+pos-diff, sz-pos+diff);
+ if (!(p = gfxRealloc(p, sz, sz+diff)))
+ return FALSE;
+ gw->text = p;
+ if (diff > 0) {
+ q = p + sz;
+ p += pos;
+ while(--q >= p)
+ q[diff] = q[0];
+ }
+ return TRUE;
+}
+
+#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD
+ static void TextEditKeyboard(GWidgetObject* gw, GEventKeyboard* pke) {
+ // Only react on KEYDOWN events. Ignore KEYUP events.
+ if ((pke->keystate & GKEYSTATE_KEYUP) || !pke->bytecount)
+ return;
+
+ // Is it a special key?
+ if (pke->keystate & GKEYSTATE_SPECIAL) {
+
+ // Arrow keys to move the cursor
+ switch ((uint8_t)pke->c[0]) {
+ case GKEY_LEFT:
+ if (!gw2obj->cursorPos)
+ return;
+ gw2obj->cursorPos--;
+ break;
+ case GKEY_RIGHT:
+ if (!gw->text[gw2obj->cursorPos])
+ return;
+ gw2obj->cursorPos++;
+ break;
+ case GKEY_HOME:
+ if (!gw2obj->cursorPos)
+ return;
+ gw2obj->cursorPos = 0;
+ break;
+ case GKEY_END:
+ if (!gw->text[gw2obj->cursorPos])
+ return;
+ gw2obj->cursorPos = strlen(gw->text);
+ break;
+ default:
+ return;
+ }
+
+ } else {
+
+ // Normal key press
+ switch((uint8_t)pke->c[0]) {
+ case GKEY_BACKSPACE:
+ // Backspace
+ if (!gw2obj->cursorPos)
+ return;
+ gw2obj->cursorPos--;
+ resizeText(gw, gw2obj->cursorPos, -1);
+ break;
+ case GKEY_TAB:
+ case GKEY_LF:
+ case GKEY_CR:
+ // Move to the next field
+ _gwinMoveFocus();
+ return;
+ case GKEY_DEL:
+ // Delete
+ if (!gw->text[gw2obj->cursorPos])
+ return;
+ resizeText(gw, gw2obj->cursorPos, -1);
+ break;
+ default:
+ // Ignore any other control characters
+ if ((uint8_t)pke->c[0] < GKEY_SPACE)
+ return;
+
+ // Keep the edit length to less than the maximum
+ if (gw2obj->maxSize && gw2obj->cursorPos+pke->bytecount > gw2obj->maxSize)
+ return;
+
+ // Make space
+ resizeText(gw, gw2obj->cursorPos, pke->bytecount);
+
+ // Insert the character
+ memcpy((char *)gw->text+gw2obj->cursorPos, pke->c, pke->bytecount);
+ gw2obj->cursorPos += pke->bytecount;
+ break;
+ }
+ }
+
+ _gwinUpdate((GHandle)gw);
+ }
+#endif
+
+static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param);
+
+static const gwidgetVMT texteditVMT = {
+ {
+ "TextEdit", // The class name
+ sizeof(GTexteditObject), // The object size
+ _gwidgetDestroy, // The destroy routine
+ _gwidgetRedraw, // The redraw routine
+ 0, // The after-clear routine
+ },
+ gwinTexteditDefaultDraw, // default drawing routine
+ #if GINPUT_NEED_MOUSE
+ {
+ 0, // Process mouse down events (NOT USED)
+ 0, // Process mouse up events (NOT USED)
+ 0, // Process mouse move events (NOT USED)
+ },
+ #endif
+ #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD
+ {
+ TextEditKeyboard // Process keyboard key down events
+ },
+ #endif
+ #if GINPUT_NEED_TOGGLE
+ {
+ 0, // No toggle role
+ 0, // Assign Toggles (NOT USED)
+ 0, // Get Toggles (NOT USED)
+ 0, // Process toggle off event (NOT USED)
+ 0, // Process toggle on event (NOT USED)
+ },
+ #endif
+ #if GINPUT_NEED_DIAL
+ {
+ 0, // No dial roles
+ 0, // Assign Dials (NOT USED)
+ 0, // Get Dials (NOT USED)
+ 0, // Procees dial move events (NOT USED)
+ },
+ #endif
+};
+
+GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize)
+{
+ char *p;
+
+ // Create the underlying widget
+ if (!(wt = (GTexteditObject*)_gwidgetCreate(g, &wt->w, pInit, &texteditVMT)))
+ return 0;
+
+ wt->maxSize = maxSize;
+
+ // Reallocate the text (if necessary)
+ if (!(wt->w.g.flags & GWIN_FLG_ALLOCTXT)) {
+ if (!(p = gfxAlloc(wt->maxSize+1)))
+ return 0;
+ strncpy(p, wt->w.text, wt->maxSize);
+ wt->w.text = p;
+ wt->w.g.flags |= GWIN_FLG_ALLOCTXT;
+ }
+
+ // Set text and cursor position
+ wt->cursorPos = strlen(wt->w.text);
+
+ gwinSetVisible(&wt->w.g, pInit->g.show);
+
+ return (GHandle)wt;
+}
+
+static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param)
+{
+ const char *p;
+ coord_t cpos, tpos;
+ color_t ccol, tcol;
+ (void)param;
+
+ // Is it a valid handle?
+ if (gw->g.vmt != (gwinVMT*)&texteditVMT)
+ return;
+
+ // Retrieve colors
+ tcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text;
+ ccol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge;
+
+ // Adjust the text position so the cursor fits in the window
+ p = gw->text;
+ if (!gw2obj->cursorPos)
+ tpos = 0;
+ else {
+ for(cpos = gw2obj->cursorPos; ; p++, cpos--) {
+ tpos = gdispGetStringWidthCount(p, gw->g.font, cpos);
+ if (tpos < gw->g.width-(TEXT_PADDING_LEFT+CURSOR_PADDING_LEFT))
+ break;
+ }
+ }
+
+ // Render background and string
+ #if TEXT_PADDING_LEFT
+ gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, TEXT_PADDING_LEFT, gw->g.height, gw->pstyle->background);
+ #endif
+ gdispGFillStringBox(gw->g.display, gw->g.x + TEXT_PADDING_LEFT, gw->g.y, gw->g.width-TEXT_PADDING_LEFT, gw->g.height, p, gw->g.font, tcol, gw->pstyle->background, justifyLeft);
+
+ // Render cursor (if focused)
+ if (gwinGetFocus() == (GHandle)gw) {
+ // Calculate cursor stuff
+
+ // Draw cursor
+ tpos += gw->g.x + CURSOR_PADDING_LEFT + TEXT_PADDING_LEFT + gdispGetFontMetric(gw->g.font, fontBaselineX)/2;
+ cpos = (gw->g.height - gdispGetFontMetric(gw->g.font, fontHeight))/2 - CURSOR_EXTRA_HEIGHT;
+ gdispGDrawLine(gw->g.display, tpos, gw->g.y + cpos, tpos, gw->g.y + gw->g.height - cpos, ccol);
+ }
+
+ // Render border
+ gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ccol);
+
+ // Render highlighted border of focused
+ _gwidgetDrawFocusRect(gw, 1, 1, gw->g.width-2, gw->g.height-2);
+
+}
+
+#undef gh2obj
+#undef gw2obj
+
+#endif // GFX_USE_GWIN && GWIN_NEED_TEXTEDIT