From fcaa42972964e002474e6f79364f3af1a616d25c Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 18:33:16 +0200 Subject: [PATCH] First working Version of TextEdit widget --- src/gwin/gwin_textedit.c | 141 +++++++++++++++++++++++++++++++++------ src/gwin/gwin_textedit.h | 9 ++- 2 files changed, 129 insertions(+), 21 deletions(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 6e90b3ac..9c7990eb 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -15,36 +15,103 @@ #if GFX_USE_GWIN && GWIN_NEED_TEXTEDIT #include "gwin_class.h" +#include +#include -// Text padding (between edge and text) in pixels -const int TEXT_PADDING = 3; +// Some settings + 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(); + +// ToDo: Optimize by using strncpy() instead +static void _shiftTextLeft(char* buffer, size_t bufferSize, size_t index) +{ + // Find the end of the string + size_t indexTerminator = index; + while (buffer[indexTerminator] != '\0' && indexTerminator < bufferSize-1) { + indexTerminator++; + } + + // Shift + size_t i = 0; + for (i = index; i < indexTerminator+1; i++) { + buffer[i-1] = buffer[i]; + } + buffer[indexTerminator] = '\0'; +} + +// ToDo: Optimize by using strncpy() instead +static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char fillChar) +{ + // Find the end of the string + size_t indexTerminator = index; + while (buffer[indexTerminator] != '\0' && indexTerminator < bufferSize-1) { + indexTerminator++; + } + + // Shift + size_t i = 0; + for (i = indexTerminator+1; i > index; i--) { + if (i > bufferSize-1) { + break; + } + + buffer[i] = buffer[i-1]; + } + + // Fill the gap + buffer[index] = fillChar; +} -// macros to assist in data type conversions -#define gh2obj ((GTexteditObject *)gh) -#define gw2obj ((GTexteditObject *)gw) #if GINPUT_NEED_KEYBOARD static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) { - // Create a temporary buffer containing the current size - unsigned bufSize = strlen(gwinGetText((GHandle)gw))+1; - char buf[bufSize]; - strncpy(buf, gwinGetText((GHandle)gw), bufSize); + // 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 - if (pke->bytecount == 1) { + else if (pke->bytecount >= 1) { // Check backspace if (pke->c[0] == GKEY_BACKSPACE) { - buf[strlen(buf)-1] = '\0'; + if (gw2obj->cursorPos == 0) { + return; + } + _shiftTextLeft(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos--); } - // Append new character + // Add a new character else { - strncat(buf, &(pke->c[0]), 1); + // Shift everything right from the cursor by one character. This includes the '\0'. Then inser the new character. + _shiftTextRight(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos++, pke->c[0]); } // Set the new text - gwinSetText((GHandle)gw, buf, TRUE); + gwinSetText((GHandle)gw, gw2obj->textBuffer, FALSE); } _gwinUpdate((GHandle)gw); @@ -93,15 +160,30 @@ static const gwidgetVMT texteditVMT = { #endif }; -GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit) +GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize) { uint16_t flags = 0; + // Create the underlying widget if (!(widget = (GTexteditObject*)_gwidgetCreate(g, &widget->w, pInit, &texteditVMT))) { return 0; } - widget->w.g.flags |= flags; + // Allocate the text buffer + widget->bufferSize = bufSize; + widget->textBuffer = gfxAlloc(widget->bufferSize); + if (widget->textBuffer == 0) { + return 0; + } + + // Initialize the text buffer + size_t i = 0; + for (i = 0; i < bufSize; i++) { + widget->textBuffer[i] = '\0'; + } + + widget->cursorPos = 0; + widget->w.g.flags |= flags; gwinSetVisible(&widget->w.g, pInit->g.show); return (GHandle)widget; @@ -109,17 +191,38 @@ GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* p static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) { - color_t textColor; - (void) param; + (void)param; // Is it a valid handle? if (gw->g.vmt != (gwinVMT*)&texteditVMT) { return; } - textColor = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; + // 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 gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font, textColor, gw->pstyle->background, justifyLeft); + + // Render cursor (if focused) + if (gwinGetFocus() == (GHandle)gw || TRUE) { + // Calculate cursor stuff + char textBeforeCursor[gw2obj->bufferSize]; + strncpy(textBeforeCursor, gw->text, gw2obj->cursorPos+1); + textBeforeCursor[gw2obj->cursorPos] = '\0'; + coord_t textWidth = gdispGetStringWidth(textBeforeCursor, gw->g.font); + 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 - 2; + coord_t lineX1 = gw->g.x + textWidth - 2; + 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 diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h index 84057df4..2b5e26e7 100644 --- a/src/gwin/gwin_textedit.h +++ b/src/gwin/gwin_textedit.h @@ -31,6 +31,10 @@ // A TextEdit widget typedef struct GTexteditObject { GWidgetObject w; + + char* textBuffer; + size_t bufferSize; + uint16_t cursorPos; } GTexteditObject; #ifdef __cplusplus @@ -46,13 +50,14 @@ extern "C" { * @param[in] g The GDisplay on which the textedit should be displayed * @param[in] widget The TextEdit structure to initialise. If this is NULL, the structure is dynamically allocated. * @param[in] pInit The initialisation parameters to use. + * @param[in] bufSize The maximum number of characters the TextEdit widget can hold. * * @return NULL if there is no resultat drawing area, otherwise the widget handle. * * @api */ -GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit); -#define gwinTexteditCreate(w, pInit) gwinGTexteditCreate(GDISP, w, pInit) +GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize); +#define gwinTexteditCreate(w, pInit, bufSize) gwinGTexteditCreate(GDISP, w, pInit, bufSize) #ifdef __cplusplus }