349 lines
12 KiB
C
349 lines
12 KiB
C
// =======================================================================
|
|
// GUIslice library extension: XGlowball
|
|
// - 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 XGlowball.c
|
|
|
|
|
|
|
|
// GUIslice library
|
|
#include "GUIslice.h"
|
|
#include "GUIslice_drv.h"
|
|
|
|
#include "XGlowball.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
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
// ============================================================================
|
|
// Extended Element: XGlowball
|
|
// ============================================================================
|
|
|
|
// Create a XGlowball element and add it to the GUI element list
|
|
// - Defines default styling for the element
|
|
// - Defines callback for redraw and touch
|
|
gslc_tsElemRef* gslc_ElemXGlowballCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
|
|
gslc_tsXGlowball* pXData, int16_t nMidX, int16_t nMidY, gslc_tsXGlowballRing* pRings, uint8_t nNumRings)
|
|
{
|
|
if ((pGui == NULL) || (pXData == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballCreate";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return NULL;
|
|
}
|
|
gslc_tsElem sElem;
|
|
gslc_tsElemRef* pElemRef = NULL;
|
|
|
|
// Calculate the actual element dimensions based on the ring defintion
|
|
// - Note that we use the outer radius of the last ring
|
|
int16_t nRadMax;
|
|
gslc_tsRect rElem;
|
|
nRadMax = pRings[nNumRings - 1].nRad2;
|
|
rElem.w = 2 * nRadMax;
|
|
rElem.h = 2 * nRadMax;
|
|
rElem.x = nMidX - nRadMax;
|
|
rElem.y = nMidY - nRadMax;
|
|
|
|
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_GLOW,rElem,NULL,0,GSLC_FONT_NONE);
|
|
sElem.colElemFill = GSLC_COL_BLACK;
|
|
sElem.colElemFillGlow = GSLC_COL_BLACK;
|
|
sElem.colElemFrame = GSLC_COL_GRAY;
|
|
sElem.colElemFrameGlow = GSLC_COL_GRAY;
|
|
sElem.colElemText = GSLC_COL_WHITE;
|
|
sElem.colElemTextGlow = GSLC_COL_WHITE;
|
|
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
|
|
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
|
|
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
|
|
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
|
|
sElem.eTxtAlign = GSLC_ALIGN_MID_LEFT;
|
|
|
|
sElem.nGroup = GSLC_GROUP_ID_NONE;
|
|
|
|
// Initialize any pxData members with default parameters
|
|
pXData->nVal = 0;
|
|
pXData->nQuality = 72;
|
|
pXData->nAngStart = 0;
|
|
pXData->nAngEnd = 360;
|
|
pXData->pRings = pRings;
|
|
pXData->nNumRings = nNumRings;
|
|
pXData->nMidX = nMidX;
|
|
pXData->nMidY = nMidY;
|
|
pXData->colBg = GSLC_COL_BLACK;
|
|
pXData->nValLast = 0;
|
|
|
|
|
|
sElem.pXData = (void*)(pXData);
|
|
// Specify the custom drawing callback
|
|
sElem.pfuncXDraw = &gslc_ElemXGlowballDraw;
|
|
// Specify the custom touch tracking callback
|
|
sElem.pfuncXTouch = NULL;
|
|
|
|
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 drawXGlowballArc(gslc_tsGui* pGui, gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nRad1, int16_t nRad2, gslc_tsColor cArc, uint16_t nAngStart, uint16_t nAngEnd)
|
|
{
|
|
gslc_tsPt anPts[4];
|
|
// TODO: Cleanup
|
|
int16_t nStep64 = 64*360 / pGlowball->nQuality;
|
|
int16_t nAng64;
|
|
int16_t nX, nY;
|
|
int16_t nSegStart, nSegEnd;
|
|
nSegStart = nAngStart * pGlowball->nQuality / 360;
|
|
nSegEnd = nAngEnd * pGlowball->nQuality / 360;
|
|
|
|
for (uint16_t nSegInd = nSegStart; nSegInd < nSegEnd; nSegInd++) {
|
|
nAng64 = nSegInd * nStep64;
|
|
nAng64 = nAng64 % (360 * 64);
|
|
|
|
gslc_PolarToXY(nRad1, nAng64, &nX, &nY);
|
|
anPts[0] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
|
|
gslc_PolarToXY(nRad2, nAng64, &nX, &nY);
|
|
anPts[1] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
|
|
gslc_PolarToXY(nRad2, nAng64 + nStep64, &nX, &nY);
|
|
anPts[2] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
|
|
gslc_PolarToXY(nRad1, nAng64 + nStep64, &nX, &nY);
|
|
anPts[3] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
|
|
|
|
gslc_DrawFillQuad(pGui, anPts, cArc);
|
|
}
|
|
}
|
|
|
|
|
|
void drawXGlowballRing(gslc_tsGui* pGui, gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nVal, uint16_t nAngStart, uint16_t nAngEnd, bool bErase)
|
|
{
|
|
int16_t nRad1, nRad2;
|
|
gslc_tsColor cCol;
|
|
|
|
if ((nVal <= 0) || (nVal > pGlowball->nNumRings)) {
|
|
return;
|
|
}
|
|
|
|
nRad1 = pGlowball->pRings[nVal - 1].nRad1;
|
|
nRad2 = pGlowball->pRings[nVal - 1].nRad2;
|
|
cCol = (bErase) ? pGlowball->colBg : pGlowball->pRings[nVal - 1].cCol;
|
|
|
|
drawXGlowballArc(pGui, pGlowball, nMidX, nMidY, nRad1, nRad2, cCol, nAngStart, nAngEnd);
|
|
}
|
|
|
|
// nAngStart & nAngEnd define the range (in degrees) for the XGlowball arcs
|
|
// - Angles are measured with 0 degrees at the top, incrementing clockwise
|
|
// - Note that the supported range is 0..510 degrees
|
|
// nVal represents the value to set (0.. NUM_RINGS)
|
|
// nMidX,nMidY define the center of the XGlowball
|
|
void drawXGlowball(gslc_tsGui* pGui,gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nVal, uint16_t nAngStart, uint16_t nAngEnd)
|
|
{
|
|
int16_t nValLast = pGlowball->nValLast;
|
|
int8_t nRingInd;
|
|
|
|
if (nVal > nValLast) {
|
|
for (nRingInd = nValLast + 1; nRingInd <= nVal; nRingInd++) {
|
|
drawXGlowballRing(pGui, pGlowball, nMidX, nMidY, nRingInd, nAngStart, nAngEnd, false);
|
|
}
|
|
} else {
|
|
for (nRingInd = nValLast; nRingInd > nVal; nRingInd--) {
|
|
drawXGlowballRing(pGui, pGlowball, nMidX, nMidY, nRingInd, nAngStart, nAngEnd, true);
|
|
}
|
|
}
|
|
pGlowball->nValLast = nVal;
|
|
}
|
|
|
|
void gslc_ElemXGlowballSetVal(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nVal)
|
|
{
|
|
if ((pGui == NULL) || (pElemRef == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetVal";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return;
|
|
}
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
|
|
|
|
// Update the value
|
|
pGlowball->nVal = nVal;
|
|
|
|
// Mark for redraw
|
|
// - Only need incremental redraw
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
|
|
}
|
|
|
|
void gslc_ElemXGlowballSetAngles(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nAngStart, int16_t nAngEnd)
|
|
{
|
|
if ((pGui == NULL) || (pElemRef == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetAngles";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return;
|
|
}
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
|
|
|
|
// Update the angular ranges
|
|
pGlowball->nAngStart = nAngStart;
|
|
pGlowball->nAngEnd = nAngEnd;
|
|
|
|
// Mark for redraw
|
|
// - Force full redraw
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
|
|
}
|
|
|
|
void gslc_ElemXGlowballSetQuality(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nQuality)
|
|
{
|
|
if ((pGui == NULL) || (pElemRef == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetQuality";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return;
|
|
}
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
|
|
|
|
// Update the rendering quality setting
|
|
pGlowball->nQuality = nQuality;
|
|
|
|
// Mark for redraw
|
|
// - Force full redraw
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
|
|
}
|
|
|
|
void gslc_ElemXGlowballSetColorBack(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colBg)
|
|
{
|
|
if ((pGui == NULL) || (pElemRef == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetColorBack";
|
|
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
|
|
return;
|
|
}
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
|
|
|
|
// Update the background color
|
|
pGlowball->colBg = colBg;
|
|
|
|
// Mark for redraw
|
|
// - Force full redraw
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
|
|
}
|
|
|
|
// Redraw the element
|
|
// - Note that this redraw is for the entire element rect region
|
|
// - The Draw function parameters use void pointers to allow for
|
|
// simpler callback function definition & scalability.
|
|
bool gslc_ElemXGlowballDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
|
|
{
|
|
if ((pvGui == NULL) || (pvElemRef == NULL)) {
|
|
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballDraw";
|
|
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);
|
|
|
|
// Fetch the element's extended data structure
|
|
gslc_tsXGlowball* pGlowball;
|
|
pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
|
|
if (pGlowball == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: ElemXGlowballDraw(%s) pXData is NULL\n","");
|
|
return false;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Init for default drawing
|
|
// --------------------------------------------------------------------------
|
|
|
|
int16_t nElemX,nElemY;
|
|
uint16_t nElemW,nElemH;
|
|
|
|
nElemX = pElem->rElem.x;
|
|
nElemY = pElem->rElem.y;
|
|
nElemW = pElem->rElem.w;
|
|
nElemH = pElem->rElem.h;
|
|
|
|
// Calculate center of XGlowball
|
|
int16_t nMidX, nMidY;
|
|
nMidX = nElemX + (nElemW / 2);
|
|
nMidY = nElemY + (nElemH / 2);
|
|
|
|
// Fetch the current value
|
|
int16_t nVal = pGlowball->nVal;
|
|
int16_t nAngStart = pGlowball->nAngStart;
|
|
int16_t nAngEnd = pGlowball->nAngEnd;
|
|
|
|
// Check to see if we need full redraw
|
|
if (eRedraw == GSLC_REDRAW_FULL) {
|
|
// Erase the region
|
|
gslc_DrawFillRect(pGui, pElem->rElem, pGlowball->colBg);
|
|
// Now force redraw from zero level to current value
|
|
pGlowball->nValLast = 0;
|
|
}
|
|
|
|
// Draw the glowball
|
|
drawXGlowball(pGui, pGlowball, nMidX, nMidY, nVal, nAngStart, nAngEnd);
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Mark the element as no longer requiring redraw
|
|
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// ============================================================================
|