Compare commits

...

5 Commits

7 changed files with 556 additions and 1 deletions

View File

@ -215,6 +215,7 @@
// #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1
// #define GWIN_NEED_KEYBOARD_ENGLISH1 TRUE
// #define GWIN_NEED_TEXTEDIT FALSE
// #define GWIN_NEED_SPINBOX FALSE
// #define GWIN_FLAT_STYLING FALSE
// #define GWIN_WIDGET_TAGS FALSE

View File

@ -22,6 +22,7 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin.c \
$(GFXLIB)/src/gwin/gwin_gl3d.c \
$(GFXLIB)/src/gwin/gwin_keyboard.c \
$(GFXLIB)/src/gwin/gwin_keyboard_layout.c \
$(GFXLIB)/src/gwin/gwin_textedit.c
$(GFXLIB)/src/gwin/gwin_textedit.c \
$(GFXLIB)/src/gwin/gwin_spinbox.c
GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include

View File

@ -25,3 +25,4 @@
#include "gwin_keyboard.c"
#include "gwin_keyboard_layout.c"
#include "gwin_textedit.c"
#include "gwin_spinbox.c"

View File

@ -163,6 +163,13 @@
#ifndef GWIN_NEED_TEXTEDIT
#define GWIN_NEED_TEXTEDIT FALSE
#endif
/**
* @brief Should the spinbox widget be included.
* @details Defaults to FALSE
*/
#ifndef GWIN_NEED_SPINBOX
#define GWIN_NEED_SPINBOX FALSE
#endif
/**
* @}
*

360
src/gwin/gwin_spinbox.c Normal file
View File

@ -0,0 +1,360 @@
/*
* 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_spinbox.c
* @brief GWIN sub-system spinbox code
*/
// This widget uses partial redraw by using the widget flags to indicate which
// portions of the widget needs to be redrawn.
#include "../../gfx.h"
#if GFX_USE_GWIN && GWIN_NEED_SPINBOX
#include "gwin_class.h"
#include "stdlib.h"
#include "string.h"
// Configuration options
#define TEXTPADDING 2
#define BOXPADDING 8
#define MAXCHARS 20
// Macros to assist in data type conversions
#define gw2obj ((GSpinboxObject *)gw)
#define gw2ButtonSize (gw->g.height)
// Macros to assist in calculations
#define FIELD_WIDTH(gw) (gw->g.width - 2*gw->g.height)
// Function to convert number to string, add decimal mark and sign
// Parameter s initially points to end of buffer
// ToDo: Improve this function. Need some serious clean-up & proper error detection/prevention/handling.
char* fmtNum(int value, char* s, short placesOrTxtlen, const char* mark)
{
uint8_t places = 0;
bool_t sign;
bool_t markAdded = FALSE;
sign = (value < 0) ? 1 : 0; // Sign flag
*--s = 0; // Add null-terminator to end of string
for (; value; value /= 10) {
*--s = '0' + abs(value) % 10;
if (++places == placesOrTxtlen) {
*--s = *mark; // Add decimal mark
markAdded = TRUE;
}
}
if (placesOrTxtlen && !markAdded) { // If a decimalplaces and no mark added then add leading zeroes
do {
*--s = '0';
} while (++places != placesOrTxtlen);
*--s = *mark; // Add decimal mark
}
if (*s == *mark)
*--s = '0'; // If first char is a decimalmark then add a leading zero
if (sign)
*--s = '-';
return s;
}
static void sendSpinboxEvent(GSpinboxObject* gsw)
{
#define ple ((GEventGWinSpinbox*)pe)
GSourceListener* psl;
GEvent* pe;
// Trigger a GWIN spinbox event
psl = 0;
while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
if (!(pe = geventGetEventBuffer(psl)))
continue;
// Fill in the event
ple->type = GEVENT_GWIN_SPINBOX;
ple->gwin = (GHandle) gsw;
ple->value = gsw->initial;
#if GWIN_WIDGET_TAGS
ple->tag = gw->tag;
#endif
geventSendEvent(psl);
}
#undef ple
}
static void mouseUp(GWidgetObject* gw, coord_t x, coord_t y)
{
#define gsw ((GSpinboxObject*)gw)
// Store the current value so we can figure out whether the value
// changed as we don't want to send events if the value remains
// the same.
int oldValue = gsw->initial;
// Only handle releases on the up/down buttons
if (y <= gw2ButtonSize) {
// The 'down' area was released
if (x >= gw->g.width - gw2ButtonSize) {
gsw->initial -= gsw->increment;
if (gsw->initial < gsw->minval)
gsw->initial = gsw->minval;
}
else if (x <= gw->g.x + gw2ButtonSize) {
// The 'up' area was released
gsw->initial += gsw->increment;
if (gsw->initial > gsw->maxval)
gsw->initial = gsw->maxval;
}
// Otherwise, don't do anything
else {
return;
}
// Issue a redraw. Use partial redrawing.
gw->g.flags |= GSPINBOX_NUM_REDRAW;
_gwinUpdate((GHandle) gw);
// Spinbox events are sent upon mouse release
if (gsw->initial != oldValue)
sendSpinboxEvent(gsw);
}
#undef gsw
}
static const gwidgetVMT SpinboxVMT = {
{
"Spinbox", // The class name
sizeof(GSpinboxObject), // The object size
_gwidgetDestroy, // The destroy routine
_gwidgetRedraw, // The redraw routine
0, // The after-clear routine
},
gwinSpinboxDefaultDraw, // Default drawing routine
#if GINPUT_NEED_MOUSE
{ 0, // Process mouse down events
mouseUp, // Process mouse up events
0, // Process mouse move events
},
#endif
#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
{
0 // Process keyboard key down events
},
#endif
#if GINPUT_NEED_TOGGLE
{
0, // No toggle role
0, // Assign Toggles
0, // Get Toggles
0, // Process toggle off event
0, // Process toggle on event
},
#endif
#if GINPUT_NEED_DIAL
{
0, // Dial roles
0, // Assign Dials
0, // Get Dials
0, // Procees dial move events
},
#endif
};
GHandle gwinGSpinboxTxtCreate(GDisplay* g, GSpinboxObject* wt, GWidgetInit* pInit, const char** textArray, uint8_t numEntries)
{
// Create the widget
if (!(wt = (GSpinboxObject*)_gwidgetCreate(g, &wt->w, pInit, &SpinboxVMT))) {
return 0;
}
// Initialize
wt->initial = 0; // Set to first item in string array
wt->minval = 0; // Set minval to first text item index
wt->maxval = numEntries-1; // Set maxval to last text item index
wt->increment = 1;
wt->txtArray = textArray;
wt->w.g.flags |= GSPINBOX_TXT; // Set flag for text spinbox
gwinSetVisible(&wt->w.g, pInit->g.show);
return (GHandle)wt;
}
GHandle gwinGSpinboxNumCreate(GDisplay* g, GSpinboxObject* wt, GWidgetInit* pInit,
int init, int min, int max, int step, const char* mark,
short places, const char* units) {
// Create the widget
if (!(wt = (GSpinboxObject*) _gwidgetCreate(g, &wt->w, pInit, &SpinboxVMT))) {
return 0;
}
// Initialize
wt->initial = init;
wt->minval = min;
wt->maxval = max;
wt->increment = step;
wt->decimalmark = mark;
wt->placesOrTxtlen = places;
wt->strData = units;
wt->w.g.flags |= GSPINBOX_NUM; // Set flag for numeric spinbox
gwinSetVisible(&wt->w.g, pInit->g.show);
return (GHandle)wt;
}
void gwinSpinboxDefaultDraw(GWidgetObject* gw, void* param)
{
#define gsw ((GSpinboxObject*)gw)
(void)param;
char p[MAXCHARS]; // spinbox text max char
char* ptr;
const GColorSet* ps;
coord_t num=0;
coord_t arrowSize, border;
// is it a valid handle?
if (gw->g.vmt != (gwinVMT*)&SpinboxVMT) {
return;
}
// Retrieve colours
if ((gw->g.flags & GWIN_FLG_SYSENABLED)) {
ps = &gw->pstyle->enabled;
} else {
ps = &gw->pstyle->disabled;
}
if (gw->g.flags & GSPINBOX_NUM) {
// Add padding zeroes for required decimalplaces
if (gsw->placesOrTxtlen >= num) {
while (num <= gsw->placesOrTxtlen)
num++; // If num <= places, (not including sign), add 1 to num for each additional place inc leading 0
num++; // Add 1 for digit before decimal mark
}
// format text string for display including sign and decimal mark if required
ptr = &p[MAXCHARS - 1]; // End of buffer
ptr = fmtNum(gsw->initial, ptr, gsw->placesOrTxtlen, gsw->decimalmark);
}
else if (gw->g.flags & GSPINBOX_TXT) {
ptr = (char*)gsw->txtArray[gsw->initial];
}
// Only numeric/text field requires updating if GSPINBOX_NUM_REDRAW flag set
if ((gw->g.flags & GSPINBOX_NUM_REDRAW)) {
// Fill the stringbox with value - justifyRight
gdispGFillStringBox(gw->g.display, gw->g.x + gw2ButtonSize + 1 + TEXTPADDING,
gw->g.y + 1, gw->g.width - 2 * gw2ButtonSize - gdispGetStringWidth(gsw->strData, gw->g.font) - 6, gw->g.height - 2, ptr,
gw->g.font, ps->text, gw->pstyle->background, justifyRight);
gw->g.flags &= ~GSPINBOX_NUM_REDRAW; // Reset flag
} else {
border = gw2ButtonSize / 8; // border is one eighth of gw2ButtonWidth
arrowSize = border*6; // arrowsize is 0.75 * gw2ButtonWidth
#if GDISP_NEED_CONVEX_POLYGON
point upArrow[3] = {
{ 0, arrowSize },
{ arrowSize + 1, arrowSize },
{ arrowSize / 2 + 1, 0 }
};
point downArrow[3] = {
{ 0, -arrowSize },
{ arrowSize, -arrowSize },
{ arrowSize / 2, 0 }
};
#endif
// Draw the border
gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width,
gw->g.height, ps->edge);
// Fill left button box background area
gdispGFillArea(gw->g.display, gw->g.x + 1,
gw->g.y + 1, gw2ButtonSize-1, gw->g.height - 2,
gdispBlendColor(ps->fill, gw->pstyle->background, 128));
// Fill right button box background area
gdispGFillArea(gw->g.display, gw->g.x + gw->g.width - gw2ButtonSize,
gw->g.y + 1, gw2ButtonSize-1, gw->g.height - 2,
gdispBlendColor(ps->fill, gw->pstyle->background, 128));
// Draw left vertical line
gdispGDrawLine(gw->g.display, gw->g.x + gw2ButtonSize,
gw->g.y + 1, gw->g.x + gw2ButtonSize,
gw->g.y + gw->g.height - 2, ps->edge);
// Draw right vertical line
gdispGDrawLine(gw->g.display, gw->g.x + gw->g.width - gw2ButtonSize,
gw->g.y + 1, gw->g.x + gw->g.width - gw2ButtonSize,
gw->g.y + gw->g.height - 2, ps->edge);
#if GDISP_NEED_CONVEX_POLYGON
// Up Arrow
gdispGFillConvexPoly(gw->g.display,gw->g.x + border, gw->g.y + border, upArrow, 3, ps->edge);
// Down Arrow
gdispGFillConvexPoly(gw->g.display,gw->g.x + gw->g.width - arrowSize - border,gw->g.y + gw->g.height - border,downArrow,3,ps->edge);
#else
#warning "GWIN: Spinbox will display arrow symbols when GDISP_NEED_CONVEX_POLYGON is turned on"
// Plus symbol horizontal line
gdispGDrawLine(gw->g.display, gw->g.x + border,
gw->g.y + gw->g.height / 2, gw->g.x + gw2ButtonSize - border,
gw->g.y + gw->g.height / 2, ps->edge);
// Plus symbol vertical line
gdispGDrawLine(gw->g.display, gw->g.x + gw2ButtonSize/2,
gw->g.y + border, gw->g.x + gw2ButtonSize/2,
gw->g.y + gw->g.height - border-1, ps->edge);
// Minus symbol horizontal line
gdispGDrawLine(gw->g.display, gw->g.x + gw->g.width - 1 - border,
gw->g.y + gw->g.height - gw->g.height / 2 -1, gw->g.x + gw->g.width - gw2ButtonSize + border,
gw->g.y + gw->g.height - gw->g.height / 2 -1, ps->edge);
#endif
if ((gw->g.flags & GSPINBOX_NUM)) {
// Fill the stringbox with units - justifyRight
gdispGFillStringBox(gw->g.display, gw->g.x + gw->g.width - gw2ButtonSize - gdispGetStringWidth(gsw->strData, gw->g.font) - TEXTPADDING,
gw->g.y + 1, gdispGetStringWidth(gsw->strData, gw->g.font),
gw->g.height - 2, gsw->strData, gw->g.font, ps->text,
gw->pstyle->background, justifyRight);
}
// Fill the stringbox with value - justifyRight
gdispGFillStringBox(gw->g.display, gw->g.x + gw2ButtonSize + 1 + TEXTPADDING,
gw->g.y + 1, gw->g.width - 2 * gw2ButtonSize - gdispGetStringWidth(gsw->strData, gw->g.font) - 6, gw->g.height - 2, ptr, gw->g.font, ps->text, gw->pstyle->background, justifyRight);
}
#undef gsw
}
// Limit the scope of the macros. Required for single-file compilation.
#undef TEXTPADDING
#undef BOXPADDING
#undef MAXCHARS
#undef gw2obj
#undef gw2ButtonSize
#endif // GFX_USE_GWIN && GWIN_NEED_SPINBOX

181
src/gwin/gwin_spinbox.h Normal file
View File

@ -0,0 +1,181 @@
/*
* 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_spinbox.h
* @brief GWIN Spinbox widget header file
*
* @defgroup Spinbox Spinbox
* @ingroup Widgets
*
* @brief Widget that spins through a text array or numeric values and returns an integer for the selection.
*
* @note The numeric spinbox is created using the following syntax:
* ghSpinBox1 = gwinSpinboxNumCreate(0, &wi, 10, 0, 90, 3, ",", 1, "px");
* where parms = initial, min, max, step, mark, places, units, (see typedef struct GSpinboxObject)
*
* The text spinbox is created using the following syntax:
* ghSpinBox2 = gwinSpinboxTxtCreate(0, &wi, DaysOfWeek);
* where DaysOfWeek is defined thus:
* static const char *DaysOfWeek[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", 0};
*
* GDISP_NEED_CONVEX_POLYGON will display up/down arrows if set, '+' & '-' symbols if not
*
* @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_SPINBOX 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_SPINBOX_H
#define _GWIN_SPINBOX_H
// This file is included within "src/gwin/gwin_widget.h"
/**
* @brief The event type for a spinbox event
*/
#define GEVENT_GWIN_SPINBOX (GEVENT_GWIN_CTRL_FIRST+10)
/**
* @brief The internal spinbox flags
* @note Used only for writing a custom draw routine.
* @{
*/
#define GSPINBOX_NUM_REDRAW 0x01 // Set flag for minimal redraw of numeric value or text, (no widget or units redrawn)
#define GSPINBOX_TXT 0x02 // Flag for text spinbox code flow
#define GSPINBOX_NUM 0x04 // Flag for numeric spinbox code flow
#define GSPINBOX_UPDOWN 0x08 // Increment/decrement flag
/** @} */
/**
* @brief A spinbox event
*/
typedef struct GEventGWinSpinbox {
GEventType type; // The type of this event (GEVENT_GWIN_SPINBOX)
GHandle gwin; // The spinbox
#if GWIN_WIDGET_TAGS
WidgetTag tag; // Tag is an short - allows user to assign a numeric ID to identify widget without knowing its type.
#endif
int value; // The spinbox value
} GEventGWinSpinbox;
// A Spinbox widget
typedef struct GSpinboxObject {
GWidgetObject w; // Base Class
int initial; // Initial value and current value
int minval; // Minimum numeric value
int maxval; // Maximum numeric value
int increment; // Numeric step value
const char* decimalmark; // Decimal mark character (eg. ".", "," etc.)
short placesOrTxtlen; // Number of digits after decimal point or the length of the text string
const char* strData; // Inits for numeric spinbox
const char** txtArray; // Pointer to array of pointers for spinbox text data
} GSpinboxObject;
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Create a text Spinbox widget
* @details A Spinbox widget is a rectangular box which can contain text or numeric data and allows the user to select
* different numeric/text values with the use of mouse/touchpanel, (up/down arrows, keyboard & dial controls not supported).
*
* @param[in] g The GDisplay on which the Spinbox should be displayed
* @param[in] wt The Spinbox structure to initialise. If this is NULL, the structure is dynamically allocated.
* @param[in] pInit The initialisation parameters to use.
* @param[in] textArray Array of strings through which the spinbox will spin through.
* @param[in] numEntries The number of strings in the @p textArray.
* @param[in] numEntries Text field pixel width in Text Spinbox,Value field width in Numeric Spinbox
*
* @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 gwinGSpinboxTxtCreate(GDisplay* g, GSpinboxObject* wt, GWidgetInit* pInit, const char** textArray, uint8_t numEntries);
#define gwinSpinboxTxtCreate(wt, pInit, textArray, numEntries) gwinGSpinboxTxtCreate(GDISP, wt, pInit, textArray, numEntries)
/**
* @brief Create a numeric Spinbox widget
* @details A Spinbox widget is a rectangular box which can contain text or numeric data and allows the user to select
* different numeric/text values with the use of mouse/touchpanel, (up/down arrows, keyboard & dial controls not supported).
*
* @param[in] g The GDisplay on which the Spinbox should be displayed
* @param[in] wt The Spinbox structure to initialise. If this is NULL, the structure is dynamically allocated.
* @param[in] pInit The initialisation parameters to use.
* @param[in] init The initial spinbox value.
* @param[in] min The minimum spinbox value.
* @param[in] max The maximum spinbox value.
* @param[in] step The increment value.
* @param[in] mark The mark character separator, (decimal point, comma, colon etc).
* @param[in] places The number of places after the mark, (0 will prevent any mark displayed).
* @param[in] units The units string, (blank for no units).
*
* @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 gwinGSpinboxNumCreate(GDisplay* g, GSpinboxObject* wt, GWidgetInit* pInit, int init, int min, int max, int step, const char* mark, short places, const char* units);
#define gwinSpinboxNumCreate(wt, pInit, init, min, max, step, mark, places, units) gwinGSpinboxNumCreate(GDISP, wt, pInit, init, min, max, step, mark, places, units)
/**
* @brief Set the spinbox minimum and maximum values.
*
* @param[in] gh The window handle (must be a spinbox window)
* @param[in] min The minimum value
* @param[in] max The maximum value
* @param[in] step The value to increment
* @note Sets the minimum, maximum and step values, returns if min > max.
*
* @api
*/
void gwinSpinboxSetParams(GHandle gh, int initial, int min, int max, int step, const char* decimalmark, short placesOrTxtlen, const char* units);
/**
* @defgroup Renderings_Spinbox Renderings
*
* @brief Built-in rendering functions for the Spinbox widget.
*
* @details These function may be passed to @p gwinSetCustomDraw() to get different Spinbox drawing styles.
*
* @note In your custom Spinbox drawing function you may optionally call these
* standard functions and then draw your extra details on top.
* @note These custom drawing routines don't have to worry about setting clipping as the framework
* sets clipping to the object window prior to calling these routines.
*
* @{
*/
/**
* @brief The default rendering function for the Spinbox widget.
*
* @param[in] gw The widget object (must be a Spinbox).
* @param[in] param A parameter passed in from the user. Ignored by this function.
*
* @api
*/
void gwinSpinboxDefaultDraw(GWidgetObject* gw, void* param);
/** @} */
#ifdef __cplusplus
}
#endif
#endif // _GWIN_Spinbox_H
/** @} */

View File

@ -447,5 +447,9 @@ bool_t gwinAttachListener(GListener *pl);
#include "gwin_textedit.h"
#endif
#if GWIN_NEED_SPINBOX || defined(__DOXYGEN__)
#include "gwin_spinbox.h"
#endif
#endif /* _GWIDGET_H */
/** @} */