From cf06739b4ee44bcbfac8ae99e605a4ec4e832c8b Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Mon, 10 Aug 2015 18:54:53 +0200 Subject: [PATCH 01/45] Fixing include --- src/ginput/ginput_keyboard_microcode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ginput/ginput_keyboard_microcode.c b/src/ginput/ginput_keyboard_microcode.c index 167ed5a4..9db0459b 100644 --- a/src/ginput/ginput_keyboard_microcode.c +++ b/src/ginput/ginput_keyboard_microcode.c @@ -14,7 +14,7 @@ #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF -#include "keyboard_microcode.h" +#include "ginput_keyboard_microcode.h" #if GKEYBOARD_LAYOUT_SCANCODE2_US From 16d213d4ed14add60e286246ad6dc563761b9689 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Wed, 12 Aug 2015 17:32:38 +0200 Subject: [PATCH 02/45] Passing keyboard events to widgets (not finished yet) --- src/gwin/gwin_button.c | 5 +++++ src/gwin/gwin_checkbox.c | 5 +++++ src/gwin/gwin_class.h | 5 +++++ src/gwin/gwin_container.c | 5 +++++ src/gwin/gwin_frame.c | 5 +++++ src/gwin/gwin_keyboard.c | 5 +++++ src/gwin/gwin_label.c | 5 +++++ src/gwin/gwin_list.c | 5 +++++ src/gwin/gwin_progressbar.c | 5 +++++ src/gwin/gwin_radio.c | 5 +++++ src/gwin/gwin_slider.c | 5 +++++ src/gwin/gwin_tabset.c | 5 +++++ src/gwin/gwin_widget.c | 21 +++++++++++++++++++++ 13 files changed, 81 insertions(+) diff --git a/src/gwin/gwin_button.c b/src/gwin/gwin_button.c index 4066884c..8a178b72 100644 --- a/src/gwin/gwin_button.c +++ b/src/gwin/gwin_button.c @@ -95,6 +95,11 @@ static const gwidgetVMT buttonVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 1, // 1 toggle role diff --git a/src/gwin/gwin_checkbox.c b/src/gwin/gwin_checkbox.c index 11e08804..83d5bc38 100644 --- a/src/gwin/gwin_checkbox.c +++ b/src/gwin/gwin_checkbox.c @@ -91,6 +91,11 @@ static const gwidgetVMT checkboxVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 1, // 1 toggle role diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h index 01b6e596..45467597 100644 --- a/src/gwin/gwin_class.h +++ b/src/gwin/gwin_class.h @@ -96,6 +96,11 @@ typedef struct gwinVMT { void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse move events (optional) }; #endif + #if GINPUT_NEED_KEYBOARD + struct { + void (*KeyboardEvent) (GWidgetObject *gw, GEventKeyboard *pke); // @< Process keyboard events (optional) + }; + #endif #if GINPUT_NEED_TOGGLE struct { uint16_t toggleroles; // @< The roles supported for toggles (0->toggleroles-1) diff --git a/src/gwin/gwin_container.c b/src/gwin/gwin_container.c index b84c96ae..172b6ae8 100644 --- a/src/gwin/gwin_container.c +++ b/src/gwin/gwin_container.c @@ -110,6 +110,11 @@ static const gcontainerVMT containerVMT = { 0, 0, 0, // No mouse }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, 0, 0, 0, 0, // No toggles diff --git a/src/gwin/gwin_frame.c b/src/gwin/gwin_frame.c index b29c4ffc..f8787dcb 100644 --- a/src/gwin/gwin_frame.c +++ b/src/gwin/gwin_frame.c @@ -178,6 +178,11 @@ static const gcontainerVMT frameVMT = { 0, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // 1 toggle role diff --git a/src/gwin/gwin_keyboard.c b/src/gwin/gwin_keyboard.c index e9f5f860..c27580de 100644 --- a/src/gwin/gwin_keyboard.c +++ b/src/gwin/gwin_keyboard.c @@ -313,6 +313,11 @@ static const gwidgetVMT keyboardVMT = { KeyMouseMove, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // No toggle roles diff --git a/src/gwin/gwin_label.c b/src/gwin/gwin_label.c index 4e4d799b..38d9b601 100644 --- a/src/gwin/gwin_label.c +++ b/src/gwin/gwin_label.c @@ -58,6 +58,11 @@ static const gwidgetVMT labelVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard key down events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // No toggle role diff --git a/src/gwin/gwin_list.c b/src/gwin/gwin_list.c index 7ca300a9..3ea6286c 100644 --- a/src/gwin/gwin_list.c +++ b/src/gwin/gwin_list.c @@ -279,6 +279,11 @@ static const gwidgetVMT listVMT = { ListMouseMove, }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 2, // two toggle roles diff --git a/src/gwin/gwin_progressbar.c b/src/gwin/gwin_progressbar.c index e275b9e4..3843949a 100644 --- a/src/gwin/gwin_progressbar.c +++ b/src/gwin/gwin_progressbar.c @@ -51,6 +51,11 @@ static const gwidgetVMT progressbarVMT = { 0, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // 1 toggle role diff --git a/src/gwin/gwin_radio.c b/src/gwin/gwin_radio.c index c7d5cf5c..fcdb242b 100644 --- a/src/gwin/gwin_radio.c +++ b/src/gwin/gwin_radio.c @@ -92,6 +92,11 @@ static const gwidgetVMT radioVMT = { 0, // Process mouse move events (NOT USED) }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 1, // 1 toggle role diff --git a/src/gwin/gwin_slider.c b/src/gwin/gwin_slider.c index c29d5996..218f4f6b 100644 --- a/src/gwin/gwin_slider.c +++ b/src/gwin/gwin_slider.c @@ -250,6 +250,11 @@ static const gwidgetVMT sliderVMT = { SliderMouseMove, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 2, // 1 toggle role diff --git a/src/gwin/gwin_tabset.c b/src/gwin/gwin_tabset.c index f1bcd374..f8731f8f 100644 --- a/src/gwin/gwin_tabset.c +++ b/src/gwin/gwin_tabset.c @@ -77,6 +77,11 @@ static const gcontainerVMT tabpageVMT = { 0, // Process mouse move events }, #endif + #if GINPUT_NEED_KEYBOARD + { + 0 // Process keyboard events + }, + #endif #if GINPUT_NEED_TOGGLE { 0, // 1 toggle role diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index bfc5a48f..666987ac 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -88,6 +88,7 @@ static const GWidgetStyle * defaultStyle = &BlackWidgetStyle; /* Process an event */ static void gwidgetEvent(void *param, GEvent *pe) { #define pme ((GEventMouse *)pe) + #define pke ((GEventKeyboard *)pe) #define pte ((GEventToggle *)pe) #define pde ((GEventDial *)pe) @@ -142,6 +143,22 @@ static void gwidgetEvent(void *param, GEvent *pe) { break; #endif + #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD + case GEVENT_KEYBOARD: + // Cycle through all windows + for (gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + + // Check whether the widget is enabled and visible + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) { + continue; + } + + // Pass the information + wvmt->KeyboardEvent(gw, pke); + } + break; + #endif + #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE case GEVENT_TOGGLE: // Cycle through all windows @@ -235,6 +252,10 @@ void _gwidgetInit(void) geventListenerInit(&gl); geventRegisterCallback(&gl, gwidgetEvent, 0); geventAttachSource(&gl, ginputGetMouse(GMOUSE_ALL_INSTANCES), GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); + + #if GINPUT_NEED_KEYBOARD + geventAttachSource(&gl, ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES), 0); + #endif } void _gwidgetDeinit(void) From 213013e68e64a655e0c6cb56875ea9a7977fe2f6 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Wed, 12 Aug 2015 19:35:44 +0200 Subject: [PATCH 03/45] Codingstyle --- src/gwin/gwin_widget.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 666987ac..6002ffa9 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -382,10 +382,10 @@ const GWidgetStyle *gwinGetDefaultStyle(void) { return defaultStyle; } - void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { - if (!(gh->flags & GWIN_FLG_WIDGET)) + if (!(gh->flags & GWIN_FLG_WIDGET)) { return; + } // Dispose of the old string if ((gh->flags & GWIN_FLG_ALLOCTXT)) { @@ -397,9 +397,9 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { } // Alloc the new text if required - if (!text || !*text) + if (!text || !*text) { gw->text = ""; - else if (useAlloc) { + } else if (useAlloc) { char *str; if ((str = gfxAlloc(strlen(text)+1))) { @@ -407,8 +407,10 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { strcpy(str, text); } gw->text = (const char *)str; - } else + } else { gw->text = text; + } + _gwinUpdate(gh); } From 46ba0420c342eaed1e4365c7e4cb65c0263bc7eb Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Wed, 12 Aug 2015 19:36:14 +0200 Subject: [PATCH 04/45] Adding TextEdit dummy widget (not implemented yet) --- gfxconf.example.h | 1 + src/gwin/gwin.mk | 3 +- src/gwin/gwin_mk.c | 1 + src/gwin/gwin_options.h | 7 +++ src/gwin/gwin_rules.h | 2 +- src/gwin/gwin_textedit.c | 113 +++++++++++++++++++++++++++++++++++++++ src/gwin/gwin_textedit.h | 62 +++++++++++++++++++++ src/gwin/gwin_widget.h | 4 ++ 8 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 src/gwin/gwin_textedit.c create mode 100644 src/gwin/gwin_textedit.h diff --git a/gfxconf.example.h b/gfxconf.example.h index d46bbae2..1a54b8eb 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -186,6 +186,7 @@ // #define GWIN_NEED_KEYBOARD FALSE // #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1 // #define GWIN_NEED_KEYBOARD_ENGLISH1 TRUE +// #define GWIN_NEED_TEXTEDIT FALSE // #define GWIN_FLAT_STYLING FALSE // #define GWIN_WIDGET_TAGS FALSE diff --git a/src/gwin/gwin.mk b/src/gwin/gwin.mk index b4357328..d735c3e1 100644 --- a/src/gwin/gwin.mk +++ b/src/gwin/gwin.mk @@ -21,6 +21,7 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin.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 + $(GFXLIB)/src/gwin/gwin_keyboard_layout.c \ + $(GFXLIB)/src/gwin/gwin_textedit.c GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include diff --git a/src/gwin/gwin_mk.c b/src/gwin/gwin_mk.c index 981eb6d2..2af501b5 100644 --- a/src/gwin/gwin_mk.c +++ b/src/gwin/gwin_mk.c @@ -24,3 +24,4 @@ #include "gwin_gl3d.c" #include "gwin_keyboard.c" #include "gwin_keyboard_layout.c" +#include "gwin_textedit.c" diff --git a/src/gwin/gwin_options.h b/src/gwin/gwin_options.h index b69796c3..46bd92e7 100644 --- a/src/gwin/gwin_options.h +++ b/src/gwin/gwin_options.h @@ -142,6 +142,13 @@ #ifndef GWIN_NEED_KEYBOARD #define GWIN_NEED_KEYBOARD FALSE #endif + /** + * @brief Should the textedit widget be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_TEXTEDIT + #define GWIN_NEED_TEXTEDIT FALSE + #endif /** * @} * diff --git a/src/gwin/gwin_rules.h b/src/gwin/gwin_rules.h index 7678ab7c..874c59b4 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_KEYBOARD + GWIN_NEED_IMAGE || GWIN_NEED_CHECKBOX || GWIN_NEED_PROGRESSBAR || GWIN_NEED_KEYBOARD || GWIN_NEED_TEXTEDIT #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_textedit.c b/src/gwin/gwin_textedit.c new file mode 100644 index 00000000..4aa41d61 --- /dev/null +++ b/src/gwin/gwin_textedit.c @@ -0,0 +1,113 @@ +/* + * 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" + +// Text padding (between edge and text) in pixels +const int TEXT_PADDING = 3; + +// 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) + { + if (pke->bytecount = 1) { + //gw->text = pke->c[0]; + gwinSetText((GHandle)gw, &(pke->c[0]), TRUE); + } + + _gwinUpdate(&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 mose down events (NOT USED) + 0, // Process mouse up events (NOT USED) + 0, // Process mouse move events (NOT USED) + }, + #endif + #if GINPUT_NEED_KEYBOARD + { + _keyboardEvent, // 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* widget, GWidgetInit* pInit) +{ + uint16_t flags = 0; + + if (!(widget = (GTexteditObject*)_gwidgetCreate(g, &widget->w, pInit, &texteditVMT))) { + return 0; + } + + widget->w.g.flags |= flags; + gwinSetVisible(&widget->w.g, pInit->g.show); + + return (GHandle)widget; +} + +static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) +{ + color_t textColor; + (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; + + + 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); +} + +#undef gh2obj +#undef gw2obj + +#endif // GFX_USE_GWIN && GWIN_NEED_TEXTEDIT diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h new file mode 100644 index 00000000..84057df4 --- /dev/null +++ b/src/gwin/gwin_textedit.h @@ -0,0 +1,62 @@ +/* + * 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.h + * @brief GWIN textedit widget header file + * + * @defgroup TextEdit TextEdit + * @ingroup Widgets + * + * @details A GWIN TextEdit widget allows user input. + * + * @pre GFX_USE_GDISP must be set to TRUE in your gfxconf.h + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GDISP_NEED_TEXT must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_TEXTEDIT must be set to TRUE in your gfxconf.h + * @pre The fonts you want to use must be enabled in your gfxconf.h + * + * @{ + */ + +#ifndef _GWIN_TEXTEDIT_H +#define _GWIN_TEXTEDIT_H + +// This file is included within "src/gwin/gwin_widget.h" + +// A TextEdit widget +typedef struct GTexteditObject { + GWidgetObject w; +} GTexteditObject; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a TextEdit widget. + * @details A TextEdit widget is a rectangular box which allows the user to input data through a keyboard. + * The keyboard can either be a physical one or a virtual on-screen keyboard as the keyboard driver + * is abstracted through the GINPUT module. + * + * @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. + * + * @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) + +#ifdef __cplusplus +} +#endif + +#endif // _GWIN_TEXTEDIT_H +/** @} */ diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index 0ed80a84..1e97cdd2 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -381,5 +381,9 @@ bool_t gwinAttachListener(GListener *pl); #include "gwin_keyboard.h" #endif +#if GWIN_NEED_TEXTEDIT || defined(__DOXYGEN__) + #include "gwin_textedit.h" +#endif + #endif /* _GWIDGET_H */ /** @} */ From f1ede211fb341ebd9bf0d31ff3ca4cf7aba43b19 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Wed, 12 Aug 2015 20:22:34 +0200 Subject: [PATCH 05/45] First (simple) implementation of textbox behavior --- src/gwin/gwin_textedit.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 4aa41d61..fe15f441 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -26,9 +26,25 @@ const int TEXT_PADDING = 3; #if GINPUT_NEED_KEYBOARD static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) { - if (pke->bytecount = 1) { - //gw->text = pke->c[0]; - gwinSetText((GHandle)gw, &(pke->c[0]), TRUE); + // Create a temporary buffer containing the current size + unsigned bufSize = strlen(gwinGetText((GHandle)gw))+1; + char buf[bufSize]; + strncpy(buf, gwinGetText((GHandle)gw), bufSize); + + // Parse the key press + if (pke->bytecount == 1) { + // Check backspace + if (pke->c[0] == GKEY_BACKSPACE) { + buf[strlen(buf)-1] = '\0'; + } + + // Append new character + else { + strncat(buf, &(pke->c[0]), 1); + } + + // Set the new text + gwinSetText((GHandle)gw, buf, TRUE); } _gwinUpdate(&gw); @@ -103,7 +119,6 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) textColor = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text; - 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); } From 765b1df8c54e3e405561a0529671c0b6f150136a Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Wed, 12 Aug 2015 23:28:59 +0200 Subject: [PATCH 06/45] Working on widget focus (not finished yet) --- src/gwin/gwin_widget.c | 51 ++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 6002ffa9..09cc91d8 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -18,10 +18,13 @@ #include "gwin_class.h" -/* Our listener for events for widgets */ -static GListener gl; +// Our listener for events for widgets +static GListener gl; -/* Our default style - a white background theme */ +// The widget that is currently in focus. May be NULL. +static GHandle widgetInFocus; + +// Our default style - a white background theme const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xFFFFFF), // window background @@ -81,11 +84,11 @@ const GWidgetStyle BlackWidgetStyle = { static const GWidgetStyle * defaultStyle = &BlackWidgetStyle; -/* We use these everywhere in this file */ +// We use these everywhere in this file #define gw ((GWidgetObject *)gh) #define wvmt ((gwidgetVMT *)gh->vmt) -/* Process an event */ +// Process an event static void gwidgetEvent(void *param, GEvent *pe) { #define pme ((GEventMouse *)pe) #define pke ((GEventKeyboard *)pe) @@ -106,7 +109,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_MOUSE: case GEVENT_TOUCH: // Cycle through all windows - for(gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) { + for (gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) { // The window must be on this display and visible to be relevant if (h->display != pme->display || !(h->flags & GWIN_FLG_SYSVISIBLE)) @@ -145,18 +148,36 @@ static void gwidgetEvent(void *param, GEvent *pe) { #if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD case GEVENT_KEYBOARD: - // Cycle through all windows - for (gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { + // If Tab key pressed then set focus to next widget + if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { + widgetInFocus = gwinGetNextWindow(widgetInFocus); + // If it was the last widget begin with the first one again + if (widgetInFocus == 0) { + widgetInFocus = gwinGetNextWindow(0); + } + break; + } - // Check whether the widget is enabled and visible - if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) { - continue; + // Otherise, send keyboard events only to widget in focus + if (widgetInFocus != 0) { + // Make sure that it is a widget + if (!(widgetInFocus->flags & GWIN_FLG_WIDGET)) { + break; } - // Pass the information - wvmt->KeyboardEvent(gw, pke); + // Make sure that the widget is enabled and visible + if (!(widgetInFocus->flags & GWIN_FLG_SYSVISIBLE) || !(widgetInFocus->flags & GWIN_FLG_ENABLED)) { + break; + } + + // Check whether this widget provides a method for handling keyboard events + if (((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent == 0) { + break; + } + + // If we got this far we can finally pass the event + ((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)widgetInFocus, pke); } - break; #endif #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE @@ -249,6 +270,8 @@ static void gwidgetEvent(void *param, GEvent *pe) { void _gwidgetInit(void) { + widgetInFocus = 0; + geventListenerInit(&gl); geventRegisterCallback(&gl, gwidgetEvent, 0); geventAttachSource(&gl, ginputGetMouse(GMOUSE_ALL_INSTANCES), GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); From 5fb63ebed6f8c44bdf39c741ffba698d20e866ff Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Thu, 13 Aug 2015 00:37:39 +0200 Subject: [PATCH 07/45] Doxygen fix --- src/gqueue/gqueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gqueue/gqueue.h b/src/gqueue/gqueue.h index 924024b7..40a41bce 100644 --- a/src/gqueue/gqueue.h +++ b/src/gqueue/gqueue.h @@ -385,7 +385,7 @@ GDataBuffer *gfxBufferGetI(void); /** @} */ /** - * @name BufferRelease) Functions + * @name BufferRelease() Functions * @brief Release a buffer back to the free list * * @param[in] pd The buffer to put (back) on the free-list. From 37a088efb73e119a6675241394926cdb47c11ab2 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Thu, 13 Aug 2015 01:12:34 +0200 Subject: [PATCH 08/45] Compiler warning --- src/gwin/gwin_textedit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index fe15f441..6e90b3ac 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -47,7 +47,7 @@ const int TEXT_PADDING = 3; gwinSetText((GHandle)gw, buf, TRUE); } - _gwinUpdate(&gw); + _gwinUpdate((GHandle)gw); } #endif From 3ba3be201cddf3c6a4606f49028fc6640da0b28e Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Thu, 13 Aug 2015 01:13:36 +0200 Subject: [PATCH 09/45] Adding gwinSetFocus() and gwinGetFocus() --- docs/releases.txt | 2 ++ src/gwin/gwin.h | 25 +++++++++++++++++++++++++ src/gwin/gwin_wm.c | 23 ++++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/releases.txt b/docs/releases.txt index 03045d13..9f7c16be 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -9,6 +9,8 @@ FEATURE: Added GFXSINGLEMAKE=yes|no to the ugfx makefile to compile ugfx as a si FEATURE: New board STM32F746G-Discovery FEATURE: New gdisp driver STM32LTDC FEATURE: Better support for Raw32 platforms +FEATURE: Implementing widget focusing. See gwinSetFocus() and gwinGetFocus() + *** Release 2.3 *** FEATURE: Added more events to the slider widget diff --git a/src/gwin/gwin.h b/src/gwin/gwin.h index 4a7d02bf..329be4c4 100644 --- a/src/gwin/gwin.h +++ b/src/gwin/gwin.h @@ -547,6 +547,31 @@ extern "C" { */ GHandle gwinGetNextWindow(GHandle gh); + /** + * @brief Set the focus to a specific widget + * + * @details The widget that is currently in focus is the widget that + * receives mouse and keyboard events. + * Passing NULL will remove the focus from any widget. + * + * @param[in] gh The widget handle. Non-widget handles will be ignored. + * + * @api + */ + void gwinSetFocus(GHandle gh); + + /** + * @brief Get the widget that is currently in focus + * + * @details The widget that is currently in focus is the widget that + * receives mouse and keyboard events. + * + * @return The handle of the widget that is currently in focus. May be NULL. + * + * @api + */ + GHandle gwinGetFocus(void); + /** * @brief Set a window or widget to flash * diff --git a/src/gwin/gwin_wm.c b/src/gwin/gwin_wm.c index 00365c36..52f7a1aa 100644 --- a/src/gwin/gwin_wm.c +++ b/src/gwin/gwin_wm.c @@ -162,7 +162,7 @@ extern const GWindowManager GNullWindowManager; GWindowManager * _GWINwm; bool_t _gwinFlashState; - +static GHandle _widgetInFocus; static gfxSem gwinsem; static gfxQueueASync _GWINList; #if GWIN_NEED_FLASHING @@ -184,6 +184,8 @@ static volatile uint8_t RedrawPending; void _gwmInit(void) { + _widgetInFocus = 0; + gfxSemInit(&gwinsem, 1, 1); gfxQueueASyncInit(&_GWINList); #if GWIN_NEED_FLASHING @@ -571,6 +573,25 @@ GHandle gwinGetNextWindow(GHandle gh) { return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList); } +void gwinSetFocus(GHandle gh) { + // Passing NULL removes the focus from any widget + if (gh == 0) { + _widgetInFocus = 0; + return; + } + + // Only accept widgets + if (!gwinIsWidget(gh)) { + return; + } + + _widgetInFocus = gh; +} + +GHandle gwinGetFocus(void) { + return _widgetInFocus; +} + #if GWIN_NEED_FLASHING static void FlashTimerFn(void *param) { GHandle gh; From e7e4f813e1dcbc608d54e30183ccee0a036e7eb8 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Thu, 13 Aug 2015 10:36:56 +0200 Subject: [PATCH 10/45] Adding gwinIsWidget() --- src/gwin/gwin_widget.c | 44 ++++++++++++++++++++++++++++++++++-------- src/gwin/gwin_widget.h | 11 +++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 09cc91d8..da816cb5 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -21,9 +21,6 @@ // Our listener for events for widgets static GListener gl; -// The widget that is currently in focus. May be NULL. -static GHandle widgetInFocus; - // Our default style - a white background theme const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xFFFFFF), // window background @@ -101,7 +98,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { uint16_t role; #endif (void) param; - +//static GHandle widgetInFocus = 0; // Process various events switch (pe->type) { @@ -127,6 +124,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { // There is only ever one captured mouse. Prevent normal mouse processing if there is a captured mouse gh = 0; + break; } @@ -150,14 +148,35 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_KEYBOARD: // If Tab key pressed then set focus to next widget if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { - widgetInFocus = gwinGetNextWindow(widgetInFocus); + GHandle nextWidgetInFocus = 0; + bool_t loopCompleted = FALSE; + do { + nextWidgetInFocus = gwinGetNextWindow(gwinGetFocus()); + printf("0x%X\r\n", nextWidgetInFocus); + // We only look out for widgets + if (!gwinIsWidget(nextWidgetInFocus)) { + continue; + } + + if (nextWidgetInFocus == 0) { + loopCompleted = TRUE; + // Restart with the first widget + nextWidgetInFocus = gwinGetNextWindow(gwinGetFocus()); + } + } while (nextWidgetInFocus == 0 && loopCompleted == FALSE); + printf("0x%X\r\n", nextWidgetInFocus); + gwinSetFocus(nextWidgetInFocus); + + /* // If it was the last widget begin with the first one again if (widgetInFocus == 0) { widgetInFocus = gwinGetNextWindow(0); } + */ + //printf("Got now: %s\n", gwinGetClassName(gwinGetFocus())); break; } - +/* // Otherise, send keyboard events only to widget in focus if (widgetInFocus != 0) { // Make sure that it is a widget @@ -178,6 +197,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { // If we got this far we can finally pass the event ((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)widgetInFocus, pke); } +*/ #endif #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE @@ -270,8 +290,6 @@ static void gwidgetEvent(void *param, GEvent *pe) { void _gwidgetInit(void) { - widgetInFocus = 0; - geventListenerInit(&gl); geventRegisterCallback(&gl, gwidgetEvent, 0); geventAttachSource(&gl, ginputGetMouse(GMOUSE_ALL_INSTANCES), GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); @@ -444,12 +462,22 @@ const char *gwinGetText(GHandle gh) { return gw->text; } +bool_t gwinIsWidget(GHandle gh) { + if (gh->flags & GWIN_FLG_WIDGET) { + return TRUE; + } + + return FALSE; +} + void gwinSetStyle(GHandle gh, const GWidgetStyle *pstyle) { if (!(gh->flags & GWIN_FLG_WIDGET)) return; + gw->pstyle = pstyle ? pstyle : defaultStyle; gh->bgcolor = pstyle->background; gh->color = pstyle->enabled.text; + _gwinUpdate(gh); } diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index 1e97cdd2..d7b88b70 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -228,6 +228,17 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc); */ const char *gwinGetText(GHandle gh); +/** + * @brief Check whether a handles is a widget handle or not + * + * @param[in] gh The handle to check. + * + * @return TRUE if the passed handle is a widget handle. FALSE otherwise. + * + * @api + */ +bool_t gwinIsWidget(GHandle gh); + #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) /** * @brief Set the tag of a widget. From 9f5e19c15106fee1dec5725602b7559c73b29a08 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 14:11:03 +0200 Subject: [PATCH 11/45] First implementation of widget focus changing using the TAB key --- src/gwin/gwin_widget.c | 66 ++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index da816cb5..3ba04bde 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -148,39 +148,55 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_KEYBOARD: // If Tab key pressed then set focus to next widget if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { - GHandle nextWidgetInFocus = 0; - bool_t loopCompleted = FALSE; + + // Get the next widget + bool_t foundWidget = FALSE; + bool_t endOfListDetected = FALSE; + GHandle nextWidget = gwinGetFocus(); do { - nextWidgetInFocus = gwinGetNextWindow(gwinGetFocus()); - printf("0x%X\r\n", nextWidgetInFocus); - // We only look out for widgets - if (!gwinIsWidget(nextWidgetInFocus)) { + nextWidget = gwinGetNextWindow(nextWidget); + foundWidget = TRUE; + + // Begin with the first one if this is the last one + if (nextWidget == 0) { + foundWidget = FALSE; + // We go through the list twice - just to be sure + if (endOfListDetected) { + break; + } + endOfListDetected = TRUE; continue; } - - if (nextWidgetInFocus == 0) { - loopCompleted = TRUE; - // Restart with the first widget - nextWidgetInFocus = gwinGetNextWindow(gwinGetFocus()); - } - } while (nextWidgetInFocus == 0 && loopCompleted == FALSE); - printf("0x%X\r\n", nextWidgetInFocus); - gwinSetFocus(nextWidgetInFocus); - /* - // If it was the last widget begin with the first one again - if (widgetInFocus == 0) { - widgetInFocus = gwinGetNextWindow(0); - } - */ - //printf("Got now: %s\n", gwinGetClassName(gwinGetFocus())); + // Check whether this is a window or a widget + if (!gwinIsWidget(nextWidget)) { + foundWidget = FALSE; + continue; + } + + // Only focus on a widget that is visible and enabled + if (!(nextWidget->flags & GWIN_FLG_SYSVISIBLE) || !(nextWidget->flags & GWIN_FLG_ENABLED)) { + foundWidget = FALSE; + continue; + } + + // When using the TAB key we only focus on widgets that process keyboard events + if (((gwidgetVMT*)nextWidget->vmt)->KeyboardEvent == 0) { + foundWidget = FALSE; + continue; + } + + } while (foundWidget == FALSE); + gwinSetFocus(nextWidget); + break; } -/* + // Otherise, send keyboard events only to widget in focus + GHandle widgetInFocus = gwinGetFocus(); if (widgetInFocus != 0) { // Make sure that it is a widget - if (!(widgetInFocus->flags & GWIN_FLG_WIDGET)) { + if (!gwinIsWidget(widgetInFocus)) { break; } @@ -197,7 +213,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { // If we got this far we can finally pass the event ((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)widgetInFocus, pke); } -*/ + #endif #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE From 95d34760e82fa4ce5f13f6d0f25a0a7ce28c2862 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 14:12:51 +0200 Subject: [PATCH 12/45] Cleanup --- src/gwin/gwin_widget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 3ba04bde..1d270a5f 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -98,7 +98,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { uint16_t role; #endif (void) param; -//static GHandle widgetInFocus = 0; + // Process various events switch (pe->type) { From 898a42347957ae5f1205ff6543428d77811ea350 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 15:09:41 +0200 Subject: [PATCH 13/45] Fixing doxygen --- src/gdisp/gdisp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index 4513f3f7..1bc1c445 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -53,7 +53,7 @@ typedef struct point { } point, point_t; /** - * @enum justify + * @enum justify_t * @brief Type for the text justification. */ typedef enum justify { @@ -63,7 +63,7 @@ typedef enum justify { } justify_t; /** - * @enum fontmetric + * @enum fontmetric_t * @brief Type for the font metric. */ typedef enum fontmetric { From 51633811777a0424d9bfe77143384a20f832c565 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 16:24:05 +0200 Subject: [PATCH 14/45] Adding TextEdit rules --- src/gwin/gwin_rules.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gwin/gwin_rules.h b/src/gwin/gwin_rules.h index 874c59b4..3426eb67 100644 --- a/src/gwin/gwin_rules.h +++ b/src/gwin/gwin_rules.h @@ -123,6 +123,14 @@ #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." #endif #endif + #if GWIN_NEED_TEXTEDIT + #if !GDISP_NEED_TEXT + #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_TEXTEDIT is TRUE." + #endif + #if !GINPUT_NEED_KEYBOARD + #error "GWIN: GINPUT_NEED_KEYBOARD is required if GWIN_NEED_TEXTEDIT is TRUE." + #endif + #endif #endif #endif /* _GWIN_RULES_H */ From fcaa42972964e002474e6f79364f3af1a616d25c Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 18:33:16 +0200 Subject: [PATCH 15/45] 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 } From b828bf567bd6989cddb3259a65871f7460011f5d Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 18:47:55 +0200 Subject: [PATCH 16/45] Optimizing string shift operations by using memcpy() --- src/gwin/gwin_textedit.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 9c7990eb..999a91dd 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -16,7 +16,6 @@ #include "gwin_class.h" #include -#include // Some settings const int CURSOR_EXTRA_HEIGHT = 1; @@ -28,7 +27,7 @@ // cursorPos is the position of the next character // textBuffer[cursorPos++] = readKey(); -// ToDo: Optimize by using strncpy() instead +// ToDo: Optimize by using memncpy() instead static void _shiftTextLeft(char* buffer, size_t bufferSize, size_t index) { // Find the end of the string @@ -38,14 +37,12 @@ static void _shiftTextLeft(char* buffer, size_t bufferSize, size_t index) } // Shift - size_t i = 0; - for (i = index; i < indexTerminator+1; i++) { - buffer[i-1] = buffer[i]; - } - buffer[indexTerminator] = '\0'; + memcpy(&buffer[index-1], &buffer[index], indexTerminator-index); + + // Terminate the string + buffer[indexTerminator-1] = '\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 @@ -55,14 +52,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char } // Shift - size_t i = 0; - for (i = indexTerminator+1; i > index; i--) { - if (i > bufferSize-1) { - break; - } - - buffer[i] = buffer[i-1]; - } + memcpy(&buffer[index+1], &buffer[index], indexTerminator-index); // Fill the gap buffer[index] = fillChar; From 2c99e8c6869c8786e7c2ce95f2af82acf0831141 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 18:53:05 +0200 Subject: [PATCH 17/45] Adding handler for the DELETE button to the TextEdit widget --- src/gwin/gwin_textedit.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 999a91dd..8f5cb8dd 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -86,7 +86,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char // Parse the key press else if (pke->bytecount >= 1) { - // Check backspace + // Is it backspace? if (pke->c[0] == GKEY_BACKSPACE) { if (gw2obj->cursorPos == 0) { return; @@ -94,6 +94,15 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char _shiftTextLeft(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos--); } + // 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') { + return; + } + _shiftTextLeft(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos+1); + } + // Add a new character else { // Shift everything right from the cursor by one character. This includes the '\0'. Then inser the new character. From 03bc3f25a7425af6255d023fc837cc8be1453e8d Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 18:53:43 +0200 Subject: [PATCH 18/45] CleanUp --- src/gwin/gwin_textedit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 8f5cb8dd..4d1f93a6 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -27,7 +27,6 @@ // cursorPos is the position of the next character // textBuffer[cursorPos++] = readKey(); -// ToDo: Optimize by using memncpy() instead static void _shiftTextLeft(char* buffer, size_t bufferSize, size_t index) { // Find the end of the string From 668257c87262f044ff3858224ba0646f690cd706 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 18:55:36 +0200 Subject: [PATCH 19/45] Prevent buffer overflow --- src/gwin/gwin_textedit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 4d1f93a6..f90666ff 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -104,6 +104,11 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char // Add a new character else { + // Prevent buffer overflow + if (gw2obj->cursorPos >= gw2obj->bufferSize) { + return; + } + // 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]); } From 608290a261b5577825a31fb4f821e263bc43c094 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 19:36:01 +0200 Subject: [PATCH 20/45] Adding more font metrics --- src/gdisp/gdisp.c | 2 ++ src/gdisp/gdisp.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c index 377ccddb..43b470b2 100644 --- a/src/gdisp/gdisp.c +++ b/src/gdisp/gdisp.c @@ -3313,6 +3313,8 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co case fontCharPadding: return 0; case fontMinWidth: return font->min_x_advance; case fontMaxWidth: return font->max_x_advance; + case fontBaselineX: return font->baseline_x; + case fontBaselineY: return font->baseline_y; } return 0; } diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index 1bc1c445..298258cb 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -72,7 +72,9 @@ typedef enum fontmetric { fontLineSpacing, /**< The line spacing */ fontCharPadding, /**< The char padding */ fontMinWidth, /**< The minimum width */ - fontMaxWidth /**< The maximum width */ + fontMaxWidth, /**< The maximum width */ + fontBaselineX, /**< The base line in x direction */ + fontBaselineY /**< The base line in y direction */ } fontmetric_t; /** From f7075e25ed9bcd701395745161f9ee086c025e21 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 20:48:41 +0200 Subject: [PATCH 21/45] More work on the TextEdit --- docs/releases.txt | 3 +++ src/gdisp/gdisp.c | 22 ++++++++++++++++++---- src/gdisp/gdisp.h | 17 ++++++++++++++++- src/gwin/gwin_textedit.c | 21 ++++++++++++--------- src/gwin/gwin_widget.c | 10 ++++++++++ 5 files changed, 59 insertions(+), 14 deletions(-) diff --git a/docs/releases.txt b/docs/releases.txt index 9f7c16be..07c97bb2 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -10,6 +10,9 @@ FEATURE: New board STM32F746G-Discovery FEATURE: New gdisp driver STM32LTDC FEATURE: Better support for Raw32 platforms FEATURE: Implementing widget focusing. See gwinSetFocus() and gwinGetFocus() +FEATURE: Adding more font metrics (BaselineX and BaselineY) +FEATURE: Adding gdispGetStringWidthCount() +FEATURE: Adding TextEdit widget *** Release 2.3 *** diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c index 43b470b2..6e431441 100644 --- a/src/gdisp/gdisp.c +++ b/src/gdisp/gdisp.c @@ -3324,12 +3324,26 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co return mf_character_width(font, c); } - coord_t gdispGetStringWidth(const char* str, font_t font) { - if (!str) + coord_t gdispGetStringWidthCount(const char* str, font_t font, uint16_t count) { + if (!str) { return 0; + } - /* No mutex required as we only read static data */ - return mf_get_string_width(font, str, 0, 0); + // No mutex required as we only read static data + #if GDISP_NEED_TEXT_KERNING + return mf_get_string_width(font, str, count, TRUE); + #else + return mf_get_string_width(font, str, count, FALSE); + #endif + } + + coord_t gdispGetStringWidth(const char* str, font_t font) { + if (!str) { + return 0; + } + + // No mutex required as we only read static data + return gdispGetStringWidthCount(str, font, 0); } #endif diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index 298258cb..b096cb3d 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -978,7 +978,22 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co coord_t gdispGetCharWidth(char c, font_t font); /** - * @brief Get the pixel width of a string. + * @brief Get the pixel width of a string of a given length. + * @return The width of the string in pixels. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @note Passing 0 to count has the same effect as calling gdispGetStringWidt() + * + * @param[in] str The string to measure + * @param[in] font The font to use + * @param[in] count The number of characters to take into account + * + * @api + */ + coord_t gdispGetStringWidthCount(const char* str, font_t font, uint16_t count); + + /** + * @brief Get the pixel width of an entire string. * @return The width of the string in pixels. * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h * diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index f90666ff..bb25c102 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -18,7 +18,9 @@ #include // Some settings - const int CURSOR_EXTRA_HEIGHT = 1; +const int TEXT_PADDING_LEFT = 4; +const int CURSOR_PADDING_LEFT = 0; +const int CURSOR_EXTRA_HEIGHT = 1; // Macros to assist in data type conversions #define gh2obj ((GTexteditObject *)gh) @@ -185,7 +187,10 @@ GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* p widget->textBuffer[i] = '\0'; } - widget->cursorPos = 0; + // Set text and cursor position + strncpy(widget->textBuffer, gwinGetText((GHandle)widget), widget->bufferSize); // FixMe: pInit->text leads to a segfault + widget->cursorPos = strlen(widget->textBuffer); + widget->w.g.flags |= flags; gwinSetVisible(&widget->w.g, pInit->g.show); @@ -206,22 +211,20 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) color_t cursorColor = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge; // 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, 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) { + if (gwinGetFocus() == (GHandle)gw) { // 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 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 - 2; - coord_t lineX1 = gw->g.x + textWidth - 2; + coord_t lineX0 = gw->g.x + textWidth + CURSOR_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); diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 1d270a5f..9dc4d0d8 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -153,6 +153,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { bool_t foundWidget = FALSE; bool_t endOfListDetected = FALSE; GHandle nextWidget = gwinGetFocus(); + GHandle prevWidget = gwinGetFocus(); do { nextWidget = gwinGetNextWindow(nextWidget); foundWidget = TRUE; @@ -189,6 +190,15 @@ static void gwidgetEvent(void *param, GEvent *pe) { } while (foundWidget == FALSE); gwinSetFocus(nextWidget); + // Redraw the new and the previous focused widget because they usually render differently when + // they are not focused anymore (eg. no blinking cursor) + if (prevWidget != 0) { + gwinRedraw(prevWidget); + } + if (nextWidget != 0) { + gwinRedraw(nextWidget); + } + break; } From 7f70789bc3423b915195bc09dfcbff5121cd2f70 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 21:12:56 +0200 Subject: [PATCH 22/45] Adding border option to TextEdit widget --- src/gwin/gwin_textedit.c | 31 +++++++++++++++++++++++++++---- src/gwin/gwin_textedit.h | 14 +++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index bb25c102..eb6340a3 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -22,6 +22,9 @@ const int TEXT_PADDING_LEFT = 4; const int CURSOR_PADDING_LEFT = 0; const int CURSOR_EXTRA_HEIGHT = 1; +// Some flags +#define GTEXTEDIT_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 0) + // Macros to assist in data type conversions #define gh2obj ((GTexteditObject *)gh) #define gw2obj ((GTexteditObject *)gw) @@ -191,14 +194,29 @@ GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* p strncpy(widget->textBuffer, gwinGetText((GHandle)widget), widget->bufferSize); // FixMe: pInit->text leads to a segfault widget->cursorPos = strlen(widget->textBuffer); - widget->w.g.flags |= flags; + widget->w.g.flags |= flags | GTEXTEDIT_FLG_BORDER;; gwinSetVisible(&widget->w.g, pInit->g.show); return (GHandle)widget; } +void gwinTexteditSetBorder(GHandle gh, bool_t border) +{ + // Is it a valid handle? + if (gh->vmt != (gwinVMT*)&texteditVMT) { + return; + } + + if (border) { + gh2obj->w.g.flags |= GTEXTEDIT_FLG_BORDER; + } else { + gh2obj->w.g.flags &=~ GTEXTEDIT_FLG_BORDER; + } +} + static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) { + (void)param; // Is it a valid handle? @@ -211,8 +229,13 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) color_t cursorColor = (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge; // 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, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font, textColor, gw->pstyle->background, justifyLeft); + 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 (if supposed to) + if (gw2obj->w.g.flags & GTEXTEDIT_FLG_BORDER) { + 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 cursor (if focused) if (gwinGetFocus() == (GHandle)gw) { @@ -223,7 +246,7 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) coord_t cursorSpacingBottom = (gw->g.height - cursorHeight)/2 - CURSOR_EXTRA_HEIGHT; // Draw cursor - coord_t lineX0 = gw->g.x + textWidth + CURSOR_PADDING_LEFT + gdispGetFontMetric(gw->g.font, fontBaselineX)/2; + 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; diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h index 2b5e26e7..5b0cea85 100644 --- a/src/gwin/gwin_textedit.h +++ b/src/gwin/gwin_textedit.h @@ -42,7 +42,7 @@ extern "C" { #endif /** - * @brief Create a TextEdit widget. + * @brief Create a TextEdit widget * @details A TextEdit widget is a rectangular box which allows the user to input data through a keyboard. * The keyboard can either be a physical one or a virtual on-screen keyboard as the keyboard driver * is abstracted through the GINPUT module. @@ -59,6 +59,18 @@ extern "C" { GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize); #define gwinTexteditCreate(w, pInit, bufSize) gwinGTexteditCreate(GDISP, w, pInit, bufSize) +/** + * @brief Border settings for the default rendering routine + * + * @note Border is enabled by default. + * + * @param[in] gh The widget handle (must be a TextEdit handle) + * @param[in] border Shall a border be rendered? + * + * @api + */ +void gwinTexteditSetBorder(GHandle gh, bool_t border); + #ifdef __cplusplus } #endif From 22ecd9a95fc6fb3f40b6b409b35bc19d27e6ca45 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 21:20:34 +0200 Subject: [PATCH 23/45] Adding TextEdit demo --- demos/modules/gwin/textedit/demo.mk | 3 + demos/modules/gwin/textedit/gfxconf.h | 67 ++++++++++++ demos/modules/gwin/textedit/main.c | 143 ++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 demos/modules/gwin/textedit/demo.mk create mode 100644 demos/modules/gwin/textedit/gfxconf.h create mode 100644 demos/modules/gwin/textedit/main.c diff --git a/demos/modules/gwin/textedit/demo.mk b/demos/modules/gwin/textedit/demo.mk new file mode 100644 index 00000000..2cd40fd3 --- /dev/null +++ b/demos/modules/gwin/textedit/demo.mk @@ -0,0 +1,3 @@ +DEMODIR = $(GFXLIB)/demos/modules/gwin/textedit +GFXINC += $(DEMODIR) +GFXSRC += $(DEMODIR)/main.c diff --git a/demos/modules/gwin/textedit/gfxconf.h b/demos/modules/gwin/textedit/gfxconf.h new file mode 100644 index 00000000..4e518774 --- /dev/null +++ b/demos/modules/gwin/textedit/gfxconf.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012, 2013, Joel Bodenmann aka Tectu + * 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 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 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. + */ + +#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 FALSE +//#define GFX_USE_OS_WIN32 FALSE +//#define GFX_USE_OS_LINUX FALSE +//#define GFX_USE_OS_OSX FALSE + +/* GFX sub-systems to turn on */ +#define GFX_USE_GDISP TRUE +#define GFX_USE_GWIN TRUE +#define GFX_USE_GINPUT TRUE +#define GFX_USE_GEVENT TRUE +#define GFX_USE_GTIMER TRUE + +/* Features for the GDISP sub-system. */ +#define GDISP_NEED_VALIDATION TRUE +#define GDISP_NEED_CLIP TRUE +#define GDISP_NEED_TEXT TRUE +#define GDISP_NEED_TEXT_KERNING TRUE +#define GDISP_NEED_STARTUP_LOGO TRUE + +/* GDISP fonts to include */ +#define GDISP_INCLUDE_FONT_UI2 TRUE +#define GDISP_INCLUDE_FONT_DEJAVUSANS16 TRUE + +/* Features for the GWIN subsystem. */ +#define GWIN_NEED_CONSOLE TRUE +#define GWIN_NEED_TEXTEDIT TRUE +#define GWIN_NEED_BUTTON TRUE + +/* Features for the GINPUT subsystem. */ +#define GINPUT_NEED_MOUSE TRUE +#define GINPUT_NEED_KEYBOARD TRUE + +#endif /* _GFXCONF_H */ + diff --git a/demos/modules/gwin/textedit/main.c b/demos/modules/gwin/textedit/main.c new file mode 100644 index 00000000..950475b5 --- /dev/null +++ b/demos/modules/gwin/textedit/main.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2012, 2013, Joel Bodenmann aka Tectu + * 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 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 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" + +static GHandle ghConsole; +static GHandle ghTextedit1; +static GHandle ghTextedit2; +static GHandle ghTextedit3; +static GListener gl; + +static void guiCreate() +{ + GWidgetInit wi; + gwinWidgetClearInit(&wi); + + // Console + wi.g.show = TRUE; + wi.g.x = 0; + wi.g.y = 0; + wi.g.width = gdispGetWidth()/2; + wi.g.height = gdispGetHeight(); + ghConsole = gwinConsoleCreate(0, &wi.g); + gwinSetColor(ghConsole, Yellow); + gwinSetBgColor(ghConsole, Black); + gwinSetFont(ghConsole, gdispOpenFont("UI2")); + gwinClear(ghConsole); + + // TextEdit1 + wi.g.show = TRUE; + wi.g.x = gdispGetWidth()/2 + 10; + wi.g.y = 20; + wi.g.width = 200; + wi.g.height = 35; + wi.text = "Use the TAB key"; + ghTextedit1 = gwinTexteditCreate(0, &wi, 100); + + // TextEdit2 + wi.g.show = TRUE; + wi.g.x = gdispGetWidth()/2 + 10; + wi.g.y = 100; + wi.g.width = 200; + wi.g.height = 35; + wi.text = "to switch between"; + ghTextedit2 = gwinTexteditCreate(0, &wi, 20); + gwinTexteditSetBorder(ghTextedit2, FALSE); + + // TextEdit3 + wi.g.show = TRUE; + wi.g.x = gdispGetWidth()/2 + 10; + wi.g.y = 180; + wi.g.width = 200; + wi.g.height = 35; + wi.text = "the different widgets"; + ghTextedit3 = gwinTexteditCreate(0, &wi, 100); + gwinTexteditSetBorder(ghTextedit3, TRUE); +} + +int main(void) { + GEventKeyboard* pk; + unsigned i; + + gfxInit(); + + gdispClear(Silver); + gwinSetDefaultFont(gdispOpenFont("DejaVuSans16")); + gwinSetDefaultStyle(&WhiteWidgetStyle, FALSE); + gwinSetDefaultColor(Black); + gwinSetDefaultBgColor(White); + + geventListenerInit(&gl); + geventAttachSource(&gl, ginputGetKeyboard(0), GLISTEN_KEYTRANSITIONS|GLISTEN_KEYUP); + + guiCreate(); + + gwinPrintf(ghConsole, "Keyboard Monitor...\n"); + + while(1) { + // Get an Event + pk = (GEventKeyboard *)geventEventWait(&gl, TIME_INFINITE); + + if (pk->type != GEVENT_KEYBOARD) + continue; + + 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"); + } +} + From 755b7a45ab517a303510b728a9c54e48f9216557 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 23:42:49 +0200 Subject: [PATCH 24/45] Adding missing break statement --- src/gwin/gwin_widget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 9dc4d0d8..c536b88a 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -223,7 +223,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { // If we got this far we can finally pass the event ((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)widgetInFocus, pke); } - + break; #endif #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE From 668b161f0eead1b9687305281eb59be0e0713bbe Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Fri, 14 Aug 2015 23:51:28 +0200 Subject: [PATCH 25/45] Adding color to widget style for focused widgets --- docs/releases.txt | 1 + src/gwin/gwin_textedit.c | 15 ++++++++++++--- src/gwin/gwin_widget.c | 18 ++++++++++-------- src/gwin/gwin_widget.h | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/docs/releases.txt b/docs/releases.txt index 07c97bb2..804f2c1b 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -13,6 +13,7 @@ FEATURE: Implementing widget focusing. See gwinSetFocus() and gwinGetFocus() FEATURE: Adding more font metrics (BaselineX and BaselineY) FEATURE: Adding gdispGetStringWidthCount() FEATURE: Adding TextEdit widget +FEATURE: Added color to widget style for focused widgets *** Release 2.3 *** diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index eb6340a3..21415b9c 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -18,9 +18,10 @@ #include // Some settings -const int TEXT_PADDING_LEFT = 4; -const int CURSOR_PADDING_LEFT = 0; -const int CURSOR_EXTRA_HEIGHT = 1; +const int TEXT_PADDING_LEFT = 4; +const int FOCUS_BORDER_THICKNESS = 3; +const int CURSOR_PADDING_LEFT = 0; +const int CURSOR_EXTRA_HEIGHT = 1; // Some flags #define GTEXTEDIT_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 0) @@ -237,6 +238,14 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) 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 + if (gwinGetFocus() == (GHandle)gw) { + int i = 0; + for (i = 0; i < FOCUS_BORDER_THICKNESS; i++) { + gdispGDrawBox(gw->g.display, gw->g.x+i, gw->g.y+i, gw->g.width-2*i, gw->g.height-2*i, gw->pstyle->focus); + } + } + // Render cursor (if focused) if (gwinGetFocus() == (GHandle)gw) { // Calculate cursor stuff diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index c536b88a..f135b306 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -24,13 +24,14 @@ static GListener gl; // Our default style - a white background theme const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xFFFFFF), // window background + HTML2COLOR(0x2A8FCD), // focused // enabled color set { HTML2COLOR(0x000000), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xE0E0E0), // progress - inactive area + HTML2COLOR(0xE0E0E0) // progress - inactive area }, // disabled color set @@ -38,7 +39,7 @@ const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0x808080), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xC0E0C0), // progress - active area + HTML2COLOR(0xC0E0C0) // progress - active area }, // pressed color set @@ -46,20 +47,21 @@ const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0x404040), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0x808080), // fill - HTML2COLOR(0x00E000), // progress - active area - }, + HTML2COLOR(0x00E000) // progress - active area + } }; /* Our black style */ const GWidgetStyle BlackWidgetStyle = { HTML2COLOR(0x000000), // window background + HTML2COLOR(0x2A8FCD), // focused // enabled color set { HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0xC0C0C0), // edge HTML2COLOR(0x606060), // fill - HTML2COLOR(0x404040), // progress - inactive area + HTML2COLOR(0x404040) // progress - inactive area }, // disabled color set @@ -67,7 +69,7 @@ const GWidgetStyle BlackWidgetStyle = { HTML2COLOR(0x808080), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0x404040), // fill - HTML2COLOR(0x004000), // progress - active area + HTML2COLOR(0x004000) // progress - active area }, // pressed color set @@ -75,8 +77,8 @@ const GWidgetStyle BlackWidgetStyle = { HTML2COLOR(0xFFFFFF), // text HTML2COLOR(0xC0C0C0), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0x008000), // progress - active area - }, + HTML2COLOR(0x008000) // progress - active area + } }; static const GWidgetStyle * defaultStyle = &BlackWidgetStyle; diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index d7b88b70..4f164184 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -49,6 +49,7 @@ typedef struct GColorSet { */ typedef struct GWidgetStyle { color_t background; // @< The window background color + color_t focus; // @< The color when a widget is focused GColorSet enabled; // @< The colors when enabled GColorSet disabled; // @< The colors when disabled GColorSet pressed; // @< The colors when pressed From ef813f44d3c10aac784f9adc7ce66509d1f31d7e Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 16 Aug 2015 00:42:22 +0200 Subject: [PATCH 26/45] Improving doxygen --- src/gdisp/gdisp.h | 6 +-- src/gwin/gwin.h | 31 +++++++----- src/gwin/gwin_class.h | 110 ++++++++++++++++++++--------------------- src/gwin/gwin_widget.h | 48 +++++++++--------- 4 files changed, 100 insertions(+), 95 deletions(-) diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index b096cb3d..1ff15e44 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -53,7 +53,7 @@ typedef struct point { } point, point_t; /** - * @enum justify_t + * @enum justify * @brief Type for the text justification. */ typedef enum justify { @@ -63,7 +63,7 @@ typedef enum justify { } justify_t; /** - * @enum fontmetric_t + * @enum fontmetric * @brief Type for the font metric. */ typedef enum fontmetric { @@ -111,7 +111,7 @@ typedef enum powermode { /* * Our black box display structure. */ -typedef struct GDisplay GDisplay; +typedef struct GDisplay GDisplay; /** * @brief The default screen to use for the gdispXXXX calls. diff --git a/src/gwin/gwin.h b/src/gwin/gwin.h index 329be4c4..4b29f1ee 100644 --- a/src/gwin/gwin.h +++ b/src/gwin/gwin.h @@ -38,19 +38,22 @@ typedef struct GWindowObject *GHandle; typedef struct GWindowObject { #if GWIN_NEED_WINDOWMANAGER // This MUST be the first member of the structure - gfxQueueASyncItem wmq; // @< The next window (for the window manager) + gfxQueueASyncItem wmq; /**< The next window (for the window manager) */ #endif - const struct gwinVMT *vmt; // @< The VMT for this GWIN - GDisplay * display; // @< The display this window is on. - coord_t x, y; // @< Screen relative position - coord_t width, height; // @< Dimensions of this window - color_t color, bgcolor; // @< The current drawing colors - uint32_t flags; // @< Window flags (the meaning is private to the GWIN class) + const struct gwinVMT* vmt; /**< The VMT for this GWIN */ + GDisplay * display; /**< The display this window is on */ + coord_t x; /**< The position relative to the screen */ + coord_t y; /**< The position relative to the screen */ + coord_t width; /**< The width of this window */ + coord_t height; /**< The height of this window */ + color_t color; /**< The current foreground drawing color */ + color_t bgcolor; /**< The current background drawing color */ + uint32_t flags; /**< Window flags (the meaning is private to the GWIN class) */ #if GDISP_NEED_TEXT - font_t font; // @< The current font + font_t font; /**< The current font */ #endif #if GWIN_NEED_CONTAINERS - GHandle parent; // @< The parent window + GHandle parent; /**< The parent window */ #endif } GWindowObject, * GHandle; /** @} */ @@ -68,11 +71,13 @@ typedef struct GWindowObject { * @{ */ typedef struct GWindowInit { - coord_t x, y; // @< The initial position relative to its parent - coord_t width, height; // @< The initial dimension - bool_t show; // @< Should the window be visible initially + coord_t x; /**< The initial position relative to its parent */ + coord_t y; /**< The initial position relative to its parent */ + coord_t width; /**< The width */ + coord_t height; /**< The height */ + bool_t show; /**< Should the window be visible initially */ #if GWIN_NEED_CONTAINERS - GHandle parent; // @< The parent - must be a container or NULL + GHandle parent; /**< The parent - must be a container or NULL */ #endif } GWindowInit; /** @} */ diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h index 45467597..81b5404a 100644 --- a/src/gwin/gwin_class.h +++ b/src/gwin/gwin_class.h @@ -31,25 +31,25 @@ * @brief The predefined flags for a Window * @{ */ -#define GWIN_FIRST_CONTROL_FLAG 0x00000001 // @< 8 bits free for the control to use -#define GWIN_LAST_CONTROL_FLAG 0x00000080 // @< 8 bits free for the control to use -#define GWIN_FLG_VISIBLE 0x00000100 // @< The window is "visible" -#define GWIN_FLG_SYSVISIBLE 0x00000200 // @< The window is visible after parents are tested -#define GWIN_FLG_ENABLED 0x00000400 // @< The window is "enabled" -#define GWIN_FLG_SYSENABLED 0x00000800 // @< The window is enabled after parents are tested -#define GWIN_FLG_DYNAMIC 0x00001000 // @< The GWIN structure is allocated -#define GWIN_FLG_ALLOCTXT 0x00002000 // @< The text/label is allocated -#define GWIN_FLG_NEEDREDRAW 0x00004000 // @< Redraw is needed but has been delayed -#define GWIN_FLG_BGREDRAW 0x00008000 // @< On redraw, if not visible redraw the revealed under-side -#define GWIN_FLG_SUPERMASK 0x000F0000 // @< The bit mask to leave just the window superclass type -#define GWIN_FLG_WIDGET 0x00010000 // @< This is a widget -#define GWIN_FLG_CONTAINER 0x00020000 // @< This is a container -#define GWIN_FLG_MINIMIZED 0x00100000 // @< The window is minimized -#define GWIN_FLG_MAXIMIZED 0x00200000 // @< The window is maximized -#define GWIN_FLG_MOUSECAPTURE 0x00400000 // @< The window has captured the mouse -#define GWIN_FLG_FLASHING 0x00800000 // @< The window is flashing - see the _gwinFlashState boolean -#define GWIN_FIRST_WM_FLAG 0x01000000 // @< 8 bits free for the window manager to use -#define GWIN_LAST_WM_FLAG 0x80000000 // @< 8 bits free for the window manager to use +#define GWIN_FIRST_CONTROL_FLAG 0x00000001 /**< 8 bits free for the control to use */ +#define GWIN_LAST_CONTROL_FLAG 0x00000080 /**< 8 bits free for the control to use */ +#define GWIN_FLG_VISIBLE 0x00000100 /**< The window is "visible" */ +#define GWIN_FLG_SYSVISIBLE 0x00000200 /**< The window is visible after parents are tested */ +#define GWIN_FLG_ENABLED 0x00000400 /**< The window is "enabled" */ +#define GWIN_FLG_SYSENABLED 0x00000800 /**< The window is enabled after parents are tested */ +#define GWIN_FLG_DYNAMIC 0x00001000 /**< The GWIN structure is allocated */ +#define GWIN_FLG_ALLOCTXT 0x00002000 /**< The text/label is allocated */ +#define GWIN_FLG_NEEDREDRAW 0x00004000 /**< Redraw is needed but has been delayed */ +#define GWIN_FLG_BGREDRAW 0x00008000 /**< On redraw, if not visible redraw the revealed under-side */ +#define GWIN_FLG_SUPERMASK 0x000F0000 /**< The bit mask to leave just the window superclass type */ +#define GWIN_FLG_WIDGET 0x00010000 /**< This is a widget */ +#define GWIN_FLG_CONTAINER 0x00020000 /**< This is a container */ +#define GWIN_FLG_MINIMIZED 0x00100000 /**< The window is minimized */ +#define GWIN_FLG_MAXIMIZED 0x00200000 /**< The window is maximized */ +#define GWIN_FLG_MOUSECAPTURE 0x00400000 /**< The window has captured the mouse */ +#define GWIN_FLG_FLASHING 0x00800000 /**< The window is flashing - see the _gwinFlashState boolean */ +#define GWIN_FIRST_WM_FLAG 0x01000000 /**< 8 bits free for the window manager to use */ +#define GWIN_LAST_WM_FLAG 0x80000000 /**< 8 bits free for the window manager to use */ /** @} */ /** @@ -57,11 +57,11 @@ * @{ */ typedef struct gwinVMT { - const char * classname; // @< The GWIN classname (mandatory) - size_t size; // @< The size of the class object - void (*Destroy) (GWindowObject *gh); // @< The GWIN destroy function (optional) - void (*Redraw) (GWindowObject *gh); // @< The GWIN redraw routine (optional) - void (*AfterClear) (GWindowObject *gh); // @< The GWIN after-clear function (optional) + const char * classname; /**< The GWIN classname (mandatory) */ + size_t size; /**< The size of the class object */ + void (*Destroy) (GWindowObject *gh); /**< The GWIN destroy function (optional) */ + void (*Redraw) (GWindowObject *gh); /**< The GWIN redraw routine (optional) */ + void (*AfterClear) (GWindowObject *gh); /**< The GWIN after-clear function (optional) */ } gwinVMT; /** @} */ @@ -87,35 +87,35 @@ typedef struct gwinVMT { * @{ */ typedef struct gwidgetVMT { - struct gwinVMT g; // @< This is still a GWIN - void (*DefaultDraw) (GWidgetObject *gw, void *param); // @< The default drawing routine (mandatory) + struct gwinVMT g; /**< This is still a GWIN */ + void (*DefaultDraw) (GWidgetObject *gw, void *param); /**< The default drawing routine (mandatory) */ #if GINPUT_NEED_MOUSE struct { - void (*MouseDown) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse down events (optional) - void (*MouseUp) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse up events (optional) - void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); // @< Process mouse move events (optional) + void (*MouseDown) (GWidgetObject *gw, coord_t x, coord_t y); /**< Process mouse down events (optional) */ + void (*MouseUp) (GWidgetObject *gw, coord_t x, coord_t y); /**< Process mouse up events (optional) */ + void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); /**< Process mouse move events (optional) */ }; #endif #if GINPUT_NEED_KEYBOARD struct { - void (*KeyboardEvent) (GWidgetObject *gw, GEventKeyboard *pke); // @< Process keyboard events (optional) + void (*KeyboardEvent) (GWidgetObject *gw, GEventKeyboard *pke); /**< Process keyboard events (optional) */ }; #endif #if GINPUT_NEED_TOGGLE struct { - uint16_t toggleroles; // @< The roles supported for toggles (0->toggleroles-1) - void (*ToggleAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Assign a toggle to a role (optional) - uint16_t (*ToggleGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) - void (*ToggleOff) (GWidgetObject *gw, uint16_t role); // @< Process toggle off events (optional) - void (*ToggleOn) (GWidgetObject *gw, uint16_t role); // @< Process toggle on events (optional) + uint16_t toggleroles; /**< The roles supported for toggles (0->toggleroles-1) */ + void (*ToggleAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); /**< Assign a toggle to a role (optional) */ + uint16_t (*ToggleGet) (GWidgetObject *gw, uint16_t role); /**< Return the instance for a particular role (optional) */ + void (*ToggleOff) (GWidgetObject *gw, uint16_t role); /**< Process toggle off events (optional) */ + void (*ToggleOn) (GWidgetObject *gw, uint16_t role); /**< Process toggle on events (optional) */ }; #endif #if GINPUT_NEED_DIAL struct { - uint16_t dialroles; // @< The roles supported for dials (0->dialroles-1) - void (*DialAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); // @< Test the role and save the dial instance handle (optional) - uint16_t (*DialGet) (GWidgetObject *gw, uint16_t role); // @< Return the instance for a particular role (optional) - void (*DialMove) (GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max); // @< Process dial move events (optional) + uint16_t dialroles; /**< The roles supported for dials (0->dialroles-1) */ + void (*DialAssign) (GWidgetObject *gw, uint16_t role, uint16_t instance); /**< Test the role and save the dial instance handle (optional) */ + uint16_t (*DialGet) (GWidgetObject *gw, uint16_t role); /**< Return the instance for a particular role (optional) */ + void (*DialMove) (GWidgetObject *gw, uint16_t role, uint16_t value, uint16_t max); /**< Process dial move events (optional) */ }; #endif } gwidgetVMT; @@ -135,12 +135,12 @@ typedef struct gwinVMT { */ typedef struct gcontainerVMT { gwidgetVMT gw; - coord_t (*LeftBorder) (GHandle gh); // @< The size of the left border (mandatory) - coord_t (*TopBorder) (GHandle gh); // @< The size of the top border (mandatory) - coord_t (*RightBorder) (GHandle gh); // @< The size of the right border (mandatory) - coord_t (*BottomBorder) (GHandle gh); // @< The size of the bottom border (mandatory) - void (*NotifyAdd) (GHandle gh, GHandle ghChild); // @< Notification that a child has been added (optional) - void (*NotifyDelete) (GHandle gh, GHandle ghChild); // @< Notification that a child has been deleted (optional) + coord_t (*LeftBorder) (GHandle gh); /**< The size of the left border (mandatory) */ + coord_t (*TopBorder) (GHandle gh); /**< The size of the top border (mandatory) */ + coord_t (*RightBorder) (GHandle gh); /**< The size of the right border (mandatory) */ + coord_t (*BottomBorder) (GHandle gh); /**< The size of the bottom border (mandatory) */ + void (*NotifyAdd) (GHandle gh, GHandle ghChild); /**< Notification that a child has been added (optional) */ + void (*NotifyDelete) (GHandle gh, GHandle ghChild); /**< Notification that a child has been deleted (optional) */ } gcontainerVMT; /** @} */ #endif @@ -156,22 +156,22 @@ typedef struct gwinVMT { * @{ */ typedef struct gwmVMT { - void (*Init) (void); // @< The window manager has just been set as the current window manager - void (*DeInit) (void); // @< The window manager has just been removed as the current window manager - bool_t (*Add) (GHandle gh, const GWindowInit *pInit); // @< A window has been added - void (*Delete) (GHandle gh); // @< A window has been deleted - void (*Redraw) (GHandle gh); // @< A window needs to be redraw (or undrawn) - void (*Size) (GHandle gh, coord_t w, coord_t h); // @< A window wants to be resized - void (*Move) (GHandle gh, coord_t x, coord_t y); // @< A window wants to be moved - void (*Raise) (GHandle gh); // @< A window wants to be on top - void (*MinMax) (GHandle gh, GWindowMinMax minmax); // @< A window wants to be minimized/maximised + void (*Init) (void); /**< The window manager has just been set as the current window manager */ + void (*DeInit) (void); /**< The window manager has just been removed as the current window manager */ + bool_t (*Add) (GHandle gh, const GWindowInit *pInit); /**< A window has been added */ + void (*Delete) (GHandle gh); /**< A window has been deleted */ + void (*Redraw) (GHandle gh); /**< A window needs to be redraw (or undrawn) */ + void (*Size) (GHandle gh, coord_t w, coord_t h); /**< A window wants to be resized */ + void (*Move) (GHandle gh, coord_t x, coord_t y); /**< A window wants to be moved */ + void (*Raise) (GHandle gh); /**< A window wants to be on top */ + void (*MinMax) (GHandle gh, GWindowMinMax minmax); /**< A window wants to be minimized/maximised */ } gwmVMT; /** @} */ /** * @brief The current window manager */ - extern GWindowManager * _GWINwm; + extern GWindowManager* _GWINwm; extern bool_t _gwinFlashState; #endif diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index 4f164184..3aa1b9f1 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -33,10 +33,10 @@ struct GWidgetObject; * @{ */ typedef struct GColorSet { - color_t text; // @< The text color - color_t edge; // @< The edge color - color_t fill; // @< The fill color - color_t progress; // @< The color of progress bars + color_t text; /**< The text color */ + color_t edge; /**< The edge color */ + color_t fill; /**< The fill color */ + color_t progress; /**< The color of progress bars */ } GColorSet; /** @} */ @@ -48,11 +48,11 @@ typedef struct GColorSet { * @{ */ typedef struct GWidgetStyle { - color_t background; // @< The window background color - color_t focus; // @< The color when a widget is focused - GColorSet enabled; // @< The colors when enabled - GColorSet disabled; // @< The colors when disabled - GColorSet pressed; // @< The colors when pressed + color_t background; /**< The window background color */ + color_t focus; /**< The color when a widget is focused */ + GColorSet enabled; /**< The colors when enabled */ + GColorSet disabled; /**< The colors when disabled */ + GColorSet pressed; /**< The colors when pressed */ } GWidgetStyle; /** @} */ @@ -93,13 +93,13 @@ typedef uint16_t WidgetTag; * @{ */ typedef struct GWidgetInit { - GWindowInit g; // @< The GWIN initializer - const char * text; // @< The initial text - CustomWidgetDrawFunction customDraw; // @< A custom draw function - use NULL for the standard - void * customParam; // @< A parameter for the custom draw function (default = NULL) - const GWidgetStyle * customStyle; // @< A custom style to use - use NULL for the default style + GWindowInit g; /**< The GWIN initializer */ + const char * text; /**< The initial text */ + CustomWidgetDrawFunction customDraw; /**< A custom draw function - use NULL for the standard */ + void * customParam; /**< A parameter for the custom draw function (default = NULL) */ + const GWidgetStyle * customStyle; /**< A custom style to use - use NULL for the default style */ #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) - WidgetTag tag; // @< The tag to associate with the widget + WidgetTag tag; /**< The tag to associate with the widget */ #endif } GWidgetInit; /** @} */ @@ -114,13 +114,13 @@ typedef struct GWidgetInit { * @{ */ typedef struct GWidgetObject { - GWindowObject g; // @< This is still a GWIN - const char * text; // @< The widget text - CustomWidgetDrawFunction fnDraw; // @< The current draw function - void * fnParam; // @< A parameter for the current draw function - const GWidgetStyle * pstyle; // @< The current widget style colors + GWindowObject g; /**< This is still a GWIN */ + const char * text; /**< The widget text */ + CustomWidgetDrawFunction fnDraw; /**< The current draw function */ + void * fnParam; /**< A parameter for the current draw function */ + const GWidgetStyle * pstyle; /**< The current widget style colors */ #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) - WidgetTag tag; // @< The widget tag + WidgetTag tag; /**< The widget tag */ #endif } GWidgetObject; /** @} */ @@ -145,10 +145,10 @@ typedef struct GWidgetObject { * @{ */ typedef struct GEventGWin { - GEventType type; // The type of this event - GHandle gwin; // The gwin window handle + GEventType type; /**< The type of this event */ + GHandle gwin; /**< The gwin window handle */ #if GWIN_NEED_WIDGET && GWIN_WIDGET_TAGS - WidgetTag tag; // The tag (if applicable) + WidgetTag tag; /**< The tag (if applicable) */ #endif } GEventGWin; /** @} */ From bd353d37e3db2aea609600342d8657c67ea8e44d Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 16 Aug 2015 01:24:18 +0200 Subject: [PATCH 27/45] Typo --- src/gwin/gwin_textedit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 21415b9c..8d242ec7 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -147,7 +147,7 @@ static const gwidgetVMT texteditVMT = { #endif #if GINPUT_NEED_KEYBOARD { - _keyboardEvent, // Process keyboard key down events + _keyboardEvent // Process keyboard key down events }, #endif #if GINPUT_NEED_TOGGLE From 63c5e4949f63470bd20d2bc35a9fcc0b50795910 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 16 Aug 2015 01:35:46 +0200 Subject: [PATCH 28/45] Adding KEYUP events --- src/gwin/gwin_textedit.c | 5 +++++ src/gwin/gwin_widget.c | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 8d242ec7..99708a0a 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -67,6 +67,11 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char #if GINPUT_NEED_KEYBOARD static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) { + // Only react on KEYDOWN events. Ignore KEYUP events. + if (pke->keystate & GKEYSTATE_KEYUP) { + return; + } + // Is it a special key? if (pke->keystate & GKEYSTATE_SPECIAL) { // Arrow keys to move the cursor diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index f135b306..84168b09 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -151,6 +151,11 @@ static void gwidgetEvent(void *param, GEvent *pe) { // If Tab key pressed then set focus to next widget if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { + // Only react on KEYDOWN events. Ignore KEYUP events. + if (pke->keystate & GKEYSTATE_KEYUP) { + break; + } + // Get the next widget bool_t foundWidget = FALSE; bool_t endOfListDetected = FALSE; @@ -323,7 +328,7 @@ void _gwidgetInit(void) geventAttachSource(&gl, ginputGetMouse(GMOUSE_ALL_INSTANCES), GLISTEN_MOUSEMETA|GLISTEN_MOUSEDOWNMOVES); #if GINPUT_NEED_KEYBOARD - geventAttachSource(&gl, ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES), 0); + geventAttachSource(&gl, ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES), GLISTEN_KEYUP); #endif } From 9915fb625edc552c804224d2a425aec271455668 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 16 Aug 2015 01:36:33 +0200 Subject: [PATCH 29/45] Adding keyboard event handler to button widget --- src/gwin/gwin_button.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/gwin/gwin_button.c b/src/gwin/gwin_button.c index 8a178b72..854f540b 100644 --- a/src/gwin/gwin_button.c +++ b/src/gwin/gwin_button.c @@ -50,6 +50,24 @@ } #endif +#if GINPUT_NEED_KEYBOARD + static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) + { + // ENTER and SPACE keys to press the button + if (pke->c[0] == GKEY_ENTER || pke->c[0] == GKEY_SPACE) { + + // Press or release event? + if (pke->keystate & GKEYSTATE_KEYUP) { + gw->g.flags &= ~GBUTTON_FLG_PRESSED; + } else { + gw->g.flags |= GBUTTON_FLG_PRESSED; + } + } + + _gwinUpdate((GHandle)gw); + } +#endif + #if GINPUT_NEED_TOGGLE // A toggle off has occurred static void ButtonToggleOff(GWidgetObject *gw, uint16_t role) { @@ -97,7 +115,7 @@ static const gwidgetVMT buttonVMT = { #endif #if GINPUT_NEED_KEYBOARD { - 0 // Process keyboard events + _keyboardEvent // Process keyboard events }, #endif #if GINPUT_NEED_TOGGLE From c451880d807cb07986cb3553e4424219166ced5e Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 16 Aug 2015 01:41:33 +0200 Subject: [PATCH 30/45] Adding keyboard event handler to checkbox widget --- src/gwin/gwin_checkbox.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/gwin/gwin_checkbox.c b/src/gwin/gwin_checkbox.c index 83d5bc38..6401092d 100644 --- a/src/gwin/gwin_checkbox.c +++ b/src/gwin/gwin_checkbox.c @@ -55,6 +55,23 @@ static void SendCheckboxEvent(GWidgetObject *gw) { } #endif +#if GINPUT_NEED_KEYBOARD + static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) + { + // Only react on KEYDOWN events. Ignore KEYUP events. + if (pke->keystate & GKEYSTATE_KEYUP) { + break; + } + + // ENTER and SPACE keys to check/uncheck the checkbox + if (pke->c[0] == GKEY_ENTER || pke->c[0] == GKEY_SPACE) { + gw->g.flags ^= GCHECKBOX_FLG_CHECKED; + } + + _gwinUpdate((GHandle)gw); + } +#endif + #if GINPUT_NEED_TOGGLE static void CheckboxToggleOn(GWidgetObject *gw, uint16_t role) { (void) role; @@ -93,7 +110,7 @@ static const gwidgetVMT checkboxVMT = { #endif #if GINPUT_NEED_KEYBOARD { - 0 // Process keyboard events + _keyboardEvent // Process keyboard events }, #endif #if GINPUT_NEED_TOGGLE From 377fe644d1233e955dfd05e40fa9d335447de325 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sun, 16 Aug 2015 14:30:25 +1000 Subject: [PATCH 31/45] Coding style, comments, duplicate symbols and other minor fixes --- drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c | 9 ++---- src/gdisp/gdisp.c | 8 +---- src/gdisp/gdisp.h | 2 +- src/gwin/gwin_button.c | 4 +-- src/gwin/gwin_checkbox.c | 4 +-- src/gwin/gwin_class.h | 2 +- src/gwin/gwin_widget.c | 32 +++++++------------ 7 files changed, 20 insertions(+), 41 deletions(-) diff --git a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c index 9c2de092..0fb8ba5a 100644 --- a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c +++ b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c @@ -316,10 +316,8 @@ LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay* g) switch(g->p.x) { case GDISP_CONTROL_POWER: // Don't do anything if it is the same power mode - if (g->g.Powermode == (powermode_t)g->p.ptr) { + if (g->g.Powermode == (powermode_t)g->p.ptr) return; - } - switch((powermode_t)g->p.ptr) { default: return; @@ -329,11 +327,8 @@ LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay* g) return; case GDISP_CONTROL_ORIENTATION: - // Don't do anything if it is the same power mode - if (g->g.Orientation == (orientation_t)g->p.ptr) { + if (g->g.Orientation == (orientation_t)g->p.ptr) return; - } - switch((orientation_t)g->p.ptr) { case GDISP_ROTATE_0: case GDISP_ROTATE_180: diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c index 6e431441..0ddee1f6 100644 --- a/src/gdisp/gdisp.c +++ b/src/gdisp/gdisp.c @@ -3325,9 +3325,8 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co } coord_t gdispGetStringWidthCount(const char* str, font_t font, uint16_t count) { - if (!str) { + if (!str) return 0; - } // No mutex required as we only read static data #if GDISP_NEED_TEXT_KERNING @@ -3338,11 +3337,6 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co } coord_t gdispGetStringWidth(const char* str, font_t font) { - if (!str) { - return 0; - } - - // No mutex required as we only read static data return gdispGetStringWidthCount(str, font, 0); } #endif diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index 1ff15e44..d0c31ebb 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -978,7 +978,7 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co coord_t gdispGetCharWidth(char c, font_t font); /** - * @brief Get the pixel width of a string of a given length. + * @brief Get the pixel width of a string of a given character length. * @return The width of the string in pixels. * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h * diff --git a/src/gwin/gwin_button.c b/src/gwin/gwin_button.c index 854f540b..7705c4ce 100644 --- a/src/gwin/gwin_button.c +++ b/src/gwin/gwin_button.c @@ -51,7 +51,7 @@ #endif #if GINPUT_NEED_KEYBOARD - static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) + static void ButtonKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { // ENTER and SPACE keys to press the button if (pke->c[0] == GKEY_ENTER || pke->c[0] == GKEY_SPACE) { @@ -115,7 +115,7 @@ static const gwidgetVMT buttonVMT = { #endif #if GINPUT_NEED_KEYBOARD { - _keyboardEvent // Process keyboard events + ButtonKeyboard // Process keyboard events }, #endif #if GINPUT_NEED_TOGGLE diff --git a/src/gwin/gwin_checkbox.c b/src/gwin/gwin_checkbox.c index 6401092d..d6906b71 100644 --- a/src/gwin/gwin_checkbox.c +++ b/src/gwin/gwin_checkbox.c @@ -56,7 +56,7 @@ static void SendCheckboxEvent(GWidgetObject *gw) { #endif #if GINPUT_NEED_KEYBOARD - static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) + static void CheckboxKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { // Only react on KEYDOWN events. Ignore KEYUP events. if (pke->keystate & GKEYSTATE_KEYUP) { @@ -110,7 +110,7 @@ static const gwidgetVMT checkboxVMT = { #endif #if GINPUT_NEED_KEYBOARD { - _keyboardEvent // Process keyboard events + CheckboxKeyboard // Process keyboard events }, #endif #if GINPUT_NEED_TOGGLE diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h index 81b5404a..03d2d506 100644 --- a/src/gwin/gwin_class.h +++ b/src/gwin/gwin_class.h @@ -171,7 +171,7 @@ typedef struct gwinVMT { /** * @brief The current window manager */ - extern GWindowManager* _GWINwm; + extern GWindowManager *_GWINwm; extern bool_t _gwinFlashState; #endif diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 84168b09..47e60e82 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -152,9 +152,8 @@ static void gwidgetEvent(void *param, GEvent *pe) { if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { // Only react on KEYDOWN events. Ignore KEYUP events. - if (pke->keystate & GKEYSTATE_KEYUP) { + if (pke->keystate & GKEYSTATE_KEYUP) break; - } // Get the next widget bool_t foundWidget = FALSE; @@ -169,9 +168,8 @@ static void gwidgetEvent(void *param, GEvent *pe) { if (nextWidget == 0) { foundWidget = FALSE; // We go through the list twice - just to be sure - if (endOfListDetected) { + if (endOfListDetected) break; - } endOfListDetected = TRUE; continue; } @@ -199,12 +197,10 @@ static void gwidgetEvent(void *param, GEvent *pe) { // Redraw the new and the previous focused widget because they usually render differently when // they are not focused anymore (eg. no blinking cursor) - if (prevWidget != 0) { + if (prevWidget != 0) gwinRedraw(prevWidget); - } - if (nextWidget != 0) { + if (nextWidget != 0) gwinRedraw(nextWidget); - } break; } @@ -213,19 +209,16 @@ static void gwidgetEvent(void *param, GEvent *pe) { GHandle widgetInFocus = gwinGetFocus(); if (widgetInFocus != 0) { // Make sure that it is a widget - if (!gwinIsWidget(widgetInFocus)) { + if (!gwinIsWidget(widgetInFocus)) break; - } // Make sure that the widget is enabled and visible - if (!(widgetInFocus->flags & GWIN_FLG_SYSVISIBLE) || !(widgetInFocus->flags & GWIN_FLG_ENABLED)) { + if (!(widgetInFocus->flags & GWIN_FLG_SYSVISIBLE) || !(widgetInFocus->flags & GWIN_FLG_ENABLED)) break; - } // Check whether this widget provides a method for handling keyboard events - if (((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent == 0) { + if (((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent == 0) break; - } // If we got this far we can finally pass the event ((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)widgetInFocus, pke); @@ -457,9 +450,8 @@ const GWidgetStyle *gwinGetDefaultStyle(void) { } void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { - if (!(gh->flags & GWIN_FLG_WIDGET)) { + if (!(gh->flags & GWIN_FLG_WIDGET)) return; - } // Dispose of the old string if ((gh->flags & GWIN_FLG_ALLOCTXT)) { @@ -471,9 +463,9 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { } // Alloc the new text if required - if (!text || !*text) { + if (!text || !*text) gw->text = ""; - } else if (useAlloc) { + else if (useAlloc) { char *str; if ((str = gfxAlloc(strlen(text)+1))) { @@ -481,10 +473,8 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc) { strcpy(str, text); } gw->text = (const char *)str; - } else { + } else gw->text = text; - } - _gwinUpdate(gh); } From 15e7342fd7b21b76a565561a3caafee394e70c88 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sun, 16 Aug 2015 21:53:47 +1000 Subject: [PATCH 32/45] Updates to focus. --- demos/modules/gwin/textedit/gfxconf.h | 8 +- demos/modules/gwin/widgets/main.c | 7 +- src/gwin/gwin.h | 25 ---- src/gwin/gwin_class.h | 51 +++++++- src/gwin/gwin_textedit.c | 60 +++------ src/gwin/gwin_textedit.h | 25 +--- src/gwin/gwin_widget.c | 182 ++++++++++++++++---------- src/gwin/gwin_widget.h | 30 +++++ src/gwin/gwin_wm.c | 57 +++++--- 9 files changed, 261 insertions(+), 184 deletions(-) diff --git a/demos/modules/gwin/textedit/gfxconf.h b/demos/modules/gwin/textedit/gfxconf.h index 4e518774..3c5d7b96 100644 --- a/demos/modules/gwin/textedit/gfxconf.h +++ b/demos/modules/gwin/textedit/gfxconf.h @@ -48,13 +48,15 @@ #define GDISP_NEED_CLIP TRUE #define GDISP_NEED_TEXT TRUE #define GDISP_NEED_TEXT_KERNING TRUE -#define GDISP_NEED_STARTUP_LOGO TRUE +#define GDISP_NEED_MULTITHREAD TRUE /* GDISP fonts to include */ #define GDISP_INCLUDE_FONT_UI2 TRUE #define GDISP_INCLUDE_FONT_DEJAVUSANS16 TRUE /* Features for the GWIN subsystem. */ +#define GWIN_NEED_WINDOWMANAGER TRUE +#define GWIN_NEED_WIDGET TRUE #define GWIN_NEED_CONSOLE TRUE #define GWIN_NEED_TEXTEDIT TRUE #define GWIN_NEED_BUTTON TRUE @@ -63,5 +65,9 @@ #define GINPUT_NEED_MOUSE TRUE #define GINPUT_NEED_KEYBOARD TRUE +/* Features for the GQUEUE subsystem. */ +#define GFX_USE_GQUEUE TRUE +#define GQUEUE_NEED_ASYNC TRUE + #endif /* _GFXCONF_H */ diff --git a/demos/modules/gwin/widgets/main.c b/demos/modules/gwin/widgets/main.c index d5bfb6d2..8f5a6329 100644 --- a/demos/modules/gwin/widgets/main.c +++ b/demos/modules/gwin/widgets/main.c @@ -49,13 +49,14 @@ /* Our custom yellow style */ static const GWidgetStyle YellowWidgetStyle = { Yellow, // window background + HTML2COLOR(0x800000), // focus // enabled color set { HTML2COLOR(0x0000FF), // text HTML2COLOR(0x404040), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xE0E0E0), // progress - inactive area + HTML2COLOR(0xE0E0E0) // progress - inactive area }, // disabled color set @@ -63,7 +64,7 @@ static const GWidgetStyle YellowWidgetStyle = { HTML2COLOR(0xC0C0C0), // text HTML2COLOR(0x808080), // edge HTML2COLOR(0xE0E0E0), // fill - HTML2COLOR(0xC0E0C0), // progress - active area + HTML2COLOR(0xC0E0C0) // progress - active area }, // pressed color set @@ -72,7 +73,7 @@ static const GWidgetStyle YellowWidgetStyle = { HTML2COLOR(0x404040), // edge HTML2COLOR(0x808080), // fill HTML2COLOR(0x00E000), // progress - active area - }, + } }; /* The variables we need */ diff --git a/src/gwin/gwin.h b/src/gwin/gwin.h index 4b29f1ee..cc0d44f1 100644 --- a/src/gwin/gwin.h +++ b/src/gwin/gwin.h @@ -552,31 +552,6 @@ extern "C" { */ GHandle gwinGetNextWindow(GHandle gh); - /** - * @brief Set the focus to a specific widget - * - * @details The widget that is currently in focus is the widget that - * receives mouse and keyboard events. - * Passing NULL will remove the focus from any widget. - * - * @param[in] gh The widget handle. Non-widget handles will be ignored. - * - * @api - */ - void gwinSetFocus(GHandle gh); - - /** - * @brief Get the widget that is currently in focus - * - * @details The widget that is currently in focus is the widget that - * receives mouse and keyboard events. - * - * @return The handle of the widget that is currently in focus. May be NULL. - * - * @api - */ - GHandle gwinGetFocus(void); - /** * @brief Set a window or widget to flash * diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h index 03d2d506..ad6df423 100644 --- a/src/gwin/gwin_class.h +++ b/src/gwin/gwin_class.h @@ -256,9 +256,8 @@ void _gwinDrawEnd(GHandle gh); * @param[in] gh The window * @param[in] how Do we wait for the lock? * - * @note This call will delete the window. If called without the - * drawing lock 'how' must be REDRAW_WAIT. If called with the drawing - * lock 'how' must be REDRAW_INSESSION. + * @note If called without the drawing lock 'how' must be REDRAW_WAIT. + * If called with the drawing lock 'how' must be REDRAW_INSESSION. * * @notapi */ @@ -322,6 +321,50 @@ bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit); */ void _gwinSendEvent(GHandle gh, GEventType type); + #if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || defined(__DOXYGEN__) + /** + * @brief Move the focus off the current focus window. + * + * @notapi + */ + void _gwinMoveFocus(void); + + /** + * @brief Do focus fixup's after a change of state for a window. + * @details If a focus window has become invisible or disabled then + * the focus must be taken away from it. If there is no focus + * window and this window is eligible then this window becomes + * the focus. + * + * @param[in] gh The window + * + * @note This routine does not actually do a redraw. It assumes that surrounding code + * will because of the change of state that lead to this being called. + * + * @notapi + */ + void _gwinFixFocus(GHandle gh); + + /** + * @brief Draw a simple focus rectangle in the default style. + * + * @param[in] gw The widget + * @param[in] x, y The start x, y position (relative to the window) + * @param[in] cx, cy The width & height of the rectangle + * + * @note Assumes the widget is in a state where it can draw. + * @note Nothing is drawn if the window doesn't have focus. + * @note The focus rectangle may be more than one pixel thick and may + * not be a continuous line. + * + * @notapi + */ + void _gwidgetDrawFocusRect(GWidgetObject *gw, coord_t x, coord_t y, coord_t cx, coord_t cy); + + #else + #define _gwinFixFocus(gh) + #define _gwidgetDrawFocusRect(gh,x,y,cx,cy) + #endif #if GWIN_NEED_FLASHING || defined(__DOXYGEN__) /** @@ -335,6 +378,8 @@ bool_t _gwinWMAdd(GHandle gh, const GWindowInit *pInit); */ const GColorSet *_gwinGetFlashedColor(GWidgetObject *gw, const GColorSet *pcol, bool_t flashOffState); #endif +#else + #define _gwinFixFocus(gh) #endif #if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 99708a0a..5ea8936a 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -23,9 +23,6 @@ const int FOCUS_BORDER_THICKNESS = 3; const int CURSOR_PADDING_LEFT = 0; const int CURSOR_EXTRA_HEIGHT = 1; -// Some flags -#define GTEXTEDIT_FLG_BORDER (GWIN_FIRST_CONTROL_FLAG << 0) - // Macros to assist in data type conversions #define gh2obj ((GTexteditObject *)gh) #define gw2obj ((GTexteditObject *)gw) @@ -65,7 +62,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char #if GINPUT_NEED_KEYBOARD - static void _keyboardEvent(GWidgetObject* gw, GEventKeyboard* pke) + static void TextEditKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { // Only react on KEYDOWN events. Ignore KEYUP events. if (pke->keystate & GKEYSTATE_KEYUP) { @@ -116,9 +113,8 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char // Add a new character else { // Prevent buffer overflow - if (gw2obj->cursorPos >= gw2obj->bufferSize) { + if (gw2obj->cursorPos >= 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->bufferSize, gw2obj->cursorPos++, pke->c[0]); @@ -152,7 +148,7 @@ static const gwidgetVMT texteditVMT = { #endif #if GINPUT_NEED_KEYBOARD { - _keyboardEvent // Process keyboard key down events + TextEditKeyboard // Process keyboard key down events }, #endif #if GINPUT_NEED_TOGGLE @@ -174,50 +170,33 @@ static const gwidgetVMT texteditVMT = { #endif }; -GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize) +GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize) { uint16_t flags = 0; // Create the underlying widget - if (!(widget = (GTexteditObject*)_gwidgetCreate(g, &widget->w, pInit, &texteditVMT))) { + if (!(wt = (GTexteditObject*)_gwidgetCreate(g, &wt->w, pInit, &texteditVMT))) return 0; - } // Allocate the text buffer - widget->bufferSize = bufSize; - widget->textBuffer = gfxAlloc(widget->bufferSize); - if (widget->textBuffer == 0) { + wt->maxSize = maxSize; + wt->textBuffer = gfxAlloc(widget->maxSize); + if (wt->textBuffer == 0) return 0; - } // Initialize the text buffer size_t i = 0; for (i = 0; i < bufSize; i++) { - widget->textBuffer[i] = '\0'; + wt->textBuffer[i] = '\0'; } // Set text and cursor position - strncpy(widget->textBuffer, gwinGetText((GHandle)widget), widget->bufferSize); // FixMe: pInit->text leads to a segfault - widget->cursorPos = strlen(widget->textBuffer); + strncpy(wt->textBuffer, gwinGetText((GHandle)wt), wt->maxSize); // FixMe: pInit->text leads to a segfault + wt->cursorPos = strlen(wt->textBuffer); - widget->w.g.flags |= flags | GTEXTEDIT_FLG_BORDER;; - gwinSetVisible(&widget->w.g, pInit->g.show); + gwinSetVisible(&wt->w.g, pInit->g.show); - return (GHandle)widget; -} - -void gwinTexteditSetBorder(GHandle gh, bool_t border) -{ - // Is it a valid handle? - if (gh->vmt != (gwinVMT*)&texteditVMT) { - return; - } - - if (border) { - gh2obj->w.g.flags |= GTEXTEDIT_FLG_BORDER; - } else { - gh2obj->w.g.flags &=~ GTEXTEDIT_FLG_BORDER; - } + return (GHandle)wt; } static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) @@ -238,18 +217,11 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) 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 (if supposed to) - if (gw2obj->w.g.flags & GTEXTEDIT_FLG_BORDER) { - 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 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 - if (gwinGetFocus() == (GHandle)gw) { - int i = 0; - for (i = 0; i < FOCUS_BORDER_THICKNESS; i++) { - gdispGDrawBox(gw->g.display, gw->g.x+i, gw->g.y+i, gw->g.width-2*i, gw->g.height-2*i, gw->pstyle->focus); - } - } + _gwidgetDrawFocusRect(gw, 0, 0, gw->g.width-1, gw->g.height-1); // Render cursor (if focused) if (gwinGetFocus() == (GHandle)gw) { diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h index 5b0cea85..f83233b4 100644 --- a/src/gwin/gwin_textedit.h +++ b/src/gwin/gwin_textedit.h @@ -33,7 +33,7 @@ typedef struct GTexteditObject { GWidgetObject w; char* textBuffer; - size_t bufferSize; + size_t maxSize; uint16_t cursorPos; } GTexteditObject; @@ -48,28 +48,17 @@ extern "C" { * is abstracted through the GINPUT module. * * @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] wt 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. + * @param[in] maxSize The maximum number of characters the TextEdit widget can hold. * - * @return NULL if there is no resultat drawing area, otherwise the widget handle. + * @return NULL if there is no resultant drawing area, otherwise the widget handle. * + * @note If the initial text set is larger than maxSize then the text is truncated at maxSize characters. * @api */ -GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* widget, GWidgetInit* pInit, size_t bufSize); -#define gwinTexteditCreate(w, pInit, bufSize) gwinGTexteditCreate(GDISP, w, pInit, bufSize) - -/** - * @brief Border settings for the default rendering routine - * - * @note Border is enabled by default. - * - * @param[in] gh The widget handle (must be a TextEdit handle) - * @param[in] border Shall a border be rendered? - * - * @api - */ -void gwinTexteditSetBorder(GHandle gh, bool_t border); +GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize); +#define gwinTexteditCreate(wt, pInit, maxSize) gwinGTexteditCreate(GDISP, wt, pInit, maxSize) #ifdef __cplusplus } diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 47e60e82..6ef148a6 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -21,6 +21,11 @@ // Our listener for events for widgets static GListener gl; +#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD + // Our current focus window + static GHandle _widgetInFocus; +#endif + // Our default style - a white background theme const GWidgetStyle WhiteWidgetStyle = { HTML2COLOR(0xFFFFFF), // window background @@ -139,6 +144,14 @@ static void gwidgetEvent(void *param, GEvent *pe) { if (gh && (gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) == (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) { if ((pme->buttons & GMETA_MOUSE_DOWN)) { gh->flags |= GWIN_FLG_MOUSECAPTURE; + + #if GFX_USE_GINPUT && GINPUT_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); + #endif + if (wvmt->MouseDown) wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); } @@ -150,79 +163,14 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_KEYBOARD: // If Tab key pressed then set focus to next widget if (pke->bytecount == 1 && pke->c[0] == GKEY_TAB) { - - // Only react on KEYDOWN events. Ignore KEYUP events. - if (pke->keystate & GKEYSTATE_KEYUP) - break; - - // Get the next widget - bool_t foundWidget = FALSE; - bool_t endOfListDetected = FALSE; - GHandle nextWidget = gwinGetFocus(); - GHandle prevWidget = gwinGetFocus(); - do { - nextWidget = gwinGetNextWindow(nextWidget); - foundWidget = TRUE; - - // Begin with the first one if this is the last one - if (nextWidget == 0) { - foundWidget = FALSE; - // We go through the list twice - just to be sure - if (endOfListDetected) - break; - endOfListDetected = TRUE; - continue; - } - - // Check whether this is a window or a widget - if (!gwinIsWidget(nextWidget)) { - foundWidget = FALSE; - continue; - } - - // Only focus on a widget that is visible and enabled - if (!(nextWidget->flags & GWIN_FLG_SYSVISIBLE) || !(nextWidget->flags & GWIN_FLG_ENABLED)) { - foundWidget = FALSE; - continue; - } - - // When using the TAB key we only focus on widgets that process keyboard events - if (((gwidgetVMT*)nextWidget->vmt)->KeyboardEvent == 0) { - foundWidget = FALSE; - continue; - } - - } while (foundWidget == FALSE); - gwinSetFocus(nextWidget); - - // Redraw the new and the previous focused widget because they usually render differently when - // they are not focused anymore (eg. no blinking cursor) - if (prevWidget != 0) - gwinRedraw(prevWidget); - if (nextWidget != 0) - gwinRedraw(nextWidget); - + if (!(pke->keystate & GKEYSTATE_KEYUP)) + _gwinMoveFocus(); break; } - // Otherise, send keyboard events only to widget in focus - GHandle widgetInFocus = gwinGetFocus(); - if (widgetInFocus != 0) { - // Make sure that it is a widget - if (!gwinIsWidget(widgetInFocus)) - break; - - // Make sure that the widget is enabled and visible - if (!(widgetInFocus->flags & GWIN_FLG_SYSVISIBLE) || !(widgetInFocus->flags & GWIN_FLG_ENABLED)) - break; - - // Check whether this widget provides a method for handling keyboard events - if (((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent == 0) - break; - - // If we got this far we can finally pass the event - ((gwidgetVMT*)widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)widgetInFocus, pke); - } + // Otherwise, send keyboard events only to widget in focus + if (_widgetInFocus) + ((gwidgetVMT*)_widgetInFocus->vmt)->KeyboardEvent((GWidgetObject*)_widgetInFocus, pke); break; #endif @@ -278,6 +226,96 @@ static void gwidgetEvent(void *param, GEvent *pe) { #undef pde } +#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD + GHandle gwinGetFocus(void) { + return _widgetInFocus; + } + + bool_t gwinSetFocus(GHandle gh) { + GHandle oldFocus; + + // Do we already have the focus? + if (gh == _widgetInFocus) + return TRUE; + + // The new window must be NULLL or a visible enabled widget with a keyboard handler + if (!gh || ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) + && ((gwidgetVMT*)gh->vmt)->KeyboardEvent)) { + // Move the current focus + oldFocus = _widgetInFocus; + _widgetInFocus = gh; + if (oldFocus) _gwinUpdate(oldFocus); + if (gh) _gwinUpdate(gh); + return TRUE; + } + return FALSE; + } + + void _gwinMoveFocus(void) { + GHandle gh; + + // Find a new focus window (one may or may not exist). + for(gh = gwinGetNextWindow(_widgetInFocus); gh && gh != _widgetInFocus; gh = gwinGetNextWindow(gh)) { + if (gwinSetFocus(gh)) + return; + } + gwinSetFocus(0); + } + + void _gwinFixFocus(GHandle gh) { + GHandle oldFocus; + + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) + && ((gwidgetVMT*)gh->vmt)->KeyboardEvent) { + + // We are a candidate to be able to claim the focus + + // Claim the focus if no-one else has + if (!_widgetInFocus) + _widgetInFocus = gh; + + return; + } + + // We have lost any right to the focus + + // Did we have the focus + if (gh != _widgetInFocus) + return; + + // We did - we need to find a new focus window + oldFocus = _widgetInFocus; + for(gh = gwinGetNextWindow(oldFocus); gh && gh != oldFocus; gh = gwinGetNextWindow(gh)) { + + // Must be a visible enabled widget with a keyboard handler + if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE)) == (GWIN_FLG_WIDGET|GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED|GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE) + && ((gwidgetVMT*)gh->vmt)->KeyboardEvent) { + + // Grab the focus for the new window + _widgetInFocus = gh; + + // This new window still needs to be marked for redraw (but don't actually do it yet). + gh->flags |= GWIN_FLG_NEEDREDRAW; + RedrawPending |= DOREDRAW_VISIBLES; + return; + } + } + + // No-one has the right to the focus + _widgetInFocus = 0; + } + + void _gwidgetDrawFocusRect(GWidgetObject *gw, coord_t x, coord_t y, coord_t cx, coord_t cy) { + // Don't do anything if we don't have the focus + if (&gw->g != _widgetInFocus) + return; + + // Use the very simplest possible focus rectangle for now. + gdispGDrawBox(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, gw->pstyle.focus); + } + +#endif + #if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE static GHandle FindToggleUser(uint16_t instance) { GHandle gh; @@ -355,6 +393,10 @@ void _gwidgetDestroy(GHandle gh) { uint16_t role, instance; #endif + // Make the window is invisible so it is not eligible for focus + gh->flags &= ~GWIN_FLG_VISIBLE; + _gwinFixFocus(gh); + // Deallocate the text (if necessary) if ((gh->flags & GWIN_FLG_ALLOCTXT)) { gh->flags &= ~GWIN_FLG_ALLOCTXT; diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index 3aa1b9f1..5239b6b8 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -356,6 +356,36 @@ 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__) + /** + * @brief Set the keyboard focus to a specific window + * @return Returns TRUE if the focus could be set to that window + * + * @param[in] gh The window + * + * @note Passing NULL will remove the focus from any window. + * @note Only visible enabled widgets are capable of getting the focus. + * + * @api + */ + bool_t gwinSetFocus(GHandle gh); + + /** + * @brief Get the widget that is currently in focus + * + * @details The widget that is currently in focus is the widget that + * receives mouse and keyboard events. + * + * @return The handle of the widget that is currently in focus. May be NULL. + * + * @api + */ + GHandle gwinGetFocus(void); +#else + #define gwinGetFocus() (0) + #define gwinSetFocus(gh) (FALSE) +#endif + #ifdef __cplusplus } #endif diff --git a/src/gwin/gwin_wm.c b/src/gwin/gwin_wm.c index 52f7a1aa..41fc385d 100644 --- a/src/gwin/gwin_wm.c +++ b/src/gwin/gwin_wm.c @@ -162,7 +162,6 @@ extern const GWindowManager GNullWindowManager; GWindowManager * _GWINwm; bool_t _gwinFlashState; -static GHandle _widgetInFocus; static gfxSem gwinsem; static gfxQueueASync _GWINList; #if GWIN_NEED_FLASHING @@ -333,14 +332,23 @@ void _gwinUpdate(GHandle gh) { if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) { // We have been made visible gh->flags |= (GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // Do we want to grab the focus + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_VISIBLES; } break; case (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE): if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) break; + // Parent has been made invisible gh->flags &= ~GWIN_FLG_SYSVISIBLE; + + // No focus for us anymore + _gwinFixFocus(gh); + break; case GWIN_FLG_SYSVISIBLE: // We have been made invisible @@ -348,6 +356,10 @@ void _gwinUpdate(GHandle gh) { if (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSVISIBLE)) { // The parent is visible so we must clear the area we took gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // No focus for us anymore + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_INVISIBLES; } break; @@ -457,6 +469,10 @@ void gwinRedraw(GHandle gh) { if (visible) { if (!(gh->flags & GWIN_FLG_VISIBLE)) { gh->flags |= (GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE|GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // Do we want to grab the focus + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_VISIBLES; TriggerRedraw(); } @@ -464,6 +480,10 @@ void gwinRedraw(GHandle gh) { if ((gh->flags & GWIN_FLG_VISIBLE)) { gh->flags &= ~(GWIN_FLG_VISIBLE|GWIN_FLG_SYSVISIBLE); gh->flags |= (GWIN_FLG_NEEDREDRAW|GWIN_FLG_BGREDRAW); + + // No focus for us anymore + _gwinFixFocus(gh); + RedrawPending |= DOREDRAW_INVISIBLES; TriggerRedraw(); } @@ -484,6 +504,10 @@ void gwinRedraw(GHandle gh) { for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if ((gh->flags & (GWIN_FLG_SYSENABLED|GWIN_FLG_ENABLED)) == GWIN_FLG_ENABLED && (!gh->parent || (gh->parent->flags & GWIN_FLG_SYSENABLED))) { gh->flags |= GWIN_FLG_SYSENABLED; // Fix it + + // Do we want to grab the focus + _gwinFixFocus(gh); + _gwinUpdate(gh); } } @@ -497,6 +521,10 @@ void gwinRedraw(GHandle gh) { for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { if ((gh->flags & GWIN_FLG_SYSENABLED) && (!(gh->flags & GWIN_FLG_ENABLED) || (gh->parent && !(gh->parent->flags & GWIN_FLG_SYSENABLED)))) { gh->flags &= ~GWIN_FLG_SYSENABLED; // Fix it + + // No focus for us anymore + _gwinFixFocus(gh); + _gwinUpdate(gh); } } @@ -508,11 +536,19 @@ void gwinRedraw(GHandle gh) { if (enabled) { if (!(gh->flags & GWIN_FLG_ENABLED)) { gh->flags |= (GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + + // Do we want to grab the focus + _gwinFixFocus(gh); + _gwinUpdate(gh); } } else { if ((gh->flags & GWIN_FLG_ENABLED)) { gh->flags &= ~(GWIN_FLG_ENABLED|GWIN_FLG_SYSENABLED); + + // No focus for us anymore + _gwinFixFocus(gh); + _gwinUpdate(gh); } } @@ -573,25 +609,6 @@ GHandle gwinGetNextWindow(GHandle gh) { return gh ? (GHandle)gfxQueueASyncNext(&gh->wmq) : (GHandle)gfxQueueASyncPeek(&_GWINList); } -void gwinSetFocus(GHandle gh) { - // Passing NULL removes the focus from any widget - if (gh == 0) { - _widgetInFocus = 0; - return; - } - - // Only accept widgets - if (!gwinIsWidget(gh)) { - return; - } - - _widgetInFocus = gh; -} - -GHandle gwinGetFocus(void) { - return _widgetInFocus; -} - #if GWIN_NEED_FLASHING static void FlashTimerFn(void *param) { GHandle gh; From af76c04767f3e5d1cc6f39ba907a7d4afdb43200 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sun, 16 Aug 2015 22:05:32 +1000 Subject: [PATCH 33/45] Compile fixes --- demos/modules/gwin/textedit/main.c | 6 +++--- src/gwin/gwin_textedit.c | 20 +++++++++----------- src/gwin/gwin_widget.c | 9 +++++---- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/demos/modules/gwin/textedit/main.c b/demos/modules/gwin/textedit/main.c index 950475b5..ed52ccb2 100644 --- a/demos/modules/gwin/textedit/main.c +++ b/demos/modules/gwin/textedit/main.c @@ -35,7 +35,7 @@ static GHandle ghTextedit2; static GHandle ghTextedit3; static GListener gl; -static void guiCreate() +static void guiCreate(void) { GWidgetInit wi; gwinWidgetClearInit(&wi); @@ -69,7 +69,7 @@ static void guiCreate() wi.g.height = 35; wi.text = "to switch between"; ghTextedit2 = gwinTexteditCreate(0, &wi, 20); - gwinTexteditSetBorder(ghTextedit2, FALSE); + //gwinTexteditSetBorder(ghTextedit2, FALSE); // TextEdit3 wi.g.show = TRUE; @@ -79,7 +79,7 @@ static void guiCreate() wi.g.height = 35; wi.text = "the different widgets"; ghTextedit3 = gwinTexteditCreate(0, &wi, 100); - gwinTexteditSetBorder(ghTextedit3, TRUE); + //gwinTexteditSetBorder(ghTextedit3, TRUE); } int main(void) { diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 5ea8936a..70a5f8f0 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -30,11 +30,11 @@ const int CURSOR_EXTRA_HEIGHT = 1; // cursorPos is the position of the next character // textBuffer[cursorPos++] = readKey(); -static void _shiftTextLeft(char* buffer, size_t bufferSize, size_t index) +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 < bufferSize-1) { + while (buffer[indexTerminator] != '\0' && indexTerminator < maxSize-1) { indexTerminator++; } @@ -45,11 +45,11 @@ static void _shiftTextLeft(char* buffer, size_t bufferSize, size_t index) buffer[indexTerminator-1] = '\0'; } -static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char fillChar) +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 < bufferSize-1) { + while (buffer[indexTerminator] != '\0' && indexTerminator < maxSize-1) { indexTerminator++; } @@ -98,7 +98,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char if (gw2obj->cursorPos == 0) { return; } - _shiftTextLeft(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos--); + _shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos--); } // Is it delete? @@ -107,7 +107,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char if (gw2obj->textBuffer[gw2obj->cursorPos] == '\0') { return; } - _shiftTextLeft(gw2obj->textBuffer, gw2obj->bufferSize, gw2obj->cursorPos+1); + _shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos+1); } // Add a new character @@ -117,7 +117,7 @@ static void _shiftTextRight(char* buffer, size_t bufferSize, size_t index, char return; // 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]); + _shiftTextRight(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos++, pke->c[0]); } // Set the new text @@ -172,21 +172,19 @@ static const gwidgetVMT texteditVMT = { GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit, size_t maxSize) { - uint16_t flags = 0; - // 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(widget->maxSize); + wt->textBuffer = gfxAlloc(wt->maxSize); if (wt->textBuffer == 0) return 0; // Initialize the text buffer size_t i = 0; - for (i = 0; i < bufSize; i++) { + for (i = 0; i < wt->maxSize; i++) { wt->textBuffer[i] = '\0'; } diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index 6ef148a6..fceb65c8 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -223,6 +223,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { #undef pme #undef pte + #undef pke #undef pde } @@ -296,7 +297,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { // This new window still needs to be marked for redraw (but don't actually do it yet). gh->flags |= GWIN_FLG_NEEDREDRAW; - RedrawPending |= DOREDRAW_VISIBLES; + // RedrawPending |= DOREDRAW_VISIBLES; - FIX LATER return; } } @@ -305,13 +306,13 @@ static void gwidgetEvent(void *param, GEvent *pe) { _widgetInFocus = 0; } - void _gwidgetDrawFocusRect(GWidgetObject *gw, coord_t x, coord_t y, coord_t cx, coord_t cy) { + void _gwidgetDrawFocusRect(GWidgetObject *gx, coord_t x, coord_t y, coord_t cx, coord_t cy) { // Don't do anything if we don't have the focus - if (&gw->g != _widgetInFocus) + if (&gx->g != _widgetInFocus) return; // Use the very simplest possible focus rectangle for now. - gdispGDrawBox(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, gw->pstyle.focus); + gdispGDrawBox(gx->g.display, gx->g.x+x, gx->g.y+y, cx, cy, gx->pstyle->focus); } #endif From 8c3c536111325eecdab186b54365930e50057530 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sun, 16 Aug 2015 22:11:19 +1000 Subject: [PATCH 34/45] Another compile fix --- src/gwin/gwin_wm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gwin/gwin_wm.c b/src/gwin/gwin_wm.c index 41fc385d..ef7333fa 100644 --- a/src/gwin/gwin_wm.c +++ b/src/gwin/gwin_wm.c @@ -183,8 +183,6 @@ static volatile uint8_t RedrawPending; void _gwmInit(void) { - _widgetInFocus = 0; - gfxSemInit(&gwinsem, 1, 1); gfxQueueASyncInit(&_GWINList); #if GWIN_NEED_FLASHING From a569bbfc1eaf2ac67e151c27ee674107a589f93d Mon Sep 17 00:00:00 2001 From: inmarket Date: Sun, 16 Aug 2015 22:20:53 +1000 Subject: [PATCH 35/45] Border fix --- src/gwin/gwin_textedit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 70a5f8f0..1395ab71 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -219,7 +219,7 @@ static void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param) 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, 0, 0, gw->g.width-1, gw->g.height-1); + _gwidgetDrawFocusRect(gw, 1, 1, gw->g.width-2, gw->g.height-2); // Render cursor (if focused) if (gwinGetFocus() == (GHandle)gw) { From 058a873e9e425207db8b99be083e87cf956b9827 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 16 Aug 2015 14:37:12 +0200 Subject: [PATCH 36/45] Adding GWIN_FOCUS_HIGHLIGHT_WIDTH --- docs/releases.txt | 1 + gfxconf.example.h | 1 + src/gwin/gwin_options.h | 7 +++++++ src/gwin/gwin_widget.c | 7 +++++-- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/releases.txt b/docs/releases.txt index 804f2c1b..c82119a7 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -14,6 +14,7 @@ FEATURE: Adding more font metrics (BaselineX and BaselineY) FEATURE: Adding gdispGetStringWidthCount() FEATURE: Adding TextEdit widget FEATURE: Added color to widget style for focused widgets +FEATURE: Added GWIN_FOCUS_HIGHLIGHT_WIDTH as an option in the configuration file *** Release 2.3 *** diff --git a/gfxconf.example.h b/gfxconf.example.h index 1a54b8eb..0140c4fc 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -167,6 +167,7 @@ //#define GWIN_NEED_GL3D FALSE //#define GWIN_NEED_WIDGET FALSE +//#define GWIN_FOCUS_HIGHLIGHT_WIDTH 1 // #define GWIN_NEED_LABEL FALSE // #define GWIN_LABEL_ATTRIBUTE FALSE // #define GWIN_NEED_BUTTON FALSE diff --git a/src/gwin/gwin_options.h b/src/gwin/gwin_options.h index 46bd92e7..45dfcd36 100644 --- a/src/gwin/gwin_options.h +++ b/src/gwin/gwin_options.h @@ -44,6 +44,13 @@ #ifndef GWIN_NEED_WIDGET #define GWIN_NEED_WIDGET FALSE #endif + /** + * @brief The width of the rectangle that highlights a widget that is focused + * @details Defaults to 1 + */ + #ifndef GWIN_FOCUS_HIGHLIGHT_WIDTH + #define GWIN_FOCUS_HIGHLIGHT_WIDTH 1 + #endif /** * @brief Should the simple container be included. * @details Defaults to FALSE diff --git a/src/gwin/gwin_widget.c b/src/gwin/gwin_widget.c index fceb65c8..162d41e3 100644 --- a/src/gwin/gwin_widget.c +++ b/src/gwin/gwin_widget.c @@ -311,8 +311,11 @@ static void gwidgetEvent(void *param, GEvent *pe) { if (&gx->g != _widgetInFocus) return; - // Use the very simplest possible focus rectangle for now. - gdispGDrawBox(gx->g.display, gx->g.x+x, gx->g.y+y, cx, cy, gx->pstyle->focus); + // Use the very simplest possible focus rectangle for now + uint16_t i = 0; + for (i = 0; i < GWIN_FOCUS_HIGHLIGHT_WIDTH; i++) { + gdispGDrawBox(gx->g.display, gx->g.x+x+i, gx->g.y+y+i, cx-2*i, cy-2*i, gx->pstyle->focus); + } } #endif From 3fea02324831eba1773f45b1dc6d0bf2a0c8326d Mon Sep 17 00:00:00 2001 From: inmarket Date: Mon, 17 Aug 2015 00:18:54 +1000 Subject: [PATCH 37/45] Add some keyboard widget support --- demos/modules/gwin/textedit/gfxconf.h | 3 +++ demos/modules/gwin/textedit/main.c | 14 ++++++++++ src/ginput/ginput_keyboard.h | 39 +++++++++++++-------------- src/gwin/gwin_button.c | 4 +-- src/gwin/gwin_checkbox.c | 4 +-- src/gwin/gwin_class.h | 2 +- src/gwin/gwin_container.c | 2 +- src/gwin/gwin_frame.c | 2 +- src/gwin/gwin_keyboard.c | 21 ++++++++++++++- src/gwin/gwin_label.c | 2 +- src/gwin/gwin_list.c | 2 +- src/gwin/gwin_progressbar.c | 2 +- src/gwin/gwin_radio.c | 2 +- src/gwin/gwin_rules.h | 4 +-- src/gwin/gwin_slider.c | 2 +- src/gwin/gwin_tabset.c | 2 +- src/gwin/gwin_textedit.c | 4 +-- 17 files changed, 73 insertions(+), 38 deletions(-) diff --git a/demos/modules/gwin/textedit/gfxconf.h b/demos/modules/gwin/textedit/gfxconf.h index 3c5d7b96..4faaae82 100644 --- a/demos/modules/gwin/textedit/gfxconf.h +++ b/demos/modules/gwin/textedit/gfxconf.h @@ -63,7 +63,10 @@ /* Features for the GINPUT subsystem. */ #define GINPUT_NEED_MOUSE TRUE + +/* One or both of these */ #define GINPUT_NEED_KEYBOARD TRUE +#define GWIN_NEED_KEYBOARD TRUE /* Features for the GQUEUE subsystem. */ #define GFX_USE_GQUEUE TRUE diff --git a/demos/modules/gwin/textedit/main.c b/demos/modules/gwin/textedit/main.c index ed52ccb2..4072f983 100644 --- a/demos/modules/gwin/textedit/main.c +++ b/demos/modules/gwin/textedit/main.c @@ -34,6 +34,9 @@ static GHandle ghTextedit1; static GHandle ghTextedit2; static GHandle ghTextedit3; static GListener gl; +#if GWIN_NEED_KEYBOARD + static GHandle ghKeyboard; +#endif static void guiCreate(void) { @@ -80,6 +83,17 @@ static void guiCreate(void) wi.text = "the different widgets"; ghTextedit3 = gwinTexteditCreate(0, &wi, 100); //gwinTexteditSetBorder(ghTextedit3, TRUE); + + // Virtual keyboard +#if GWIN_NEED_KEYBOARD + wi.g.show = TRUE; + wi.g.x = 0; + wi.g.y = gdispGetHeight()*3/4; + wi.g.width = gdispGetWidth(); + wi.g.height = gdispGetHeight()/4; + ghKeyboard = gwinKeyboardCreate(0, &wi); +#endif + } int main(void) { diff --git a/src/ginput/ginput_keyboard.h b/src/ginput/ginput_keyboard.h index 1349092f..40691a0d 100644 --- a/src/ginput/ginput_keyboard.h +++ b/src/ginput/ginput_keyboard.h @@ -164,13 +164,11 @@ 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) +#endif + /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ @@ -188,33 +186,34 @@ extern "C" { */ GSourceHandle ginputGetKeyboard(unsigned instance); - /** - * @brief Get the current keyboard status - * - * @param[in] instance The ID of the keyboard input instance - * @param[in] pkeyboard The keyboard event struct - * - * @return Returns FALSE on an error (eg invalid instance) - */ - bool_t ginputGetKeyboardStatus(unsigned instance, GEventKeyboard *pkeyboard); + #if GINPUT_NEED_KEYBOARD || defined(__DOXYGEN__) - #if !GKEYBOARD_LAYOUT_OFF || defined(__DOXYGEN__) /** - * @brief Set the keyboard layout + * @brief Get the current keyboard status * * @param[in] instance The ID of the keyboard input instance - * @param[in] pLayout The keyboard layout micro-code. Passing NULL defaults to the driver's default layout. + * @param[in] pkeyboard The keyboard event struct * * @return Returns FALSE on an error (eg invalid instance) */ - bool_t ginputSetKeyboardLayout(unsigned instance, const void *pLayout); - #endif + bool_t ginputGetKeyboardStatus(unsigned instance, GEventKeyboard *pkeyboard); + + #if !GKEYBOARD_LAYOUT_OFF || defined(__DOXYGEN__) + /** + * @brief Set the keyboard layout + * + * @param[in] instance The ID of the keyboard input instance + * @param[in] pLayout The keyboard layout micro-code. Passing NULL defaults to the driver's default layout. + * + * @return Returns FALSE on an error (eg invalid instance) + */ + bool_t ginputSetKeyboardLayout(unsigned instance, const void *pLayout); + #endif + #endif /* GINPUT_NEED_KEYBOARD */ #ifdef __cplusplus } #endif -#endif /* GINPUT_NEED_KEYBOARD */ - #endif /* _GINPUT_KEYBOARD_H */ /** @} */ diff --git a/src/gwin/gwin_button.c b/src/gwin/gwin_button.c index 7705c4ce..4d83348e 100644 --- a/src/gwin/gwin_button.c +++ b/src/gwin/gwin_button.c @@ -50,7 +50,7 @@ } #endif -#if GINPUT_NEED_KEYBOARD +#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD static void ButtonKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { // ENTER and SPACE keys to press the button @@ -113,7 +113,7 @@ static const gwidgetVMT buttonVMT = { 0, // Process mouse move events (NOT USED) }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { ButtonKeyboard // Process keyboard events }, diff --git a/src/gwin/gwin_checkbox.c b/src/gwin/gwin_checkbox.c index d6906b71..d477420b 100644 --- a/src/gwin/gwin_checkbox.c +++ b/src/gwin/gwin_checkbox.c @@ -55,7 +55,7 @@ static void SendCheckboxEvent(GWidgetObject *gw) { } #endif -#if GINPUT_NEED_KEYBOARD +#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD static void CheckboxKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { // Only react on KEYDOWN events. Ignore KEYUP events. @@ -108,7 +108,7 @@ static const gwidgetVMT checkboxVMT = { 0, // Process mouse move events (NOT USED) }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { CheckboxKeyboard // Process keyboard events }, diff --git a/src/gwin/gwin_class.h b/src/gwin/gwin_class.h index ad6df423..8aa5b9ff 100644 --- a/src/gwin/gwin_class.h +++ b/src/gwin/gwin_class.h @@ -96,7 +96,7 @@ typedef struct gwinVMT { void (*MouseMove) (GWidgetObject *gw, coord_t x, coord_t y); /**< Process mouse move events (optional) */ }; #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD struct { void (*KeyboardEvent) (GWidgetObject *gw, GEventKeyboard *pke); /**< Process keyboard events (optional) */ }; diff --git a/src/gwin/gwin_container.c b/src/gwin/gwin_container.c index 172b6ae8..89d09e9a 100644 --- a/src/gwin/gwin_container.c +++ b/src/gwin/gwin_container.c @@ -110,7 +110,7 @@ static const gcontainerVMT containerVMT = { 0, 0, 0, // No mouse }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, diff --git a/src/gwin/gwin_frame.c b/src/gwin/gwin_frame.c index f8787dcb..7400b0f5 100644 --- a/src/gwin/gwin_frame.c +++ b/src/gwin/gwin_frame.c @@ -178,7 +178,7 @@ static const gcontainerVMT frameVMT = { 0, // Process mouse move events }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, diff --git a/src/gwin/gwin_keyboard.c b/src/gwin/gwin_keyboard.c index c27580de..70771d9b 100644 --- a/src/gwin/gwin_keyboard.c +++ b/src/gwin/gwin_keyboard.c @@ -30,6 +30,8 @@ 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; +static GSourceHandle AllKeyboards; + // Get the length of a UTF-8 string static int UTF8StrLen(const utf8 *s) { int len; @@ -164,6 +166,11 @@ static void SendKeyboardEventToListener(GSourceListener *psl, GKeyboardObject *g static void SendKeyboardEvent(GKeyboardObject *gk) { GSourceListener *psl; + // Send to the "All Keyboards" source listeners + psl = 0; + while ((psl = geventGetSourceListener(AllKeyboards, psl))) + SendKeyboardEventToListener(psl, gk); + // Send to the keyboard specific source listeners psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)gk, psl))) @@ -313,7 +320,7 @@ static const gwidgetVMT keyboardVMT = { KeyMouseMove, // Process mouse move events }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, @@ -344,6 +351,10 @@ GHandle gwinGKeyboardCreate(GDisplay *g, GKeyboardObject *gk, const GWidgetInit gk->keytable = &GWIN_KEYBOARD_DEFAULT_LAYOUT; gk->keyset = gk->keytable->ksets[0]; gk->lastkeyrow = gk->lastkeycol = gk->keyrow = gk->keycol = GKEY_BAD_ROWCOL; + + if (!AllKeyboards) + AllKeyboards = ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES); + gwinSetVisible((GHandle)gk, pInit->g.show); return (GHandle)gk; } @@ -473,4 +484,12 @@ void gwinKeyboardDraw_Normal(GWidgetObject *gw, void *param) { #undef gk } +#if !(GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) + GSourceHandle ginputGetKeyboard(unsigned instance) { + if (instance == GKEYBOARD_ALL_INSTANCES) + return (GSourceHandle)&AllKeyboards; + return 0; + } +#endif + #endif /* GFX_USE_GWIN && GWIN_NEED_KEYBOARD */ diff --git a/src/gwin/gwin_label.c b/src/gwin/gwin_label.c index 38d9b601..b78e4b3b 100644 --- a/src/gwin/gwin_label.c +++ b/src/gwin/gwin_label.c @@ -58,7 +58,7 @@ static const gwidgetVMT labelVMT = { 0, // Process mouse move events (NOT USED) }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard key down events }, diff --git a/src/gwin/gwin_list.c b/src/gwin/gwin_list.c index 3ea6286c..88d3028a 100644 --- a/src/gwin/gwin_list.c +++ b/src/gwin/gwin_list.c @@ -279,7 +279,7 @@ static const gwidgetVMT listVMT = { ListMouseMove, }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, diff --git a/src/gwin/gwin_progressbar.c b/src/gwin/gwin_progressbar.c index 3843949a..0de69867 100644 --- a/src/gwin/gwin_progressbar.c +++ b/src/gwin/gwin_progressbar.c @@ -51,7 +51,7 @@ static const gwidgetVMT progressbarVMT = { 0, // Process mouse move events }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, diff --git a/src/gwin/gwin_radio.c b/src/gwin/gwin_radio.c index fcdb242b..470a1f2a 100644 --- a/src/gwin/gwin_radio.c +++ b/src/gwin/gwin_radio.c @@ -92,7 +92,7 @@ static const gwidgetVMT radioVMT = { 0, // Process mouse move events (NOT USED) }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, diff --git a/src/gwin/gwin_rules.h b/src/gwin/gwin_rules.h index 3426eb67..ea35a426 100644 --- a/src/gwin/gwin_rules.h +++ b/src/gwin/gwin_rules.h @@ -127,8 +127,8 @@ #if !GDISP_NEED_TEXT #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_TEXTEDIT is TRUE." #endif - #if !GINPUT_NEED_KEYBOARD - #error "GWIN: GINPUT_NEED_KEYBOARD is required if GWIN_NEED_TEXTEDIT is TRUE." + #if !(GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD) + #error "GWIN: GINPUT_NEED_KEYBOARD or GWIN_NEED_KEYBOARD is required if GWIN_NEED_TEXTEDIT is TRUE." #endif #endif #endif diff --git a/src/gwin/gwin_slider.c b/src/gwin/gwin_slider.c index 218f4f6b..c8f47f95 100644 --- a/src/gwin/gwin_slider.c +++ b/src/gwin/gwin_slider.c @@ -250,7 +250,7 @@ static const gwidgetVMT sliderVMT = { SliderMouseMove, // Process mouse move events }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, diff --git a/src/gwin/gwin_tabset.c b/src/gwin/gwin_tabset.c index f8731f8f..d059256d 100644 --- a/src/gwin/gwin_tabset.c +++ b/src/gwin/gwin_tabset.c @@ -77,7 +77,7 @@ static const gcontainerVMT tabpageVMT = { 0, // Process mouse move events }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { 0 // Process keyboard events }, diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c index 1395ab71..c1373897 100644 --- a/src/gwin/gwin_textedit.c +++ b/src/gwin/gwin_textedit.c @@ -61,7 +61,7 @@ static void _shiftTextRight(char* buffer, size_t maxSize, size_t index, char fil } -#if GINPUT_NEED_KEYBOARD +#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD static void TextEditKeyboard(GWidgetObject* gw, GEventKeyboard* pke) { // Only react on KEYDOWN events. Ignore KEYUP events. @@ -146,7 +146,7 @@ static const gwidgetVMT texteditVMT = { 0, // Process mouse move events (NOT USED) }, #endif - #if GINPUT_NEED_KEYBOARD + #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD { TextEditKeyboard // Process keyboard key down events }, From baa65f3376787295287cdde88808af4070f0aa63 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 16 Aug 2015 20:30:04 +0200 Subject: [PATCH 38/45] Adding new studio JSON file containing focus field for widget styles --- tools/studio/options_v2.json | 483 +++++++++++++++++++++++++++++++++++ 1 file changed, 483 insertions(+) create mode 100644 tools/studio/options_v2.json diff --git a/tools/studio/options_v2.json b/tools/studio/options_v2.json new file mode 100644 index 00000000..d2d7e1f0 --- /dev/null +++ b/tools/studio/options_v2.json @@ -0,0 +1,483 @@ +[ + { + "GOS" : [ + { + "Priorities" : [ + { + "User": "Low", + "Library": "LOW_PRIORITY" + }, + { + "User": "Normal", + "Library": "NORMAL_PRIORITY" + }, + { + "User": "High", + "Library": "HIGH_PRIORITY" + } + ] + } + ] + }, + { + "GDISP" : [ + { + "Orientations" : [ + { + "User": "Native", + "Library": "GDISP_ROTATE_0" + }, + { + "User": "Portrait", + "Library": "GDISP_ROTATE_PORTRAIT" + }, + { + "User": "Landscape", + "Library": "GDISP_ROTATE_LANDSCAPE" + }, + { + "User": "Rotate 90 degree", + "Library": "GDISP_ROTATE_90" + }, + { + "User": "Rotate 180 degree", + "Library": "GDISP_ROTATE_180" + }, + { + "User": "Rotate 270 degree", + "Library": "GDISP_ROTATE_270" + } + ] + }, + { + "Colors" : [ + { + "User": "White", + "Library" : "White", + "RGB": "0xFFFFFF" + }, + { + "User": "Black", + "Library": "Black", + "RGB": "0x000000" + }, + { + "User": "Gray", + "Library": "Gray", + "RGB": "0x808080" + }, + { + "User": "Blue", + "Library": "Blue", + "RGB": "0x0000FF" + }, + { + "User": "Red", + "Library": "Red", + "RGB": "0xFF0000" + }, + { + "User": "Magenta", + "Library": "Magenta", + "RGB": "0xFF00FF" + }, + { + "User": "Green", + "Library": "Green", + "RGB": "0x008000" + }, + { + "User": "Yellow", + "Library": "Yellow", + "RGB": "0xFFFF00" + }, + { + "User": "Cyan", + "Library": "Cyan", + "RGB": "0x00FFFF" + }, + { + "User": "Lime", + "Library": "Lime", + "RGB": "0x00FF00" + }, + { + "User": "Maroon", + "Library": "Maroon", + "RGB": "0x800000" + }, + { + "User": "Navy", + "Library": "Navy", + "RGB": "0x000080" + }, + { + "User": "Olive", + "Library": "Olive", + "RGB": "0x808000" + }, + { + "User": "Purple", + "Library": "Purple", + "RGB": "0x800080" + }, + { + "User": "Silver", + "Library": "Silver", + "RGB": "0xC0C0C0" + }, + { + "User": "Teal", + "Library": "Teal", + "RGB": "0x008080" + }, + { + "User": "Orange", + "Library": "Orange", + "RGB": "0xFFA500" + }, + { + "User": "Pink", + "Library": "Pink", + "RGB": "0xFFC0CB" + }, + { + "User": "SkyBlue", + "Library": "SkyBlue", + "RGB": "0x87CEEB" + } + ] + }, + { + "Justifications" : [ + { + "User": "Left", + "Library": "justifyLeft" + }, + { + "User": "Right", + "Library": "justifyRight" + }, + { + "User": "Center", + "Library": "justifyCenter" + } + ] + } + ] + }, + { + "GWIN" : [ + { + "Fonts" : [ + { + "User": "uGFX Font 1", + "Library": "UI1", + "Config": "GDISP_INCLUDE_FONT_UI1", + "Size": 10 + }, + { + "User": "uGFX Font 2", + "Library": "UI2", + "Config": "GDISP_INCLUDE_FONT_UI2", + "Size": 10 + }, + { + "User": "Fixed 5x8", + "Library": "fixed_5x8", + "Config": "GDISP_INCLUDE_FONT_FIXED_5X8", + "Size": 8 + }, + { + "User": "Fixed 7x14", + "Library": "fixed_7x14", + "Config": "GDISP_INCLUDE_FONT_FIXED_7X14", + "Size": 14 + }, + { + "User": "Fixed 10x20", + "Library": "fixed_10x20", + "Config": "GDISP_INCLUDE_FONT_FIXED_10X20", + "Size": 20 + }, + { + "User": "DejaVu Sans 10", + "Library": "DejaVuSans10", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS10", + "Size": 10 + }, + { + "User": "DejaVu Sans 12", + "Library": "DejaVuSans12", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS12", + "Size": 12 + }, + { + "User": "DejaVu Sans 16", + "Library": "DejaVuSans16", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS16", + "Size": 16 + }, + { + "User": "DejaVu Sans 20", + "Library": "DejaVuSans20", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS20", + "Size": 20 + }, + { + "User": "DejaVu Sans 24", + "Library": "DejaVuSans24", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS24", + "Size": 24 + }, + { + "User": "DejaVu Sans 32", + "Library": "DejaVuSans32", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS32", + "Size": 32 + }, + { + "User": "DejaVu Sans 12 Anti-Aliased", + "Library": "DejaVuSans12_aa", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS12_AA", + "Size": 12 + }, + { + "User": "DejaVu Sans 16 Anti-Aliased", + "Library": "DejaVuSans16_aa", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS16_AA", + "Size": 16 + }, + { + "User": "DejaVu Sans 20 Anti-Aliased", + "Library": "DejaVuSans20_aa", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS20_AA", + "Size": 20 + }, + { + "User": "DejaVu Sans 24 Anti-Aliased", + "Library": "DejaVuSans24_aa", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS24_AA", + "Size": 24 + }, + { + "User": "DejaVu Sans 32 Anti-Aliased", + "Library": "DejaVuSans32_aa", + "Config": "GDISP_INCLUDE_FONT_DEJAVUSANS32_AA", + "Size": 32 + }, + { + "User": "Large Numbers", + "Library": "LargeNumbers", + "Config": "GDISP_INCLUDE_FONT_LARGENUMBERS", + "Size": 23 + } + ] + }, + { + "Styles" : [ + { + "User": "White", + "Handle": "WhiteWidgetStyle", + "Colors": + { + "Background": + { + "User": "", + "Library": "", + "RGB": "0xFFFFFF" + }, + "Focus": + { + "User": "", + "Library": "", + "RGB": "0x2A8FCD" + }, + "Enabled": + { + "Text": + { + "User": "", + "Library": "", + "RGB": "0x000000" + }, + "Edge": + { + "User": "", + "Library": "", + "RGB": "0x404040" + }, + "Fill": + { + "User": "", + "Library": "", + "RGB": "0xE0E0E0" + }, + "Progress": + { + "User": "", + "Library": "", + "RGB": "0xE0E0E0" + } + }, + "Disabled": + { + "Text": + { + "User": "", + "Library": "", + "RGB": "0xC0C0C0" + }, + "Edge": + { + "User": "", + "Library": "", + "RGB": "0x808080" + }, + "Fill": + { + "User": "", + "Library": "", + "RGB": "0xE0E0E0" + }, + "Progress": + { + "User": "", + "Library": "", + "RGB": "0xC0E0C0" + } + }, + "Pressed": + { + "Text": + { + "User": "", + "Library": "", + "RGB": "0x404040" + }, + "Edge": + { + "User": "", + "Library": "", + "RGB": "0x404040" + }, + "Fill": + { + "User": "", + "Library": "", + "RGB": "0x808080" + }, + "Progress": + { + "User": "", + "Library": "", + "RGB": "0x00E000" + } + } + } + }, + { + "User": "Black", + "Handle": "BlackWidgetStyle", + "Colors": + { + "Background": + { + "User": "", + "Library": "", + "RGB": "0x000000" + }, + "Focus": + { + "User": "", + "Library": "", + "RGB": "0x2A8FCD" + }, + "Enabled": + { + "Text": + { + "User": "", + "Library": "", + "RGB": "0xC0C0C0" + }, + "Edge": + { + "User": "", + "Library": "", + "RGB": "0xC0C0C0" + }, + "Fill": + { + "User": "", + "Library": "", + "RGB": "0x606060" + }, + "Progress": + { + "User": "", + "Library": "", + "RGB": "0x404040" + } + }, + "Disabled": + { + "Text": + { + "User": "", + "Library": "", + "RGB": "0x808080" + }, + "Edge": + { + "User": "", + "Library": "", + "RGB": "0x404040" + }, + "Fill": + { + "User": "", + "Library": "", + "RGB": "0x404040" + }, + "Progress": + { + "User": "", + "Library": "", + "RGB": "0x004000" + } + }, + "Pressed": + { + "Text": + { + "User": "", + "Library": "", + "RGB": "0xFFFFFF" + }, + "Edge": + { + "User": "", + "Library": "", + "RGB": "0xC0C0C0" + }, + "Fill": + { + "User": "", + "Library": "", + "RGB": "0xE0E0E0" + }, + "Progress": + { + "User": "", + "Library": "", + "RGB": "0x008000" + } + } + } + } + ] + } + ] + } +] From 2867d6b8f96d899153bc7dd030e0644afe816592 Mon Sep 17 00:00:00 2001 From: inmarket Date: Tue, 18 Aug 2015 07:39:33 +1000 Subject: [PATCH 39/45] Another fix to gdriver --- src/gdriver/gdriver.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/gdriver/gdriver.c b/src/gdriver/gdriver.c index c90ad8e3..116abd32 100644 --- a/src/gdriver/gdriver.c +++ b/src/gdriver/gdriver.c @@ -52,13 +52,12 @@ GDriver *gdriverRegister(const GDriverVMT *vmt, void *param) { return 0; } - // Add it to the driver chain - if (dhead) { + // Add it to the driver chain (at the end) + if (dhead) dtail->driverchain = pd; - dtail = pd; - } else { - dhead = dtail = pd; - } + else + dhead = pd; + dtail = pd; // Do the post init if (vmt->postinit) @@ -81,6 +80,8 @@ void gdriverUnRegister(GDriver *driver) { for(pd = dhead; pd->driverchain; pd = pd->driverchain) { if (pd->driverchain == driver) { pd->driverchain = driver->driverchain; + if (!pd->driverchain) + dtail = pd; break; } } From 367089f10d5984e3dec6d455b5871feef01dcfda Mon Sep 17 00:00:00 2001 From: inmarket Date: Fri, 18 Sep 2015 11:17:19 +1000 Subject: [PATCH 40/45] Add support for SH1106 in the SSD1306 driver --- drivers/gdisp/SSD1306/SSD1306.h | 3 +++ drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/gdisp/SSD1306/SSD1306.h b/drivers/gdisp/SSD1306/SSD1306.h index 7d7010e8..8ec8b3ad 100644 --- a/drivers/gdisp/SSD1306/SSD1306.h +++ b/drivers/gdisp/SSD1306/SSD1306.h @@ -44,6 +44,9 @@ #define SSD1306_EXTERNALVCC 0x1 #define SSD1306_SWITCHCAPVCC 0x2 +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 + // Scrolling #defines #define SSD1306_SCROLL_ACTIVATE 0x2F #define SSD1306_SCROLL_DEACTIVATE 0x2E diff --git a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c index 2313e765..5a950b0d 100644 --- a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c +++ b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c @@ -150,6 +150,12 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { write_cmd(g, SSD1306_SETSTARTLINE | 0); while (pages--) { + #if SSD1306_SH1106 + write_cmd(g, SSD1306_PAM_PAGE_START + (7 - pages)); + write_cmd(g, SSD1306_SETLOWCOLUMN + 2); + write_cmd(g, SSD1306_SETHIGHCOLUMN); + #endif + write_data(g, ram, SSD1306_PAGE_WIDTH); ram += SSD1306_PAGE_WIDTH; } From 754d2e072b51bc43c72a37d46e22337d594f18dc Mon Sep 17 00:00:00 2001 From: inmarket Date: Mon, 5 Oct 2015 07:33:26 +1000 Subject: [PATCH 41/45] Minor update --- boards/base/Embest-STM32-DMSTF4BB/board.mk | 2 +- boards/base/Embest-STM32-DMSTF4BB/example_chibios_2.x/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/boards/base/Embest-STM32-DMSTF4BB/board.mk b/boards/base/Embest-STM32-DMSTF4BB/board.mk index 09bfaadf..cd17a8ae 100644 --- a/boards/base/Embest-STM32-DMSTF4BB/board.mk +++ b/boards/base/Embest-STM32-DMSTF4BB/board.mk @@ -1,6 +1,6 @@ GFXINC += $(GFXLIB)/boards/base/Embest-STM32-DMSTF4BB GFXSRC += -GFXDEFS += -DGFX_USE_CHIBIOS=FALSE +GFXDEFS += -DGFX_USE_CHIBIOS=TRUE include $(GFXLIB)/drivers/gdisp/SSD2119/driver.mk include $(GFXLIB)/drivers/ginput/touch/STMPE811/driver.mk diff --git a/boards/base/Embest-STM32-DMSTF4BB/example_chibios_2.x/Makefile b/boards/base/Embest-STM32-DMSTF4BB/example_chibios_2.x/Makefile index a9ba950b..d709f9fe 100644 --- a/boards/base/Embest-STM32-DMSTF4BB/example_chibios_2.x/Makefile +++ b/boards/base/Embest-STM32-DMSTF4BB/example_chibios_2.x/Makefile @@ -39,7 +39,7 @@ CXXFLAGS = -fno-rtti ASFLAGS = LDFLAGS = -SRC = board.c +SRC = OBJS = DEFS = LIBS = From 52783f7cc44a06251649c179f1fa00916436f925 Mon Sep 17 00:00:00 2001 From: inmarket Date: Tue, 6 Oct 2015 12:10:59 +1000 Subject: [PATCH 42/45] Spacing fix --- src/gdisp/gdisp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index d0c31ebb..d51ab917 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -111,7 +111,7 @@ typedef enum powermode { /* * Our black box display structure. */ -typedef struct GDisplay GDisplay; +typedef struct GDisplay GDisplay; /** * @brief The default screen to use for the gdispXXXX calls. From 5c615c3430cf1457d721f22a90808edc067fb6ea Mon Sep 17 00:00:00 2001 From: inmarket Date: Tue, 6 Oct 2015 17:27:24 +1000 Subject: [PATCH 43/45] Stop overwriting by the bounce ball when switching away from it in the applications/combo demo --- demos/applications/combo/bounce.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/demos/applications/combo/bounce.c b/demos/applications/combo/bounce.c index 72e75af8..f0b78266 100644 --- a/demos/applications/combo/bounce.c +++ b/demos/applications/combo/bounce.c @@ -91,8 +91,6 @@ static DECLARE_THREAD_FUNCTION(task, param) { minx = miny = 0; maxx = width; maxy = height; // The clipping window for this frame. while(run) { - gfxYield(); - // Draw one frame gdispStreamStart(winx+minx, winy+miny, maxx-minx, maxy-miny); for (y=miny; h = (bally-y)*ii, y width-radius ? spinspeed=-spinspeed,-dx : dx; dy = bally > height-1.75*floor ? -.04*height : dy+.002*height; + + // Give someone else a go on cooperative os's like RAW32 + gfxYield(); } return 0; } From 5e8e0b7744a59c5bbf918a5133b55c3d1beef12f Mon Sep 17 00:00:00 2001 From: inmarket Date: Tue, 13 Oct 2015 00:58:31 +1000 Subject: [PATCH 44/45] Working TextEdit with on-screen keyboard (and real keyboard) --- src/gwin/gwin_class.h | 2 + src/gwin/gwin_textedit.c | 239 ++++++++++++++++++++------------------- src/gwin/gwin_widget.c | 22 ++-- src/gwin/gwin_widget.h | 2 +- 4 files changed, 140 insertions(+), 125 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,112 +18,111 @@ #include // 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 bool_t resizeText(GWidgetObject* gw, size_t pos, int32_t diff) { + char *p, *q; + size_t sz; -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++; + 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); - - // Terminate the string - buffer[indexTerminator-1] = '\0'; + return TRUE; } -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++; - } - - // Shift - memcpy(&buffer[index+1], &buffer[index], indexTerminator-index); - - // Fill the gap - buffer[index] = fillChar; -} - - -#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; + 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; } } - // Parse the key press - else if (pke->bytecount >= 1) { - // Is it backspace? - if (pke->c[0] == GKEY_BACKSPACE) { - if (gw2obj->cursorPos == 0) { - return; - } - _shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos--); - } - - // 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') { - return; - } - _shiftTextLeft(gw2obj->textBuffer, gw2obj->maxSize, gw2obj->cursorPos+1); - } - - // Add a new character - else { - // Prevent buffer overflow - if (gw2obj->cursorPos >= 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]); - } - - // Set the new text - gwinSetText((GHandle)gw, gw2obj->textBuffer, FALSE); - } - _gwinUpdate((GHandle)gw); } #endif @@ -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 From 5cf81b6fbe76cd5decab8e532fd607202abbc6c6 Mon Sep 17 00:00:00 2001 From: inmarket Date: Tue, 13 Oct 2015 00:59:42 +1000 Subject: [PATCH 45/45] Doco --- src/gwin/gwin_textedit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h index f83233b4..f39f0c2a 100644 --- a/src/gwin/gwin_textedit.h +++ b/src/gwin/gwin_textedit.h @@ -50,7 +50,7 @@ extern "C" { * @param[in] g The GDisplay on which the textedit should be displayed * @param[in] wt The TextEdit structure to initialise. If this is NULL, the structure is dynamically allocated. * @param[in] pInit The initialisation parameters to use. - * @param[in] maxSize The maximum number of characters the TextEdit widget can hold. + * @param[in] maxSize The maximum number of characters the TextEdit widget can hold. (0 means unlimited). * * @return NULL if there is no resultant drawing area, otherwise the widget handle. *