serial_debugger/hardware/_controller/src/guislice/XKeyPad.c

715 lines
24 KiB
C

// =======================================================================
// GUIslice library extension: XKeyPad control
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XKeyPad.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
#if (GSLC_FEATURE_COMPOUND)
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: KeyPad
// - Keypad element with numeric input
// - Optionally supports floating point values
// - Optionally supports negative values
// ============================================================================
// Define the button labels
// - TODO: Create more efficient storage for the labels
// so that it doesn't consume 4 bytes even for labels
// that can be generated (eg. 0..9, a--z, etc.)
// - NOTE: Not using "const char" in order to support
// modification of labels by user.
const char KEYPAD_DISP_NEGATIVE = '-';
const char KEYPAD_DISP_DECIMAL_PT = '.';
// --------------------------------------------------------------------------
// Create the keypad definition
// --------------------------------------------------------------------------
void XKeyPadAddKeyElem(gslc_tsGui* pGui, gslc_tsXKeyPad* pXData, int16_t nKeyId, bool bTxtField, int16_t nRow, int16_t nCol, int8_t nRowSpan, int8_t nColSpan,
gslc_tsColor cColFill, gslc_tsColor cColGlow, bool bVisible)
{
gslc_tsXKeyPadCfg* pConfig;
char* pKeyStr = NULL;
pConfig = &(pXData->sConfig);
gslc_tsElemRef* pElemRefTmp = NULL;
gslc_tsElem* pElemTmp = NULL;
int16_t nButtonSzW = pConfig->nButtonSzW;
int16_t nButtonSzH = pConfig->nButtonSzH;
int16_t nOffsetX = pConfig->nOffsetX;
int16_t nOffsetY = pConfig->nOffsetY;
int8_t nFontId = pConfig->nFontId;
int16_t nButtonW = (nColSpan * nButtonSzW);
int16_t nButtonH = (nRowSpan * nButtonSzH);
// Fetch the keypad key string
if (bTxtField) {
pKeyStr = (char*)pXData->acValStr;
} else {
XKEYPAD_LOOKUP pfuncLookup = pXData->pfuncLookup;
int16_t nKeyInd = (*pfuncLookup)(pGui, nKeyId);
pKeyStr = pXData->pacKeys[nKeyInd];
}
//GSLC_DEBUG2_PRINT("DBG: KeyId=%d R=%d C=%d str=[%s] SubElemMax=%d\n", nKeyId, nRow,nCol,pKeyStr,pXData->nSubElemMax);
if (bTxtField) {
// Text field
pElemRefTmp = gslc_ElemCreateTxt(pGui, nKeyId, GSLC_PAGE_NONE,
(gslc_tsRect) { nOffsetX + (nCol*nButtonSzW), nOffsetY + (nRow*nButtonSzH), nButtonW-1, nButtonH - 1 },
pKeyStr, XKEYPAD_VAL_LEN, nFontId);
gslc_ElemSetFrameEn(pGui, pElemRefTmp, true);
gslc_ElemSetTxtMargin(pGui, pElemRefTmp, 5); // Apply default margin
} else {
// Text button
//GSLC_DEBUG2_PRINT("DrawBtn: %d [%s] @ r=%d,c=%d,width=%d\n", nKeyId, pXData->pacKeys[nKeyId], nRow, nCol, nButtonW);
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui, nKeyId, GSLC_PAGE_NONE,
(gslc_tsRect) { nOffsetX + (nCol*nButtonSzW), nOffsetY + (nRow*nButtonSzH), nButtonW - 1, nButtonH - 1 },
pKeyStr, sizeof(pKeyStr), nFontId, &gslc_ElemXKeyPadClick);
// For the text buttons, optionally use rounded profile if enabled
gslc_ElemSetRoundEn(pGui, pElemRefTmp, pConfig->bRoundEn);
}
// Set color
gslc_ElemSetTxtCol(pGui, pElemRefTmp, GSLC_COL_WHITE);
gslc_ElemSetCol(pGui, pElemRefTmp, GSLC_COL_WHITE, cColFill, cColGlow);
// Set the visibility status
// FIXME: Need to fix dynamic visibility change
// gslc_ElemSetVisible(pGui, pElemRefTmp, bVisible);
if (!bVisible) {
// For now, skip addition of invisible buttons
return;
}
// Add element to compound element collection
pElemTmp = gslc_GetElemFromRef(pGui, pElemRefTmp);
pElemRefTmp = gslc_CollectElemAdd(pGui, &pXData->sCollect, pElemTmp, GSLC_ELEMREF_DEFAULT);
}
gslc_tsElemRef* gslc_ElemXKeyPadCreateBase(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXKeyPad* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig,
XKEYPAD_CREATE pfuncCreate, XKEYPAD_LOOKUP pfuncLookup)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXKeyPadCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL, FUNCSTR);
return NULL;
}
// Fetch config options
int8_t nButtonSzW = pConfig->nButtonSzW;
int8_t nButtonSzH = pConfig->nButtonSzH;
gslc_tsRect rElem;
rElem.x = nX0;
rElem.y = nY0;
rElem.w = (nButtonSzW * pConfig->nMaxCols) + (2 * pConfig->nFrameMargin);
rElem.h = (nButtonSzH * pConfig->nMaxRows) + (2 * pConfig->nFrameMargin);
gslc_tsElem sElem;
// Initialize composite element
sElem = gslc_ElemCreate(pGui, nElemId, nPage, GSLC_TYPEX_KEYPAD, rElem, NULL, 0, GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN; // Don't need to glow outer element
sElem.nGroup = GSLC_GROUP_ID_NONE;
gslc_CollectReset(&pXData->sCollect, pXData->psElem, pXData->nSubElemMax, pXData->psElemRef, pXData->nSubElemMax);
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXKeyPadDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXKeyPadTouch;
// shouldn't be used
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_BLUE;
sElem.colElemFrameGlow = GSLC_COL_BLUE_LT4;
// Now create the sub elements
gslc_tsElemRef* pElemRef = NULL;
// Determine offset coordinate of compound element so that we can
// specify relative positioning during the sub-element Create() operations.
int16_t nOffsetX = nX0 + pConfig->nFrameMargin;
int16_t nOffsetY = nY0 + pConfig->nFrameMargin;
// Reset value string
memset(pXData->acValStr, 0, XKEYPAD_VAL_LEN);
pXData->nValStrPos = 0;
pXData->pacKeys = pConfig->pacKeys;
pXData->pfuncCb = NULL;
pXData->pfuncLookup = pfuncLookup;
pXData->nTargetId = GSLC_ID_NONE; // Default to no target defined
pXData->bValPositive = true;
pXData->bValDecimalPt = false;
// Update config with constructor settings
pConfig->nFontId = nFontId;
pConfig->nOffsetX = nOffsetX;
pConfig->nOffsetY = nOffsetY;
// Copy content into local structure
pXData->sConfig = *pConfig;
// Create the keypad definition
// -- Call XKeyPadCreateKeys()
if (pfuncCreate != NULL) {
(*pfuncCreate)(pGui, pXData);
}
// Now proceed to add the compound element to the page
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui, nPage, &sElem, GSLC_ELEMREF_DEFAULT);
// Now propagate the parent relationship to enable a cascade
// of redrawing from low-level elements to the top
gslc_CollectSetParent(pGui, &pXData->sCollect, pElemRef);
return pElemRef;
}
else {
#if defined(DEBUG_LOG)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXKeyPadCreate(%s) Compound elements inside compound elements not supported\n", "");
#endif
return NULL;
}
}
void gslc_ElemXKeyPadValSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, const char* pStrBuf)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
// Copy over the new value string
strncpy(pKeyPad->acValStr, pStrBuf, XKEYPAD_VAL_LEN);
pKeyPad->acValStr[XKEYPAD_VAL_LEN - 1] = 0; // Force null terminate
// Handle negation and floating point detection
pKeyPad->bValPositive = true;
if (pKeyPad->acValStr[0] == KEYPAD_DISP_NEGATIVE) {
pKeyPad->bValPositive = false;
}
pKeyPad->bValDecimalPt = false;
if (strchr(pKeyPad->acValStr, KEYPAD_DISP_DECIMAL_PT)) {
pKeyPad->bValDecimalPt = true;
}
// Update the current buffer position
pKeyPad->nValStrPos = strlen(pKeyPad->acValStr);
// Find element associated with text field
gslc_tsElemRef* pTxtElemRef = gslc_CollectFindElemById(pGui, &pKeyPad->sCollect, KEYPAD_ID_TXT);
// Mark as needing redraw
gslc_ElemSetRedraw(pGui,pTxtElemRef,GSLC_REDRAW_INC);
}
void gslc_ElemXKeyPadTargetIdSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nTargetId)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->nTargetId = nTargetId;
}
int16_t gslc_ElemXKeyPadDataTargetIdGet(gslc_tsGui* pGui, void* pvData)
{
if (pvData == NULL) {
#if defined(DEBUG_LOG)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXKeyPadDataTargetIdGet() NULL data\n", "");
#endif
return GSLC_ID_NONE;
}
gslc_tsXKeyPadData* pData = (gslc_tsXKeyPadData*)pvData;
return pData->nTargetId;
}
char* gslc_ElemXKeyPadDataValGet(gslc_tsGui* pGui, void* pvData)
{
if (pvData == NULL) {
#if defined(DEBUG_LOG)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXKeyPadDataValGet() NULL data\n", "");
#endif
return NULL;
}
gslc_tsXKeyPadData* pData = (gslc_tsXKeyPadData*)pvData;
return pData->pStr;
}
// NOTE: API changed to pass nStrBufLen (total buffer size including terminator)
// instead of nStrBufMax (max index value)
bool gslc_ElemXKeyPadValGet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, char* pStrBuf, uint8_t nStrBufLen)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return false;
// - Check for negation flag
// - If negative, copy minus sign
// - Copy the remainder of the string
// FIXME: check for shortest of XKEYPAD_VAL_LEN & nStrBufLen
char* pBufPtr = pStrBuf;
uint8_t nMaxCopyLen = nStrBufLen - 1;
// FIXME: Do we still need to do this step?
if (!pKeyPad->bValPositive) {
*pBufPtr = KEYPAD_DISP_NEGATIVE;
pBufPtr++;
nMaxCopyLen--;
}
strncpy(pBufPtr, pKeyPad->acValStr, nMaxCopyLen);
pStrBuf[nStrBufLen-1] = '\0'; // Force termination
// TODO: Do we need to reset the contents?
return true;
}
// Redraw the KeyPad element
// - When drawing a KeyPad we do not clear the background.
// We do redraw the sub-element collection.
bool gslc_ElemXKeyPadDraw(void* pvGui, void* pvElemRef, gslc_teRedrawType eRedraw)
{
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return false;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui, pElemRef);
// Draw any parts of the compound element itself
if (eRedraw == GSLC_REDRAW_FULL) {
// Force the fill to ensure the background doesn't bleed thorugh gaps
// between the sub-elements
gslc_DrawFillRect(pGui, pElem->rElem, pElem->colElemFill);
if (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN) {
gslc_DrawFrameRect(pGui, pElem->rElem, pElem->colElemFrame);
}
}
// Draw the sub-elements
gslc_tsCollect* pCollect = &pKeyPad->sCollect;
if (eRedraw != GSLC_REDRAW_NONE) {
uint8_t sEventSubType = GSLC_EVTSUB_DRAW_NEEDED;
if (eRedraw == GSLC_REDRAW_FULL) {
sEventSubType = GSLC_EVTSUB_DRAW_FORCE;
}
gslc_tsEvent sEvent = gslc_EventCreate(pGui, GSLC_EVT_DRAW, sEventSubType, (void*)(pCollect), NULL);
gslc_CollectEvent(pGui, sEvent);
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui, true);
return true;
}
void gslc_ElemXKeyPadValSetCb(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, GSLC_CB_INPUT pfuncCb)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->pfuncCb = pfuncCb;
}
// Change the sign of the number string
// - This function also performs in-place shifting of the content
void gslc_ElemXKeyPadValSetSign(gslc_tsGui* pGui, gslc_tsElemRef *pElemRef, bool bPositive)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
char* pStrBuf = pKeyPad->acValStr;
//GSLC_DEBUG2_PRINT("SetSign: old=%d new=%d\n", pKeyPad->bValPositive, bPositive);
if (pKeyPad->bValPositive == bPositive) {
// No change to sign
return;
}
//GSLC_DEBUG2_PRINT("SetSign: str_old=[%s] idx=%d\n", pStrBuf,pKeyPad->nValStrPos);
if ((pKeyPad->bValPositive) && (!bPositive)) {
// Change from positive to negative
// - Shift string right one position, and replace first position with minus sign
// - Increase current buffer position
memmove(pStrBuf+1, pStrBuf, XKEYPAD_VAL_LEN-1);
pStrBuf[0] = KEYPAD_DISP_NEGATIVE; // Overwrite with minus sign
pStrBuf[XKEYPAD_VAL_LEN-1] = 0; // Force terminate
pKeyPad->nValStrPos++; // Advance buffer position
} else if ((!pKeyPad->bValPositive) && (bPositive)) {
// Change from negative to positive
// - Shift string left one position, overwriting the minus sign
memmove(pStrBuf, pStrBuf+1, XKEYPAD_VAL_LEN-1);
pStrBuf[XKEYPAD_VAL_LEN-1] = 0; // Force terminate
if (pKeyPad->nValStrPos > 0) {
// Expect that original position should be non-zero if previously minux
pKeyPad->nValStrPos--; // Reduce buffer position
}
}
//GSLC_DEBUG2_PRINT("SetSign: str_new=[%s] idx=%d\n", pStrBuf,pKeyPad->nValStrPos);
// Update sign state
pKeyPad->bValPositive = bPositive;
// The text string sub-element content has already been updated,
// so now we can simply mark it for redraw
gslc_tsElemRef* pTxtElemRef = gslc_CollectFindElemById(pGui, &pKeyPad->sCollect, KEYPAD_ID_TXT);
gslc_ElemSetRedraw(pGui,pTxtElemRef,GSLC_REDRAW_INC);
}
bool ElemXKeyPadAddChar(gslc_tsGui* pGui, gslc_tsXKeyPad* pKeyPad, char ch)
{
bool bRedraw = false;
// Do we have space for this character?
if (pKeyPad->nValStrPos < XKEYPAD_VAL_LEN - 1) {
pKeyPad->acValStr[pKeyPad->nValStrPos] = ch;
pKeyPad->nValStrPos++;
pKeyPad->acValStr[pKeyPad->nValStrPos] = '\0'; // Zero terminate
bRedraw = true;
}
return bRedraw;
}
// Handle the compound element main functionality
// - This routine is called by gslc_ElemEvent() to handle
// any click events that resulted from the touch tracking process.
// - The code here will generally represent the core
// functionality of the compound element and any communication
// between sub-elements.
// - pvElemRef is a void pointer to the element ref being tracked. From
// the pElemRefParent member we can get the parent/compound element
// data structures.
bool gslc_ElemXKeyPadClick(void* pvGui, void *pvElemRef, gslc_teTouch eTouch, int16_t nX, int16_t nY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRefD(pGui, pElemRef, __LINE__);
// Fetch the parent of the clicked element which is the compound
// element itself. This enables us to access the extra control data.
gslc_tsElemRef* pElemRefParent = pElem->pElemRefParent;
if (pElemRefParent == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXKeyPadClick(%s) parent ElemRef ptr NULL\n", "");
return false;
}
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRefParent, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return false;
//GSLC_DEBUG2_PRINT("KeyPad Click Touch=%d\n", eTouch);
// Handle the various button presses
if (eTouch == GSLC_TOUCH_UP_IN) {
// Get the tracked element ID
gslc_tsElemRef* pElemRefTracked = pKeyPad->sCollect.pElemRefTracked;
gslc_tsElem* pElemTracked = gslc_GetElemFromRef(pGui, pElemRefTracked);
int nSubElemId = pElemTracked->nId;
int16_t nKeyInd = 0;
bool bValRedraw = false;
GSLC_CB_INPUT pfuncXInput = pKeyPad->pfuncCb;
XKEYPAD_LOOKUP pfuncLookup = pKeyPad->pfuncLookup;
gslc_tsXKeyPadData sKeyPadData;
if (pfuncLookup != NULL) {
// FIXME: ERROR
}
// Prepare the return data
sKeyPadData.pStr = pKeyPad->acValStr;
sKeyPadData.nTargetId = pKeyPad->nTargetId;
//GSLC_DEBUG2_PRINT("KeyPad Click ID=%d\n", nSubElemId);
switch (nSubElemId) {
case KEYPAD_ID_ENTER:
//GSLC_DEBUG2_PRINT("KeyPad Key=ENT\n", "");
//GSLC_DEBUG2_PRINT("KeyPad Done Str=[%s]\n", pKeyPad->acValStr);
// Issue callback with Done status
if (pfuncXInput != NULL) {
(*pfuncXInput)(pvGui, (void*)(pElemRefParent), XKEYPAD_CB_STATE_DONE, (void*)(&sKeyPadData));
}
// Clear the contents
memset(pKeyPad->acValStr, 0, XKEYPAD_VAL_LEN);
pKeyPad->nValStrPos = 0;
pKeyPad->nTargetId = GSLC_ID_NONE;
break;
case KEYPAD_ID_ESC:
//GSLC_DEBUG2_PRINT("KeyPad Key=ESC\n", "");
// Clear the contents
memset(pKeyPad->acValStr, 0, XKEYPAD_VAL_LEN);
pKeyPad->nValStrPos = 0;
pKeyPad->nTargetId = GSLC_ID_NONE;
bValRedraw = true;
//GSLC_DEBUG2_PRINT("KeyPad Done Str=[%s]\n", pKeyPad->acValStr);
// Issue callback with Cancel status
if (pfuncXInput != NULL) {
(*pfuncXInput)(pvGui, (void*)(pElemRefParent), XKEYPAD_CB_STATE_CANCEL, (void*)(&sKeyPadData));
}
break;
case KEYPAD_ID_DECIMAL:
//GSLC_DEBUG2_PRINT("KeyPad Key=Decimal\n", "");
if (!pKeyPad->sConfig.bFloatEn) break; // Ignore if floating point not enabled
if (!pKeyPad->bValDecimalPt) {
bValRedraw |= ElemXKeyPadAddChar(pGui, pKeyPad, KEYPAD_DISP_DECIMAL_PT);
}
break;
case KEYPAD_ID_MINUS:
//GSLC_DEBUG2_PRINT("KeyPad Key=Minus\n", "");
if (!pKeyPad->sConfig.bSignEn) break; // Ignore if negative numbers not enabled
// Toggle sign
gslc_ElemXKeyPadValSetSign(pGui, pElemRefParent, pKeyPad->bValPositive ? false : true);
bValRedraw = true;
break;
case KEYPAD_ID_BACKSPACE:
//GSLC_DEBUG2_PRINT("KeyPad Key=BS\n", "");
if (pKeyPad->nValStrPos < 1) break;
pKeyPad->nValStrPos--;
// Handle the special case of decimal point if floating point enabled
if (pKeyPad->sConfig.bFloatEn) {
if (pKeyPad->acValStr[pKeyPad->nValStrPos] == KEYPAD_DISP_DECIMAL_PT) {
//GSLC_DEBUG2_PRINT("KeyPad Key=BS across decimal\n", "");
pKeyPad->bValDecimalPt = false;
}
}
if (pKeyPad->nValStrPos == 0) {
memset(pKeyPad->acValStr, 0, XKEYPAD_VAL_LEN);
pKeyPad->bValPositive = true;
}
pKeyPad->acValStr[pKeyPad->nValStrPos] = '\0';
bValRedraw = true;
break;
case KEYPAD_ID_PERIOD:
case KEYPAD_ID_SPACE:
default:
// Normal character
//GSLC_DEBUG2_PRINT("KeyPad Key=Digit\n", "");
// For basic buttons, we need to fetch the keypad string index
nKeyInd = (*pfuncLookup)(pGui, nSubElemId);
bValRedraw |= ElemXKeyPadAddChar(pGui, pKeyPad, pKeyPad->pacKeys[nKeyInd][0]);
break;
} // switch
// Do we need to redraw the text field?
if (bValRedraw) {
pElemRef = gslc_CollectFindElemById(pGui, &pKeyPad->sCollect, KEYPAD_ID_TXT);
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_INC);
// Issue callback with Update status
if (pfuncXInput != NULL) {
(*pfuncXInput)(pvGui, (void*)(pElemRefParent), XKEYPAD_CB_STATE_UPDATE, (void*)(&sKeyPadData));
}
} // bValRedraw
} // eTouch
return true;
#endif // !DRV_TOUCH_NONE
}
bool gslc_ElemXKeyPadTouch(void* pvGui, void* pvElemRef, gslc_teTouch eTouch, int16_t nRelX, int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXKeyPadTouch";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL, FUNCSTR);
return false;
}
gslc_tsGui* pGui = NULL;
gslc_tsElemRef* pElemRef = NULL;
gslc_tsElem* pElem = NULL;
gslc_tsXKeyPad* pKeyPad = NULL;
// Typecast the parameters to match the GUI
pGui = (gslc_tsGui*)(pvGui);
pElemRef = (gslc_tsElemRef*)(pvElemRef);
pElem = gslc_GetElemFromRef(pGui, pElemRef);
pKeyPad = (gslc_tsXKeyPad*)(pElem->pXData);
// Get Collection
gslc_tsCollect* pCollect = &pKeyPad->sCollect;
// Cascade the touch event to the sub-element collection
return gslc_CollectTouchCompound(pvGui, pvElemRef, eTouch, nRelX, nRelY, pCollect);
#endif // !DRV_TOUCH_NONE
}
void gslc_ElemXKeyPadCfgSetButtonSz(gslc_tsXKeyPadCfg* pConfig, int8_t nButtonSzW, int8_t nButtonSzH)
{
pConfig->nButtonSzW = nButtonSzW;
pConfig->nButtonSzH = nButtonSzH;
}
void gslc_ElemXKeyPadCfgSetFloatEn(gslc_tsXKeyPadCfg* pConfig, bool bEn)
{
pConfig->bFloatEn = bEn;
}
void gslc_ElemXKeyPadCfgSetSignEn(gslc_tsXKeyPadCfg* pConfig, bool bEn)
{
pConfig->bSignEn = bEn;
}
void gslc_ElemXKeyPadCfgSetRoundEn(gslc_tsXKeyPadCfg* pConfig, bool bEn)
{
pConfig->bRoundEn = bEn;
}
// FIXME: Runtime API not fully functional yet - Do not use
void gslc_ElemXKeyPadSetFloatEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->sConfig.bFloatEn = bEn;
// Mark as needing full redraw as button visibility may have changed
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// FIXME: Runtime API not fully functional yet - Do not use
void gslc_ElemXKeyPadSetSignEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->sConfig.bSignEn = bEn;
// Mark as needing full redraw as button visibility may have changed
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXKeyPadInputAsk(gslc_tsGui* pGui, gslc_tsElemRef* pKeyPadRef, int16_t nPgPopup, gslc_tsElemRef* pTxtRef)
{
// Fetch the Element ID from the text element
gslc_tsElem* pTxtElem = gslc_GetElemFromRefD(pGui,pTxtRef,__LINE__);
if (!pTxtElem) return;
int16_t nTxtElemId = pTxtElem->nId;
// Associate the keypad with the text field
gslc_ElemXKeyPadTargetIdSet(pGui, pKeyPadRef, nTxtElemId);
// Show a popup box for the keypad
gslc_PopupShow(pGui, nPgPopup, true);
// Preload text field with current value
gslc_ElemXKeyPadValSet(pGui, pKeyPadRef, gslc_ElemGetTxtStr(pGui, pTxtRef));
}
char* gslc_ElemXKeyPadInputGet(gslc_tsGui* pGui, gslc_tsElemRef* pTxtRef, void* pvCbData)
{
char* pStr = NULL;
// Fetch the current value of the keypad popup
pStr = gslc_ElemXKeyPadDataValGet(pGui, pvCbData);
// Update the linked text item
gslc_ElemSetTxtStr(pGui, pTxtRef, gslc_ElemXKeyPadDataValGet(pGui, pvCbData));
// Hide the popup
gslc_PopupHide(pGui);
// Return the text string in case the user wants it
return pStr;
}
#endif // GSLC_FEATURE_COMPOUND
// ============================================================================