summaryrefslogtreecommitdiff
path: root/glcdskin/object.c
diff options
context:
space:
mode:
Diffstat (limited to 'glcdskin/object.c')
-rw-r--r--glcdskin/object.c502
1 files changed, 483 insertions, 19 deletions
diff --git a/glcdskin/object.c b/glcdskin/object.c
index c79fa59..3a8162e 100644
--- a/glcdskin/object.c
+++ b/glcdskin/object.c
@@ -4,6 +4,8 @@
#include "cache.h"
#include "function.h"
+#include <typeinfo>
+
namespace GLCD
{
@@ -19,9 +21,11 @@ static const std::string ObjectNames[] =
"text",
"scrolltext",
"scrollbar",
+ "button",
"block",
"list",
- "item"
+ "item",
+ "condblock"
};
cSkinObject::cSkinObject(cSkinDisplay * Parent)
@@ -30,12 +34,14 @@ cSkinObject::cSkinObject(cSkinDisplay * Parent)
mType((eType) __COUNT_OBJECT__),
mPos1(0, 0),
mPos2(-1, -1),
- mColor(GLCD::clrBlack),
+ mColor(cColor(cColor::Black)),
+ mBackgroundColor(cColor(cColor::Transparent)),
mFilled(false),
mRadius(0),
mArc(0),
mDirection(0),
mAlign(taLeft),
+ mVerticalAlign(tvaTop),
mMultiline(false),
mPath(this, false),
mCurrent(this, false),
@@ -43,8 +49,23 @@ cSkinObject::cSkinObject(cSkinDisplay * Parent)
mFont(this, false),
mText(this, false),
mCondition(NULL),
+ mLastChange(0),
+ mChangeDelay(-1), // delay between two images frames: -1: not animated / don't care
+ mStoredImagePath(""),
+ mImageFrameId(0), // start with 1st frame
+ mScrollLoopMode(-1), // scroll (text) or loop (image) mode: default (-1)
+ mScrollLoopReached(false), // if scroll/loop == once: already looped once?
+ mScrollSpeed(0), // scroll speed: default (0)
+ mScrollTime(0), // scroll time interval: default (0)
+ mScrollOffset(0), // scroll offset (pixels)
+ mCurrText(""), // current text (for checks if text has changed)
+ mAltText(""), // alternative text source for text-objects
+ mAltCondition(NULL), // condition when alternative sources are used
+ mAction(""), // action (e.g. touchscreen action)
mObjects(NULL)
{
+ mColor = Parent->Skin()->Config().GetDriver()->GetForegroundColor();
+ mBackgroundColor = Parent->Skin()->Config().GetDriver()->GetBackgroundColor();
}
cSkinObject::cSkinObject(const cSkinObject & Src)
@@ -54,11 +75,13 @@ cSkinObject::cSkinObject(const cSkinObject & Src)
mPos1(Src.mPos1),
mPos2(Src.mPos2),
mColor(Src.mColor),
+ mBackgroundColor(Src.mBackgroundColor),
mFilled(Src.mFilled),
mRadius(Src.mRadius),
mArc(Src.mArc),
mDirection(Src.mDirection),
mAlign(Src.mAlign),
+ mVerticalAlign(Src.mVerticalAlign),
mMultiline(Src.mMultiline),
mPath(Src.mPath),
mCurrent(Src.mCurrent),
@@ -66,6 +89,19 @@ cSkinObject::cSkinObject(const cSkinObject & Src)
mFont(Src.mFont),
mText(Src.mText),
mCondition(Src.mCondition),
+ mLastChange(0),
+ mChangeDelay(-1),
+ mStoredImagePath(Src.mStoredImagePath),
+ mImageFrameId(0),
+ mScrollLoopMode(Src.mScrollLoopMode),
+ mScrollLoopReached(Src.mScrollLoopReached),
+ mScrollSpeed(Src.mScrollSpeed),
+ mScrollTime(Src.mScrollTime),
+ mScrollOffset(Src.mScrollOffset),
+ mCurrText(Src.mCurrText),
+ mAltText(Src.mAltText),
+ mAltCondition(Src.mAltCondition),
+ mAction(Src.mAction),
mObjects(NULL)
{
if (Src.mObjects)
@@ -90,15 +126,17 @@ bool cSkinObject::ParseType(const std::string & Text)
return false;
}
-bool cSkinObject::ParseColor(const std::string & Text)
+bool cSkinObject::ParseColor(const std::string & Text, cColor & ParamColor)
{
- if (Text == "white")
- mColor = GLCD::clrWhite;
- else if (Text == "black")
- mColor = GLCD::clrBlack;
- else
- return false;
- return true;
+ std::string text = (std::string) Text;
+ if (text[0] == '#') {
+ cSkinVariable * variable = mSkin->GetVariable(text.substr(1));
+ if (variable) {
+ text = variable->Value().String();
+ }
+ }
+ ParamColor = cColor::ParseColor(text);
+ return (ParamColor == cColor::ERRCOL) ? false : true;
}
bool cSkinObject::ParseCondition(const std::string & Text)
@@ -126,6 +164,19 @@ bool cSkinObject::ParseAlignment(const std::string & Text)
return true;
}
+bool cSkinObject::ParseVerticalAlignment(const std::string & Text)
+{
+ if (Text == "top")
+ mVerticalAlign = tvaTop;
+ else if (Text == "middle")
+ mVerticalAlign = tvaMiddle;
+ else if (Text == "bottom")
+ mVerticalAlign = tvaBottom;
+ else
+ return false;
+ return true;
+}
+
bool cSkinObject::ParseIntParam(const std::string &Text, int & Param)
{
if (isalpha(Text[0]) || Text[0] == '#')
@@ -181,6 +232,63 @@ bool cSkinObject::ParseFontFace(const std::string & Text)
return mFont.Parse(Text);
}
+
+bool cSkinObject::ParseScrollLoopMode(const std::string & Text)
+{
+ if (Text == "never")
+ mScrollLoopMode = 0;
+ else if (Text == "once")
+ mScrollLoopMode = 1;
+ else if (Text == "always")
+ mScrollLoopMode = 2;
+ else
+ return false;
+ return true;
+}
+
+bool cSkinObject::ParseScrollSpeed(const std::string & Text)
+{
+ int val;
+ if (!ParseIntParam(Text, val))
+ return false;
+
+ if (val < 0 || val > 10)
+ return false;
+
+ mScrollSpeed = val;
+ return true;
+}
+
+bool cSkinObject::ParseScrollTime(const std::string & Text)
+{
+ int val;
+ if (!ParseIntParam(Text, val))
+ return false;
+
+ if (val < 0 || val > 2000)
+ return false;
+
+ if (val > 0 && val < 100)
+ val = 100;
+
+ mScrollTime = val;
+ return true;
+}
+
+
+bool cSkinObject::ParseAltCondition(const std::string & Text)
+{
+ cSkinFunction *result = new cSkinFunction(this);
+ if (result->Parse(Text))
+ {
+ delete mAltCondition;
+ mAltCondition = result;
+ return true;
+ }
+ return false;
+}
+
+
void cSkinObject::SetListIndex(int MaxItems, int Index)
{
mText.SetListIndex(MaxItems, Index);
@@ -211,22 +319,65 @@ tSize cSkinObject::Size(void) const
void cSkinObject::Render(GLCD::cBitmap * screen)
{
+ uint64_t timestamp;
+
if (mCondition != NULL && !mCondition->Evaluate())
return;
+ timestamp = mSkin->Config().Now();
+
switch (Type())
{
case cSkinObject::image:
{
cImageCache * cache = mSkin->ImageCache();
- GLCD::cImage * image = cache->Get(mPath.Evaluate());
+ int currScrollLoopMode = 2; //default if not configured in the skin: always
+
+ cType evalPath = mPath.Evaluate();
+ std::string currPath = evalPath;
+
+ if (currPath != mStoredImagePath) {
+ mImageFrameId = 0;
+ mStoredImagePath = currPath;
+ mScrollLoopReached = false;
+ mLastChange = timestamp;
+ mChangeDelay = -1;
+ }
+
+ GLCD::cImage * image = cache->Get(evalPath);
if (image)
{
- const GLCD::cBitmap * bitmap = image->GetBitmap();
+ int framecount = image->Count();
+
+ const GLCD::cBitmap * bitmap = image->GetBitmap(mImageFrameId);
+
if (bitmap)
{
- screen->DrawBitmap(Pos().x, Pos().y, *bitmap, mColor);
+ if (mColor == cColor::ERRCOL)
+ screen->DrawBitmap(Pos().x, Pos().y, *bitmap);
+ else
+ screen->DrawBitmap(Pos().x, Pos().y, *bitmap, mColor, mBackgroundColor);
+ }
+
+ if (mScrollLoopMode != -1) // if == -1: currScrollLoopMode already contains correct value
+ currScrollLoopMode = mScrollLoopMode;
+
+ if (framecount > 1 && currScrollLoopMode > 0 && !mScrollLoopReached) {
+ mChangeDelay = image->Delay();
+
+ if ( (uint32_t)(timestamp - mLastChange) >= (uint32_t)mChangeDelay) {
+
+ if (currScrollLoopMode == 1 && mImageFrameId+1 == framecount) {
+ mScrollLoopReached = true; // stop looping and switch to 1st frame
+ }
+
+ mImageFrameId = (mImageFrameId+1) % framecount;
+ mLastChange = timestamp;
+ }
}
+
+ if (mLastChange == 0)
+ mLastChange = timestamp;
}
break;
}
@@ -301,16 +452,76 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
}
case cSkinObject::text:
+ case cSkinObject::scrolltext:
{
cSkinFont * skinFont = mSkin->GetFont(mFont.Evaluate());
+ int currScrollLoopMode = 1; // default values if no setup default values available
+ int currScrollSpeed = 8;
+ int currScrollTime = 500;
+
+ // get default values from derived config-class if available
+ tSkinToken token = tSkinToken();
+ token.Id = mSkin->Config().GetTokenId("ScrollMode");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollLoopMode = (int)(t);
+ }
+ token.Id = mSkin->Config().GetTokenId("ScrollSpeed");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollSpeed = (int)(t);
+ }
+ token.Id = mSkin->Config().GetTokenId("ScrollTime");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollTime = (int)(t);
+ }
+
if (skinFont)
{
const cFont * font = skinFont->Font();
- std::string text = mText.Evaluate();
+ std::string text = "";
+
+ // is an alternative text defined + alternative condition defined and true?
+ if (mAltCondition != NULL && mAltCondition->Evaluate() && (mAltText.size() != 0)) {
+ cSkinString *result = new cSkinString(this, false);
+
+ if (result->Parse(mAltText)) {
+ text = (std::string) result->Evaluate();
+ }
+ delete result;
+ } else { // nope: use the original text
+ text = (std::string) mText.Evaluate();
+ }
+
+ if (! (text == mCurrText) ) {
+ mScrollOffset = 0;
+ mCurrText = text;
+ mScrollLoopReached = false;
+ mLastChange = timestamp;
+ }
+
if (mMultiline)
{
+ // scrolling in multiline not supported at the moment
+ mScrollLoopReached = true; // avoid check in NeedsUpdate()
+
std::vector <std::string> lines;
font->WrapText(Size().w, Size().h, text, lines);
+
+ // vertical alignment, calculate y offset
+ int yoff = 0;
+ int diff = Size().h - lines.size() * font->LineHeight();
+ switch (mVerticalAlign) {
+ case tvaMiddle:
+ yoff = (diff > 0) ? diff >> 1 : 0;
+ break;
+ case tvaBottom:
+ yoff = (diff > 0) ? diff : 0;
+ break;
+ default: yoff = 0;
+ }
+
for (size_t i = 0; i < lines.size(); i++)
{
int w = font->Width(lines[i]);
@@ -326,14 +537,30 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
x += (Size().w - w) / 2;
}
}
- screen->DrawText(x, Pos().y + i * font->LineHeight(), x + Size().w - 1, lines[i], font, mColor);
+ screen->DrawText(x, yoff + Pos().y + i * font->LineHeight(), x + Size().w - 1, lines[i], font, mColor, mBackgroundColor);
}
}
else
{
+ // vertical alignment, calculate y offset
+ int yoff = 0;
+ int diff = Size().h - font->LineHeight();
+ switch (mVerticalAlign) {
+ case tvaMiddle:
+ yoff = (diff > 0) ? diff >> 1 : 0;
+ break;
+ case tvaBottom:
+ yoff = (diff > 0) ? diff : 0;
+ break;
+ default: yoff = 0;
+ }
+
if (text.find('\t') != std::string::npos
&& mSkin->Config().GetTabPosition(0, Size().w, *font) > 0)
{
+ // scrolling in texts with tabulators not supported at the moment
+ mScrollLoopReached = true; // avoid check in NeedsUpdate()
+
std::string::size_type pos1;
std::string::size_type pos2;
std::string str;
@@ -348,7 +575,7 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
{
str = text.substr(pos1, pos2 - pos1);
tabWidth = mSkin->Config().GetTabPosition(tab, Size().w, *font);
- screen->DrawText(x, Pos().y, x + tabWidth - 1, str, font, mColor);
+ screen->DrawText(x, yoff + Pos().y, x + tabWidth - 1, str, font, mColor, mBackgroundColor);
pos1 = pos2 + 1;
pos2 = text.find('\t', pos1);
tabWidth += font->Width(' ');
@@ -357,14 +584,18 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
tab++;
}
str = text.substr(pos1);
- screen->DrawText(x, Pos().y, x + w - 1, str, font, mColor);
+ screen->DrawText(x, yoff + Pos().y, x + w - 1, str, font, mColor, mBackgroundColor);
}
else
{
int w = font->Width(text);
int x = Pos().x;
+ bool updateScroll = false;
+
if (w < Size().w)
{
+ mScrollLoopReached = true; // avoid check in NeedsUpdate()
+
if (mAlign == taRight)
{
x += Size().w - w;
@@ -373,18 +604,105 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
{
x += (Size().w - w) / 2;
}
+ } else {
+
+ if (mScrollLoopMode != -1) // if == -1: currScrollLoopMode already contains correct value
+ currScrollLoopMode = mScrollLoopMode;
+
+ if (mScrollSpeed > 0)
+ currScrollSpeed = mScrollSpeed;
+
+ if (mScrollTime > 0)
+ currScrollTime = mScrollTime;
+
+ if (currScrollLoopMode > 0 && (!mScrollLoopReached || mScrollOffset) &&
+ ((uint32_t)(timestamp-mLastChange) >= (uint32_t)currScrollTime)
+ )
+ {
+ if (mScrollLoopReached)
+ mScrollOffset = 0;
+ else
+ updateScroll = true;
+ }
+
+ }
+
+ if (mScrollOffset) {
+ int corr_scrolloffset = mScrollOffset;
+ /* object update before scrolltime? use previous offset to avoid 'stumbling' scrolling */
+ if ((uint32_t)(timestamp-mLastChange) < (uint32_t)currScrollTime) {
+ corr_scrolloffset -= currScrollSpeed;
+ if (corr_scrolloffset < 0)
+ corr_scrolloffset = 0;
+ }
+ w += font->Width(" ");
+ std::string textdoubled = text + " " + text;
+ screen->DrawText(x, yoff + Pos().y, x + Size().w - 1, textdoubled, font, mColor, mBackgroundColor, true, corr_scrolloffset);
+ } else {
+ screen->DrawText(x, yoff + Pos().y, x + Size().w - 1, text, font, mColor, mBackgroundColor, true, mScrollOffset);
+ }
+
+ if (updateScroll) {
+ mScrollOffset += currScrollSpeed;
+
+ if ( x + Size().w + mScrollOffset >= (w+Size().w - font->Width(" "))) {
+ if (currScrollLoopMode == 1)
+ // reset mScrollOffset in next step (else: string not redrawn when scroll done)
+ mScrollLoopReached = true;
+ else
+ mScrollOffset= 0;
+ }
+ updateScroll = false;
+ mLastChange = timestamp;
}
- screen->DrawText(x, Pos().y, x + Size().w - 1, text, font, mColor);
}
}
}
break;
}
- case cSkinObject::scrolltext:
+// case cSkinObject::scrolltext:
//DrawScrolltext(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
// Object->Align());
+// break;
+
+ case cSkinObject::button:
+ {
+ cSkinFont * skinFont = mSkin->GetFont(mFont.Evaluate());
+
+ if (mBackgroundColor == mColor || mBackgroundColor == cColor::Transparent)
+ mBackgroundColor = cColor(mColor).Invert();
+
+ if (mRadius == 0)
+ screen->DrawRectangle(Pos().x, Pos().y, Pos().x + Size().w - 1, Pos().y + Size().h - 1, mBackgroundColor, true);
+ else
+ screen->DrawRoundRectangle(Pos().x, Pos().y, Pos().x + Size().w - 1, Pos().y + Size().h - 1, mBackgroundColor, true, mRadius);
+
+ if (skinFont)
+ {
+ const cFont * font = skinFont->Font();
+ std::string text = "";
+
+ text = (std::string) mText.Evaluate();
+
+ if (! (text == mCurrText) ) {
+ mCurrText = text;
+ }
+ std::vector <std::string> lines;
+ font->WrapText(Size().w, Size().h, text, lines);
+
+ // always use middle vertical alignment for buttons
+ int diff = Size().h - lines.size() * font->LineHeight();
+ int yoff = (diff > 0) ? diff >> 1 : 0;
+
+ int w = font->Width(text);
+ int x = Pos().x;
+ if (w < Size().w) // always center alignment for buttons
+ x += (Size().w - w) / 2;
+ screen->DrawText(x, yoff + Pos().y, x + Size().w - 1, text, font, mColor, mBackgroundColor);
+ }
break;
+ }
case cSkinObject::scrollbar:
//DrawScrollbar(Object->Pos(), Object->Size(), Object->Bg(), Object->Fg());
@@ -430,6 +748,152 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
}
}
+bool cSkinObject::NeedsUpdate(uint64_t CurrentTime)
+{
+ if (mCondition != NULL && !mCondition->Evaluate())
+ return false;
+
+ switch (Type())
+ {
+ case cSkinObject::image:
+ {
+ int currScrollLoopMode = 2;
+
+ if (mScrollLoopMode != -1)
+ currScrollLoopMode = mScrollLoopMode;
+
+ if ( mChangeDelay > 0 && currScrollLoopMode > 0 && !mScrollLoopReached &&
+ ( (uint32_t)(CurrentTime-mLastChange) >= (uint32_t)mChangeDelay)
+ )
+ {
+ return true;
+ }
+ return false;
+ break;
+ }
+ case cSkinObject::text:
+ case cSkinObject::scrolltext:
+ //case cSkinObject::button:
+ {
+ int currScrollLoopMode = 1; // default values if no setup default values available
+ int currScrollTime = 500;
+
+ std::string text = "";
+
+ // is an alternative text defined + alternative condition defined and true?
+ if (mAltCondition != NULL && mAltCondition->Evaluate() && (mAltText.size() != 0)) {
+ cSkinString *result = new cSkinString(this, false);
+
+ if (result->Parse(mAltText)) {
+ text = (std::string) result->Evaluate();
+ }
+ delete result;
+ } else { // nope: use the original text
+ text = (std::string) mText.Evaluate();
+ }
+
+ // get default values from derived config-class if available
+ tSkinToken token = tSkinToken();
+ token.Id = mSkin->Config().GetTokenId("ScrollMode");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollLoopMode = (int)(t);
+ }
+ token.Id = mSkin->Config().GetTokenId("ScrollTime");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollTime = (int)(t);
+ }
+
+ if (mScrollLoopMode != -1) // if == -1: currScrollLoopMode already contains correct value
+ currScrollLoopMode = mScrollLoopMode;
+
+ if (mScrollTime > 0)
+ currScrollTime = mScrollTime;
+
+ if ( (text != mCurrText) ||
+ ( (currScrollLoopMode > 0) && (!mScrollLoopReached || mScrollOffset) &&
+ ((uint32_t)(CurrentTime-mLastChange) >= (uint32_t)currScrollTime)
+ )
+ )
+ {
+ return true;
+ }
+ return false;
+ break;
+ }
+ case cSkinObject::progress:
+ return false;
+ break;
+ case cSkinObject::block:
+ {
+ for (uint32_t i = 0; i < NumObjects(); i++) {
+ if ( GetObject(i)->NeedsUpdate(CurrentTime) ) {
+ return true;
+ }
+ }
+ return false;
+ break;
+ }
+ default: // all other elements are static ones
+ return false;
+ }
+ return false;
+}
+
+
+std::string cSkinObject::CheckAction(cGLCDEvent * ev)
+{
+ if (mCondition != NULL && !mCondition->Evaluate())
+ return "";
+
+ switch (Type())
+ {
+ case cSkinObject::image:
+ case cSkinObject::text:
+ case cSkinObject::scrolltext:
+ case cSkinObject::progress:
+ case cSkinObject::rectangle:
+ case cSkinObject::ellipse:
+ case cSkinObject::slope:
+ case cSkinObject::button:
+ case cSkinObject::item:
+ {
+ if (mAction == "")
+ return "";
+
+ if (ev && (typeid(*ev) == typeid(cSimpleTouchEvent))) {
+ cSimpleTouchEvent * stev = (cSimpleTouchEvent*)ev;
+ // check if touch event is in bounding box of object
+ // uses > and < -1 instead of >= and < -0 for better results
+ if ( (stev->x > Pos().x) && (stev->x < (Pos().x+Size().w -1)) &&
+ (stev->y > Pos().y) && (stev->y < (Pos().y+Size().h -1))
+ )
+ {
+ return mAction;
+ }
+ }
+ return "";
+ break;
+ }
+ case cSkinObject::block:
+ {
+ std::string rv = "";
+ for (uint32_t i = 0; i < NumObjects(); i++) {
+ if ( (rv = GetObject(i)->CheckAction(ev)) != "" ) {
+ return rv;
+ }
+ }
+ return "";
+ break;
+ }
+ default:
+ return "";
+ }
+ return "";
+}
+
+
cSkinObjects::cSkinObjects(void)
{
}