Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 65 additions & 4 deletions src/GEMSpinner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,37 @@
#include "GEMSpinner.h"
#include "constants.h"

GEMSpinner::GEMSpinner(GEMSpinnerBoundariesByte boundaries_, bool loop_)
GEMSpinner::GEMSpinner(GEMSpinnerBoundariesByte boundaries_, bool loop_, GEMSpinnerValueStringCallback valueStringCallback_)
: _boundaries{ { .boundariesByte = { .step = boundaries_.step, .min = boundaries_.min < boundaries_.max ? boundaries_.min : boundaries_.max, .max = boundaries_.max > boundaries_.min ? boundaries_.max : boundaries_.min } } }
, _type(GEM_VAL_BYTE)
, _length(abs((boundaries_.max - boundaries_.min) / boundaries_.step) + 1)
, _loop(loop_)
, _valueStringCallback(valueStringCallback_)
{ }

GEMSpinner::GEMSpinner(GEMSpinnerBoundariesInt boundaries_, bool loop_)
GEMSpinner::GEMSpinner(GEMSpinnerBoundariesInt boundaries_, bool loop_, GEMSpinnerValueStringCallback valueStringCallback_)
: _boundaries{ { .boundariesInt = { .step = abs(boundaries_.step), .min = boundaries_.min < boundaries_.max ? boundaries_.min : boundaries_.max, .max = boundaries_.max > boundaries_.min ? boundaries_.max : boundaries_.min } } }
, _type(GEM_VAL_INTEGER)
, _length(abs((boundaries_.max - boundaries_.min) / boundaries_.step) + 1)
, _loop(loop_)
, _valueStringCallback(valueStringCallback_)
{ }

#ifdef GEM_SUPPORT_FLOAT_EDIT
GEMSpinner::GEMSpinner(GEMSpinnerBoundariesFloat boundaries_, bool loop_)
GEMSpinner::GEMSpinner(GEMSpinnerBoundariesFloat boundaries_, bool loop_, GEMSpinnerValueStringCallback valueStringCallback_)
: _boundaries{ { .boundariesFloat = { .step = abs(boundaries_.step), .min = boundaries_.min < boundaries_.max ? boundaries_.min : boundaries_.max, .max = boundaries_.max > boundaries_.min ? boundaries_.max : boundaries_.min } } }
, _type(GEM_VAL_FLOAT)
, _length(abs((boundaries_.max - boundaries_.min) / boundaries_.step) + 1)
, _loop(loop_)
, _valueStringCallback(valueStringCallback_)
{ }

GEMSpinner::GEMSpinner(GEMSpinnerBoundariesDouble boundaries_, bool loop_)
GEMSpinner::GEMSpinner(GEMSpinnerBoundariesDouble boundaries_, bool loop_, GEMSpinnerValueStringCallback valueStringCallback_)
: _boundaries{ { .boundariesDouble = { .step = abs(boundaries_.step), .min = boundaries_.min < boundaries_.max ? boundaries_.min : boundaries_.max, .max = boundaries_.max > boundaries_.min ? boundaries_.max : boundaries_.min } } }
, _type(GEM_VAL_DOUBLE)
, _length(abs((boundaries_.max - boundaries_.min) / boundaries_.step) + 1)
, _loop(loop_)
, _valueStringCallback(valueStringCallback_)
{ }
#endif

Expand Down Expand Up @@ -196,3 +200,60 @@ void GEMSpinner::setValue(void* variable, int index, void* referenceVariable) {
#endif
}
}


byte GEMSpinner::produceStringForValue(char* stringBuffer, byte stringBufferLength, const GEMSpinnerValue& value, int valuePrecision) const
{
if (_valueStringCallback != nullptr)
{
const char* const string = _valueStringCallback(value, _type, valuePrecision);
if (string != nullptr)
{
const size_t stringLength = MIN(strlen(string), (size_t)stringBufferLength - 1);
memcpy(stringBuffer, string, stringLength);
if (stringLength == stringBufferLength - 1)
{
stringBuffer[stringLength - 1] = '~'; // our "ellipsis"
}
stringBuffer[stringLength] = '\0';
return (byte)stringLength;
}
}

switch (_type)
{
case GEM_VAL_BYTE:
itoa(value.valByte, stringBuffer, 10);
break;
case GEM_VAL_INTEGER:
itoa(value.valInt, stringBuffer, 10);
break;
#ifdef GEM_SUPPORT_FLOAT_EDIT
case GEM_VAL_FLOAT:
dtostrf(value.valFloat, valuePrecision + 1, valuePrecision, stringBuffer);
break;
case GEM_VAL_DOUBLE:
dtostrf(value.valDouble, valuePrecision + 1, valuePrecision, stringBuffer);
break;
#endif
default:
*stringBuffer = '\0';
break;
}

return strlen(stringBuffer);
}


GEMSpinner& GEMSpinner::setValueStringCallback(GEMSpinnerValueStringCallback callback)
{
_valueStringCallback = callback;
return *this;
}


GEMSpinner& GEMSpinner::removeValueStringCallback()
{
_valueStringCallback = nullptr;
return *this;
}
14 changes: 10 additions & 4 deletions src/GEMSpinner.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ struct GEMSpinnerValue {
};
};

typedef const char* const (*GEMSpinnerValueStringCallback)(GEMSpinnerValue /* value */, byte /* value type */, int /* value precision */);

// Declaration of GEMSpinner class
class GEMSpinner {
friend class GEM;
Expand All @@ -105,21 +107,25 @@ class GEMSpinner {
@param 'loop_' (optional) - whether iteration over options should be looped
values GEM_LOOP (alias for true)
*/
GEMSpinner(GEMSpinnerBoundariesByte boundaries_, bool loop_ = false);
GEMSpinner(GEMSpinnerBoundariesInt boundaries_, bool loop_ = false);
GEMSpinner(GEMSpinnerBoundariesByte boundaries_, bool loop_ = false, GEMSpinnerValueStringCallback valueStringCallback_ = nullptr);
GEMSpinner(GEMSpinnerBoundariesInt boundaries_, bool loop_ = false, GEMSpinnerValueStringCallback valueStringCallback_ = nullptr);
#ifdef GEM_SUPPORT_FLOAT_EDIT
GEMSpinner(GEMSpinnerBoundariesFloat boundaries_, bool loop_ = false);
GEMSpinner(GEMSpinnerBoundariesDouble boundaries_, bool loop_ = false);
GEMSpinner(GEMSpinnerBoundariesFloat boundaries_, bool loop_ = false, GEMSpinnerValueStringCallback valueStringCallback_ = nullptr);
GEMSpinner(GEMSpinnerBoundariesDouble boundaries_, bool loop_ = false, GEMSpinnerValueStringCallback valueStringCallback_ = nullptr);
#endif
GEMSpinner& setLoop(bool mode = true); // Explicitly set or unset loop mode
bool getLoop(); // Get current value of loop mode
GEM_VIRTUAL GEMSpinnerValue getOptionNameByIndex(void* variable, int index); // Get option by its index
GEM_VIRTUAL byte produceStringForValue(char* stringBuffer, byte stringBufferLength, const GEMSpinnerValue& value, int valuePrecision) const;
GEM_VIRTUAL GEMSpinner& setValueStringCallback(GEMSpinnerValueStringCallback callback);
GEM_VIRTUAL GEMSpinner& removeValueStringCallback();
protected:
GEMSpinnerBoundaries _boundaries;
byte _type;
int _length;
bool _loop = false;
byte getType();
GEMSpinnerValueStringCallback _valueStringCallback;
int getLength();
GEM_VIRTUAL int getSelectedOptionNum(void* variable);
GEM_VIRTUAL void setValue(void* variable, int index, void* referenceVariable = nullptr); // Assign value of the selected option to supplied variable, optionally setting reference variable for counting options
Expand Down
18 changes: 2 additions & 16 deletions src/GEM_adafruit_gfx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,22 +425,8 @@ void GEM_adafruit_gfx::printMenuItem(GEMItem* menuItemTmp, byte yText, byte yDra
case GEM_VAL_SPINNER:
{
GEMSpinner* spinner = menuItemTmp->spinner;
switch (spinner->getType()) {
case GEM_VAL_BYTE:
itoa(*(byte*)menuItemTmp->linkedVariable, valueStringTmp, 10);
break;
case GEM_VAL_INTEGER:
itoa(*(int*)menuItemTmp->linkedVariable, valueStringTmp, 10);
break;
#ifdef GEM_SUPPORT_FLOAT_EDIT
case GEM_VAL_FLOAT:
dtostrf(*(float*)menuItemTmp->linkedVariable, menuItemTmp->precision + 1, menuItemTmp->precision, valueStringTmp);
break;
case GEM_VAL_DOUBLE:
dtostrf(*(double*)menuItemTmp->linkedVariable, menuItemTmp->precision + 1, menuItemTmp->precision, valueStringTmp);
break;
#endif
}
GEMSpinnerValue valueTmp = spinner->getOptionNameByIndex(menuItemTmp->linkedVariable, _valueSelectNum);
spinner->produceStringForValue(valueStringTmp, sizeof(valueStringTmp), valueTmp, menuItemTmp->precision);
printMenuItemValue(valueStringTmp, -1 * calculateSpriteOverlap(GEM_ICON_SPINNER_ARROWS));
drawSprite(_agfx.width() - getSprite(GEM_ICON_SPINNER_ARROWS)->width - 1 * _spriteSize, yDraw, GEM_ICON_SPINNER_ARROWS, color, menuItemTmp);
}
Expand Down
34 changes: 2 additions & 32 deletions src/GEM_u8g2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,42 +496,12 @@ void GEM_u8g2::printMenuItems() {
GEMSpinner* spinner = menuItemTmp->spinner;
if (_editValueMode && menuItemTmp == _menuPageCurrent->getCurrentMenuItem()) {
GEMSpinnerValue valueTmp = spinner->getOptionNameByIndex(menuItemTmp->linkedVariable, _valueSelectNum);
switch (spinner->getType()) {
case GEM_VAL_BYTE:
itoa(valueTmp.valByte, valueStringTmp, 10);
break;
case GEM_VAL_INTEGER:
itoa(valueTmp.valInt, valueStringTmp, 10);
break;
#ifdef GEM_SUPPORT_FLOAT_EDIT
case GEM_VAL_FLOAT:
dtostrf(valueTmp.valFloat, menuItemTmp->precision + 1, menuItemTmp->precision, valueStringTmp);
break;
case GEM_VAL_DOUBLE:
dtostrf(valueTmp.valDouble, menuItemTmp->precision + 1, menuItemTmp->precision, valueStringTmp);
break;
#endif
}
spinner->produceStringForValue(valueStringTmp, sizeof(valueStringTmp), valueTmp, menuItemTmp->precision);
printMenuItemValue(valueStringTmp, -1 * calculateSpriteOverlap(GEM_ICON_SPINNER_ARROWS));
drawSprite(_u8g2.getDisplayWidth() - getSprite(GEM_ICON_SPINNER_ARROWS)->width - 1, yDraw, GEM_ICON_SPINNER_ARROWS, menuItemTmp);
drawEditValueCursor();
} else {
switch (spinner->getType()) {
case GEM_VAL_BYTE:
itoa(*(byte*)menuItemTmp->linkedVariable, valueStringTmp, 10);
break;
case GEM_VAL_INTEGER:
itoa(*(int*)menuItemTmp->linkedVariable, valueStringTmp, 10);
break;
#ifdef GEM_SUPPORT_FLOAT_EDIT
case GEM_VAL_FLOAT:
dtostrf(*(float*)menuItemTmp->linkedVariable, menuItemTmp->precision + 1, menuItemTmp->precision, valueStringTmp);
break;
case GEM_VAL_DOUBLE:
dtostrf(*(double*)menuItemTmp->linkedVariable, menuItemTmp->precision + 1, menuItemTmp->precision, valueStringTmp);
break;
#endif
}
spinner->produceStringForValue(valueStringTmp, sizeof(valueStringTmp), *static_cast<const GEMSpinnerValue*>(menuItemTmp->linkedVariable), menuItemTmp->precision);
printMenuItemValue(valueStringTmp, -1 * calculateSpriteOverlap(GEM_ICON_SPINNER_ARROWS));
drawSprite(_u8g2.getDisplayWidth() - getSprite(GEM_ICON_SPINNER_ARROWS)->width - 1, yDraw, GEM_ICON_SPINNER_ARROWS, menuItemTmp);
}
Expand Down