664 lines
22 KiB
C
664 lines
22 KiB
C
// =======================================================================
|
|
// GUIslice library (extensions)
|
|
// - 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 XTextbox.c
|
|
|
|
|
|
|
|
// GUIslice library
|
|
#include "GUIslice.h"
|
|
#include "GUIslice_drv.h"
|
|
|
|
#include "XTextbox.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#if (GSLC_USE_PROGMEM)
|
|
#include <avr/pgmspace.h>
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// 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
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
// ============================================================================
|
|
|
|
gslc_tsElemRef* gslc_ElemXTextboxCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
|
|
gslc_tsXTextbox* pXData,gslc_tsRect rElem,int16_t nFontId,char* pBuf,
|
|
uint16_t nBufRows,uint16_t nBufCols)
|
|
{
|
|
if ((pGui == NULL) || (pXData == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxCreate";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return NULL;
|
|
}
|
|
gslc_tsElem sElem;
|
|
gslc_tsElemRef* pElemRef = NULL;
|
|
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_TEXTBOX,rElem,NULL,0,nFontId);
|
|
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;
|
|
// Default group assignment. Can override later with ElemSetGroup()
|
|
sElem.nGroup = GSLC_GROUP_ID_NONE;
|
|
|
|
// Define other extended data
|
|
pXData->pBuf = pBuf;
|
|
pXData->nMarginX = 5;
|
|
pXData->nMarginY = 5;
|
|
pXData->bWrapEn = true;
|
|
pXData->nCurPosX = 0;
|
|
pXData->nCurPosY = 0;
|
|
|
|
pXData->nBufRows = nBufRows;
|
|
pXData->nBufCols = nBufCols;
|
|
pXData->nBufPosX = 0;
|
|
pXData->nBufPosY = 0;
|
|
pXData->nWndRowStart = 0;
|
|
|
|
pXData->nRedrawRow = XTEXTBOX_REDRAW_ALL;
|
|
|
|
// Clear the buffer
|
|
memset(pBuf,0,nBufRows*nBufCols*sizeof(char));
|
|
|
|
// Precalculate certain parameters
|
|
// Determine the maximum size of a character
|
|
// - For now, assume we are using a monospaced font and derive
|
|
// text pixel coords from the size of a worst-case character.
|
|
int16_t nChOffsetX, nChOffsetY, nChOffsetTmp;
|
|
uint16_t nChSzW,nChSzH,nChSzTmp;
|
|
|
|
// Fetch X & Y sizing and offsets independently, based on characters that
|
|
// are likely to maximize the ascenders / descenders / width attributes
|
|
char acMonoH[3] = "p$";
|
|
char acMonoW[2] = "W";
|
|
|
|
gslc_DrvGetTxtSize(pGui, sElem.pTxtFont, (char*)&acMonoH, sElem.eTxtFlags, &nChOffsetTmp, &nChOffsetY, &nChSzTmp, &nChSzH);
|
|
gslc_DrvGetTxtSize(pGui, sElem.pTxtFont, (char*)&acMonoW, sElem.eTxtFlags, &nChOffsetX, &nChOffsetTmp, &nChSzW, &nChSzTmp);
|
|
|
|
pXData->nWndCols = (rElem.w - (2*pXData->nMarginX)) / nChSzW;
|
|
pXData->nWndRows = (rElem.h - (2*pXData->nMarginY)) / nChSzH;
|
|
|
|
// Adjust margin to correct for character offsets
|
|
pXData->nMarginX -= nChOffsetX;
|
|
pXData->nMarginY -= nChOffsetY;
|
|
|
|
pXData->nChSizeX = nChSzW;
|
|
pXData->nChSizeY = nChSzH;
|
|
|
|
// Determine if scrollbar should be enabled
|
|
if (pXData->nWndRows >= pXData->nBufRows) {
|
|
// Disable scrollbar as the window is larger
|
|
// than the number of rows in the buffer
|
|
pXData->bScrollEn = false;
|
|
pXData->nScrollPos = 0;
|
|
} else {
|
|
// Scrollbar is enabled
|
|
pXData->bScrollEn = true;
|
|
pXData->nScrollPos = 0;
|
|
}
|
|
|
|
sElem.pXData = (void*)(pXData);
|
|
|
|
// Specify the custom drawing callback
|
|
sElem.pfuncXDraw = &gslc_ElemXTextboxDraw;
|
|
sElem.pfuncXTouch = NULL;
|
|
sElem.colElemFill = GSLC_COL_BLACK;
|
|
sElem.colElemFillGlow = GSLC_COL_BLACK;
|
|
sElem.colElemFrame = GSLC_COL_GRAY;
|
|
sElem.colElemFrameGlow = GSLC_COL_WHITE;
|
|
if (nPage != GSLC_PAGE_NONE) {
|
|
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
|
|
return pElemRef;
|
|
#if (GSLC_FEATURE_COMPOUND)
|
|
} else {
|
|
// Save as temporary element
|
|
pGui->sElemTmp = sElem;
|
|
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
|
|
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
|
|
return &(pGui->sElemRefTmp);
|
|
#endif
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void gslc_ElemXTextboxReset(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
|
|
{
|
|
if (pElemRef == NULL) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxReset";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return;
|
|
}
|
|
gslc_tsXTextbox* pBox;
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
|
|
// Reset the positional state
|
|
pBox->nCurPosX = 0;
|
|
pBox->nCurPosY = 0;
|
|
pBox->nBufPosX = 0;
|
|
pBox->nBufPosY = 0;
|
|
pBox->nWndRowStart = 0;
|
|
|
|
// Clear the buffer
|
|
memset(pBox->pBuf,0,pBox->nBufRows*pBox->nBufCols*sizeof(char));
|
|
|
|
// Set the redraw flag
|
|
// - Only need incremental redraw
|
|
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL; // All-row update
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
|
|
}
|
|
|
|
// Advance the buffer writer to the next line
|
|
// The window is also shifted if we are eating the first row
|
|
void gslc_ElemXTextboxLineWrAdv(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
|
|
{
|
|
if (pElemRef == NULL) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxLineWrAdv";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return;
|
|
}
|
|
gslc_tsXTextbox* pBox;
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
|
|
pBox->nBufPosX = 0;
|
|
pBox->nBufPosY++;
|
|
|
|
// Wrap the pointers around end of buffer
|
|
pBox->nBufPosY = pBox->nBufPosY % pBox->nBufRows;
|
|
|
|
// Did the buffer write pointer start to encroach upon
|
|
// the visible window region? If so, shift the window
|
|
if (pBox->nBufPosY == pBox->nWndRowStart) {
|
|
// Advance the window (with wrap if needed)
|
|
pBox->nWndRowStart = (pBox->nWndRowStart + 1) % pBox->nBufRows;
|
|
// Ensure all rows get redrawn
|
|
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL;
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
|
|
}
|
|
}
|
|
|
|
void gslc_ElemXTextboxScrollSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,uint8_t nScrollPos,uint8_t nScrollMax)
|
|
{
|
|
|
|
gslc_tsXTextbox* pBox;
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
|
|
// Ensure scrollbar is enabled
|
|
if (!pBox->bScrollEn) {
|
|
// Scrollbar is disabled, so ignore
|
|
return;
|
|
}
|
|
|
|
// Check for out-of-range values
|
|
if (nScrollPos > nScrollMax) {
|
|
GSLC_DEBUG2_PRINT("ERROR: ElemXTextboxScrollSet() pos [%u] exceeds max [%u]\n", nScrollPos,nScrollMax);
|
|
// Force the position to the max
|
|
nScrollPos = nScrollMax;
|
|
}
|
|
|
|
// Assign proportional value based on visible window region
|
|
uint16_t nScrollPosOld = pBox->nScrollPos;
|
|
pBox->nScrollPos = nScrollPos * (pBox->nBufRows - pBox->nWndRows) / nScrollMax;
|
|
|
|
// Set the redraw flag
|
|
// - Only need incremental redraw
|
|
// - Only redraw if changed actual scroll row
|
|
if (pBox->nScrollPos != nScrollPosOld) {
|
|
// Ensure all rows get redrawn
|
|
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL;
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
|
|
}
|
|
}
|
|
|
|
// Write a character to the buffer
|
|
// - Advance the write ptr, wrap if needed
|
|
// - If encroach upon buffer read ptr, then drop the oldest line from the buffer
|
|
// NOTE: This should not be called with newline char!
|
|
void gslc_ElemXTextboxBufAdd(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,unsigned char chNew,bool bAdvance)
|
|
{
|
|
gslc_tsXTextbox* pBox;
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
|
|
// Ensure that we haven't gone past end of line
|
|
// - Note that we have to leave one extra byte for the line terminator (NULL)
|
|
if ((pBox->nBufPosX+1) >= pBox->nBufCols) {
|
|
if (pBox->bWrapEn) {
|
|
// Perform line wrap
|
|
// - Force a null at the end of the current line first
|
|
pBox->pBuf[pBox->nBufPosY * pBox->nBufCols + (pBox->nBufCols-1)] = 0;
|
|
gslc_ElemXTextboxLineWrAdv(pGui,pElemRef);
|
|
} else {
|
|
// Ignore the write
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint16_t nBufPos = pBox->nBufPosY * pBox->nBufCols + pBox->nBufPosX;
|
|
|
|
// Add the character
|
|
pBox->pBuf[nBufPos] = chNew;
|
|
|
|
// Only trigger redraw if we have output a printable character
|
|
// to the display buffer. For simplicity, just check for
|
|
// null for now.
|
|
// TODO: Consider checking isprint()
|
|
if (chNew == 0) {
|
|
// Don't update redraw on non-printing characters
|
|
} else {
|
|
|
|
// Mark this specific row as needing redraw
|
|
if (pBox->nRedrawRow == XTEXTBOX_REDRAW_NONE) {
|
|
// No redraw was pending, so mark single row pending
|
|
pBox->nRedrawRow = pBox->nBufPosY;
|
|
}
|
|
else if (pBox->nRedrawRow == XTEXTBOX_REDRAW_ALL) {
|
|
// All-row redraw was pending, so no change
|
|
}
|
|
else {
|
|
// Single row redraw was pending
|
|
if (pBox->nRedrawRow != pBox->nBufPosY) {
|
|
// But the pending row differs from the current row,
|
|
// so promote redraw to all lines
|
|
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL;
|
|
}
|
|
else {
|
|
// Pending row is the same, so no change
|
|
}
|
|
}
|
|
} // chNew
|
|
|
|
// Optionally advance the pointer
|
|
// - The only time we don't advance is if we added NULL
|
|
// but note that in some special commands there may be
|
|
// zero values added, so we still need to advance these
|
|
if (bAdvance) {
|
|
pBox->nBufPosX++;
|
|
}
|
|
|
|
}
|
|
|
|
void gslc_ElemXTextboxColSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor nCol)
|
|
{
|
|
#if (GSLC_FEATURE_XTEXTBOX_EMBED == 0)
|
|
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXTextboxColSet() not enabled. Requires GSLC_FEATURE_XTEXTBOX_EMBED=1 %s\n","");
|
|
return;
|
|
#else
|
|
gslc_tsXTextbox* pBox = NULL;
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
|
|
// Ensure that there are enough free columns in current
|
|
// buffer row to accommodate the color code (4 bytes)
|
|
if (pBox->nBufPosX +4 >= pBox->nBufCols) {
|
|
// Not enough space for the code, so ignore it
|
|
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXTextboxColSet() not enough cols [Pos=%u Cols=%u]\n",pBox->nBufPosX,pBox->nBufCols);
|
|
return;
|
|
}
|
|
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,GSLC_XTEXTBOX_CODE_COL_SET,true);
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,nCol.r,true);
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,nCol.g,true);
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,nCol.b,true);
|
|
#endif
|
|
}
|
|
|
|
void gslc_ElemXTextboxColReset(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
|
|
{
|
|
#if (GSLC_FEATURE_XTEXTBOX_EMBED == 0)
|
|
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXTextboxColReset() not enabled. Requires GSLC_FEATURE_XTEXTBOX_EMBED=1 %s\n","");
|
|
return;
|
|
#else
|
|
gslc_tsXTextbox* pBox = NULL;
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
(void)pBox; // Unused
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,GSLC_XTEXTBOX_CODE_COL_RESET,true);
|
|
#endif
|
|
}
|
|
|
|
void gslc_ElemXTextboxWrapSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bWrapEn)
|
|
{
|
|
gslc_tsXTextbox* pBox = NULL;
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
pBox->bWrapEn = bWrapEn;
|
|
}
|
|
|
|
|
|
void gslc_ElemXTextboxAdd(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,char* pTxt)
|
|
{
|
|
|
|
// Warn the user about mode compatibility
|
|
#if (GSLC_FEATURE_XTEXTBOX_EMBED)
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
//gslc_tsXTextbox* pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
static bool bWarned = false; // Warn only once
|
|
bool bEncUtf8 = ((pElem->eTxtFlags & GSLC_TXT_ENC) == GSLC_TXT_ENC_UTF8);
|
|
if ((!bWarned) && (bEncUtf8)) {
|
|
// Continue to render the text, but issue warning to the user
|
|
GSLC_DEBUG2_PRINT("WARNING: ElemXTextboxAdd(%s) UTF-8 encoding not supported in GSLC_FEATURE_XTEXTBOX_EMBED=1 mode\n","");
|
|
bWarned = true;
|
|
}
|
|
#endif
|
|
|
|
// Add null-terminated string to the bottom of the buffer
|
|
// If the string exceeds the buffer length then it will wrap
|
|
// back to the beginning.
|
|
// TODO: Ensure that buffer wrap doesn't encroach upon visible region!
|
|
// TODO: Assert (pBox)
|
|
bool bDone = false;
|
|
uint16_t nTxtPos = 0;
|
|
unsigned char chNext;
|
|
|
|
if (pTxt == NULL) { bDone = true; }
|
|
while (!bDone) {
|
|
chNext = pTxt[nTxtPos];
|
|
nTxtPos++;
|
|
if (chNext == 0) {
|
|
// Reached terminator character
|
|
// Add terminator to buffer but don't advance write pointer
|
|
// since we want next write to overwrite this
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,0,false);
|
|
|
|
bDone = true;
|
|
continue;
|
|
}
|
|
|
|
// FIXME: It is possible that the following check may no longer be
|
|
// appropriate when using UTF-8 encoding mode.
|
|
if (chNext == '\n') {
|
|
// Terminate the line
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,0,false);
|
|
// Advance the writer by one line
|
|
gslc_ElemXTextboxLineWrAdv(pGui,pElemRef);
|
|
} else {
|
|
// TODO: Check to see if we are in mask/truncate state
|
|
// Note that this routine also handles line wrap
|
|
gslc_ElemXTextboxBufAdd(pGui,pElemRef,chNext,true);
|
|
}
|
|
}
|
|
|
|
// Set the redraw flag
|
|
// - Only need incremental redraw
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
|
|
}
|
|
|
|
bool gslc_ElemXTextboxDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
|
|
{
|
|
if ((pvGui == NULL) || (pvElemRef == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxDraw";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return false;
|
|
}
|
|
// Typecast the parameters to match the GUI and element types
|
|
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
|
|
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
gslc_tsColor colBg = GSLC_COL_BLACK;
|
|
// Fetch the element's extended data structure
|
|
gslc_tsXTextbox* pBox;
|
|
pBox = (gslc_tsXTextbox*)(pElem->pXData);
|
|
if (pBox == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: ElemXTextboxDraw(%s) pXData is NULL\n","");
|
|
return false;
|
|
}
|
|
|
|
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
|
|
bool bFrameEn = (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN);
|
|
|
|
// Draw the frame
|
|
if (eRedraw == GSLC_REDRAW_FULL) {
|
|
if (bFrameEn) {
|
|
gslc_DrawFrameRect(pGui,pElem->rElem,pElem->colElemFrame);
|
|
}
|
|
}
|
|
|
|
// Clear the background (inset from frame)
|
|
// - Only do this if we need to redraw all rows
|
|
if ((eRedraw == GSLC_REDRAW_FULL) || (pBox->nRedrawRow == XTEXTBOX_REDRAW_ALL)) {
|
|
gslc_tsRect rInner = gslc_ExpandRect(pElem->rElem, -1, -1);
|
|
colBg = (bGlow) ? pElem->colElemFillGlow : pElem->colElemFill;
|
|
gslc_DrawFillRect(pGui, rInner, colBg);
|
|
}
|
|
|
|
uint16_t nBufPos = 0;
|
|
|
|
uint16_t nTxtPixX;
|
|
uint16_t nTxtPixY;
|
|
gslc_tsColor colTxt;
|
|
//bool bEncUtf8;
|
|
bool bRedrawLine;
|
|
|
|
// Determine what encoding method is used for text
|
|
// Not used at the moment
|
|
//bEncUtf8 = ((pElem->eTxtFlags & GSLC_TXT_ENC) == GSLC_TXT_ENC_UTF8);
|
|
|
|
// Initialize color state
|
|
colTxt = pElem->colElemText;
|
|
|
|
// Calculate the starting row for the window
|
|
uint16_t nWndRowStartScr = pBox->nWndRowStart;
|
|
|
|
// Only correct for scrollbar position if enabled
|
|
if (pBox->bScrollEn) {
|
|
nWndRowStartScr = (pBox->nWndRowStart + pBox->nScrollPos) % pBox->nBufRows;
|
|
}
|
|
|
|
#if (GSLC_FEATURE_XTEXTBOX_EMBED == 0)
|
|
|
|
// Normal mode support (no embedded text color)
|
|
// - This mode is much faster and is able to support UTF-8 text encoding
|
|
|
|
uint8_t nCurY = 0;
|
|
|
|
uint8_t nOutRow = 0;
|
|
uint8_t nMaxRow = 0;
|
|
|
|
nMaxRow = (pBox->nBufRows < pBox->nWndRows)? pBox->nBufRows : pBox->nWndRows;
|
|
for (nOutRow=0;nOutRow<nMaxRow;nOutRow++) {
|
|
|
|
bRedrawLine = true; // Default to drawing the row
|
|
|
|
// Calculate row offset after accounting for buffer wrap
|
|
// and current window starting offset
|
|
uint16_t nRowCur = nWndRowStartScr + nOutRow;
|
|
nRowCur = nRowCur % pBox->nBufRows;
|
|
|
|
// If we are doing incremental redraw and only a single
|
|
// row has been marked as requiring redraw, then skip
|
|
// all other rows
|
|
if (eRedraw == GSLC_REDRAW_INC) {
|
|
if ((pBox->nRedrawRow >= 0) && (pBox->nRedrawRow != nRowCur)) {
|
|
// Single-row redraw, but we are not on that row, so skip
|
|
bRedrawLine = false;
|
|
}
|
|
}
|
|
|
|
// NOTE: At the start of buffer fill where we have
|
|
// only written a couple rows, we don't stop reading
|
|
// across all of the rows. We are dependent upon
|
|
// the reset to initialize all rows with NULL terminator
|
|
// so that we don't show garbage.
|
|
|
|
if (bRedrawLine) {
|
|
nBufPos = nRowCur * pBox->nBufCols;
|
|
|
|
nTxtPixX = pElem->rElem.x + pBox->nMarginX + 0 * pBox->nChSizeX;
|
|
nTxtPixY = pElem->rElem.y + pBox->nMarginY + nCurY * pBox->nChSizeY;
|
|
#if (DRV_OVERRIDE_TXT_ALIGN)
|
|
gslc_DrvDrawTxtAlign(pGui,nTxtPixX,nTxtPixY,nTxtPixX,nTxtPixY,GSLC_ALIGN_TOP_LEFT,pElem->pTxtFont,
|
|
(char*)&(pBox->pBuf[nBufPos]),pElem->eTxtFlags,colTxt,colBg);
|
|
#else
|
|
gslc_DrvDrawTxt(pGui, nTxtPixX, nTxtPixY, pElem->pTxtFont, (char*)&(pBox->pBuf[nBufPos]), pElem->eTxtFlags, colTxt, colBg);
|
|
#endif
|
|
}
|
|
|
|
nCurY++;
|
|
} // nOutRow
|
|
|
|
#else
|
|
|
|
// Embedded color mode support
|
|
// - This mode supports inline changing of text color
|
|
// - However, it does not support UTF-8 character encoding
|
|
// - It is also slower since rendering is per-character
|
|
|
|
enum {TBOX_NORM, TBOX_COL_SET};
|
|
int16_t eTBoxState = TBOX_NORM;
|
|
uint16_t nTBoxStateCnt = 0;
|
|
|
|
unsigned char chNext;
|
|
uint8_t nCurX = 0;
|
|
uint8_t nCurY = 0;
|
|
|
|
uint8_t nOutRow = 0;
|
|
uint8_t nOutCol = 0;
|
|
uint8_t nMaxCol = 0;
|
|
uint8_t nMaxRow = 0;
|
|
bool bRowDone = false;
|
|
nMaxCol = (pBox->nBufCols < pBox->nWndCols)? pBox->nBufCols : pBox->nWndCols;
|
|
nMaxRow = (pBox->nBufRows < pBox->nWndRows)? pBox->nBufRows : pBox->nWndRows;
|
|
for (nOutRow=0;nOutRow<nMaxRow;nOutRow++) {
|
|
|
|
bRedrawLine = true; // Default to drawing the row
|
|
|
|
// Calculate row offset after accounting for buffer wrap
|
|
// and current window starting offset
|
|
uint16_t nRowCur = nWndRowStartScr + nOutRow;
|
|
nRowCur = nRowCur % pBox->nBufRows;
|
|
|
|
// If we are doing incremental redraw and only a single
|
|
// row has been marked as requiring redraw, then skip
|
|
// all other rows
|
|
if (eRedraw == GSLC_REDRAW_INC) {
|
|
if ((pBox->nRedrawRow >= 0) && (pBox->nRedrawRow != nRowCur)) {
|
|
// Single-row redraw, but we are not on that row, so skip
|
|
bRedrawLine = false;
|
|
}
|
|
}
|
|
|
|
bRowDone = false;
|
|
nCurX = 0;
|
|
for (nOutCol=0;(!bRowDone)&&(bRedrawLine)&&(nOutCol<nMaxCol);nOutCol++) {
|
|
|
|
|
|
|
|
|
|
|
|
// NOTE: At the start of buffer fill where we have
|
|
// only written a couple rows, we don't stop reading
|
|
// across all of the rows. We are dependent upon
|
|
// the reset to initialize all rows with NULL terminator
|
|
// so that we don't show garbage.
|
|
|
|
nBufPos = nRowCur * pBox->nBufCols + nOutCol;
|
|
chNext = pBox->pBuf[nBufPos];
|
|
|
|
if (eTBoxState == TBOX_NORM) {
|
|
if (chNext == 0) {
|
|
// Reached early terminator
|
|
bRowDone = true;
|
|
continue;
|
|
} else if (chNext == GSLC_XTEXTBOX_CODE_COL_SET) {
|
|
// Set color (enter FSM)
|
|
eTBoxState = TBOX_COL_SET;
|
|
nTBoxStateCnt = 0;
|
|
} else if (chNext == GSLC_XTEXTBOX_CODE_COL_RESET) {
|
|
// Reset color
|
|
colTxt = pElem->colElemText;
|
|
} else {
|
|
|
|
// Render the character
|
|
// TODO: Optimize by coalescing all characters in row before calling DrvDrawTxt
|
|
// - Note that this would make it harder to change aspects (such as color)
|
|
// in mid-line.
|
|
char acChToDraw[2] = "";
|
|
acChToDraw[0] = chNext;
|
|
acChToDraw[1] = 0;
|
|
nTxtPixX = pElem->rElem.x + pBox->nMarginX + nCurX * pBox->nChSizeX;
|
|
nTxtPixY = pElem->rElem.y + pBox->nMarginY + nCurY * pBox->nChSizeY;
|
|
#if (DRV_OVERRIDE_TXT_ALIGN)
|
|
gslc_DrvDrawTxtAlign(pGui,nTxtPixX,nTxtPixY,nTxtPixX,nTxtPixY,GSLC_ALIGN_TOP_LEFT,pElem->pTxtFont,
|
|
(char*)&acChToDraw,pElem->eTxtFlags,colTxt,colBg);
|
|
#else
|
|
gslc_DrvDrawTxt(pGui,nTxtPixX,nTxtPixY,pElem->pTxtFont,(char*)&acChToDraw,pElem->eTxtFlags,colTxt,colBg);
|
|
#endif
|
|
nCurX++;
|
|
|
|
}
|
|
|
|
} else if (eTBoxState == TBOX_COL_SET) {
|
|
nTBoxStateCnt++;
|
|
if (nTBoxStateCnt == 1) { colTxt.r = chNext; }
|
|
else if (nTBoxStateCnt == 2) { colTxt.g = chNext; }
|
|
else if (nTBoxStateCnt == 3) {
|
|
colTxt.b = chNext;
|
|
eTBoxState = TBOX_NORM;
|
|
}
|
|
} // eTBoxState
|
|
|
|
} // nOutCol
|
|
nCurY++;
|
|
} // nOutRow
|
|
|
|
#endif // GSLC_FEATURE_XTEXTBOX_EMBED
|
|
|
|
// Clear the redraw flag
|
|
pBox->nRedrawRow = XTEXTBOX_REDRAW_NONE;
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
|
|
|
|
// Mark page as needing flip
|
|
gslc_PageFlipSet(pGui,true);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ============================================================================
|