Is your feature request related to a problem?
Currently the BaseWidgetValue<T,V> template takes a format string specifier and assumes that the literal T value is what is going to be "printed" to the buffer for display.
I have a circumstance where there is a level of indirection for providing the string representation of the value (e.g, const char* stringForValue(const T& value)). I need to do this for both ItemList and ItemRange menu items, which means I had to create 4 new template classes which closely mirror the Item/Widget pairings and have an overridden uint8_t draw(char* buffer, const uint8_t start) method in the widget classes. It felt like I was essentially copying code instead of providing just some minor overrides.
If the BaseWidgetValue<T,V> supported an additional constructor (or template parameter) which described the mechanism for "drawing" the value's representation then that could percolate up through the existing class hierarchy to ItemRange, ItemList and ItemBool.
Describe the solution you'd like?
Starting at BaseWidgetValue<T,V>:
template <typename T>
class ValueDrawer
{
public:
virtual uint8_t draw(const T& value, char* buffer, const uint8_t start) = 0;
};
template <typename T>
class PrintfValueDrawer : ValueDrawer<T>
{
public:
PrintfValueDrawer(const char* format)
: ValueDrawer<T>(), format(format)
{ }
uint8_t draw(const T& value, char* buffer, const uint8_t start) override
{
if (start >= ITEM_DRAW_BUFFER_SIZE) return 0;
return snprintf(buffer + start, ITEM_DRAW_BUFFER_SIZE - start, format, value);
}
private:
const char* format;
};
template <typename T>
class DynamicStringValueDrawer : ValueDrawer<T>
{
public:
typedef const char* (*StringProvingFunction)(const T& value);
DynamicStringValueDrawer(StringProvingFunction stringForValue)
: ValueDrawer<T>(), stringForValue(stringForValue)
{ }
uint8_t draw(const T& value, char* buffer, const uint8_t start) override
{
if (start >= ITEM_DRAW_BUFFER_SIZE) return 0;
const char* valueString = stringForValue(value);
if (valueString == nullptr) return 0;
return snprintf(buffer + start, ITEM_DRAW_BUFFER_SIZE - start, "%s", valueString);
}
private:
StringProvingFunction stringForValue;
};
template <typename T>
class BaseWidgetValue : public BaseWidget
{
ValueDrawer<T>* valueDrawer;
BaseWidgetValue(
const T& value,
const char* format,
const uint8_t cursorOffset = 0,
void (*callback)(const T&) = nullptr)
: BaseWidgetValue(value, new PrintfValueDrawer(format), cursorOffset, callback)
BaseWidgetValue(
const T& value,
DynamicStringValueDrawer<T>::StringProvingFunction stringForValueFunction,
const uint8_t cursorOffset = 0,
void (*callback)(const T&) = nullptr)
: BaseWidgetValue(value, new DynamicStringValueDrawer(stringForValueFunction), cursorOffset, callback)
BaseWidgetValue(
const T& value,
ValueDrawer<T>* valueDrawer,
const uint8_t cursorOffset = 0,
void (*callback)(const T&) = nullptr)
: BaseWidget(cursorOffset), value(value), valueDrawer(valueDrawer), callback(callback) {}
uint8_t draw(char* buffer, const uint8_t start) override {
return valueDrawer->draw(value, buffer, start);
}
}
and then the format vs. stringForValueFunction (or just the ValueDrawer<T>*) could then be distributed to all the instantiators of BaseWidgetValue.
Describe alternatives you've considered
I wrote my own subclasses that mirrored the ItemList/WidgetList and ItemRange/WidgetRange pairings so that I could override the WidgetList::draw() and WidgetRange::draw() functions.
Additional context
No response
Is your feature request related to a problem?
Currently the
BaseWidgetValue<T,V>template takes a format string specifier and assumes that the literalTvalue is what is going to be "printed" to the buffer for display.I have a circumstance where there is a level of indirection for providing the string representation of the value (e.g,
const char* stringForValue(const T& value)). I need to do this for bothItemListandItemRangemenu items, which means I had to create 4 new template classes which closely mirror the Item/Widget pairings and have an overriddenuint8_t draw(char* buffer, const uint8_t start)method in the widget classes. It felt like I was essentially copying code instead of providing just some minor overrides.If the
BaseWidgetValue<T,V>supported an additional constructor (or template parameter) which described the mechanism for "drawing" the value's representation then that could percolate up through the existing class hierarchy toItemRange,ItemListandItemBool.Describe the solution you'd like?
Starting at
BaseWidgetValue<T,V>:and then the
formatvs.stringForValueFunction(or just theValueDrawer<T>*) could then be distributed to all the instantiators ofBaseWidgetValue.Describe alternatives you've considered
I wrote my own subclasses that mirrored the
ItemList/WidgetListandItemRange/WidgetRangepairings so that I could override theWidgetList::draw()andWidgetRange::draw()functions.Additional context
No response