715 lines
24 KiB
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
|
||
|
|
||
|
|
||
|
// ============================================================================
|