#ifndef TWEAKABLE_H
#define TWEAKABLE_H
#include "gtArraySimple.h"
#include <stdint.h>
#include "MiscHelpers.h"
#include "gtVector3i.h"
// Define an interface for variables which can be tweaked
class Tweakable
{
public:
virtual ~Tweakable() {}
virtual const char* GetName() const = 0;
virtual const char* GetValue() const = 0;
virtual void Increment() = 0;
virtual void Decrement() = 0;
virtual void Select() = 0; // possible to do some action if enter pressed
virtual bool AutoRepeat() = 0;
};
// Little helper to convert different types to a const char*
class String
{
public:
String(char a_c) { m_str = new char[64]; SNPRINTF(m_str, 64, "%c", a_c); }
String(const char* a_s) { m_str = new char[64]; SNPRINTF(m_str, 64, "%s", a_s); }
String(int a_i) { m_str = new char[64]; SNPRINTF(m_str, 64, "%i", a_i); }
String(unsigned a_i) { m_str = new char[64]; SNPRINTF(m_str, 64, "%ui", a_i); }
String(float a_f) { m_str = new char[64]; SNPRINTF(m_str, 64, "%.3g", a_f); }
String(double a_f) { m_str = new char[64]; SNPRINTF(m_str, 64, "%.3g", a_f); }
String(bool a_b) { m_str = new char[64]; SNPRINTF(m_str, 64, "%s", (a_b) ? "true" : "false"); }
String(gtVector3i a_v) { m_str = new char[64]; SNPRINTF(m_str, 64, "( %i, %i, %i )", a_v.x, a_v.y, a_v.z); }
operator const char*() { return m_str; }
static void Release(const char* a_str) { delete[] a_str; }
private:
char* m_str; // for small strings
};
// Generic tweakable implementation which uses the String class above for automatic conversion of the variable to a string for printing
// To handle new types using this generic implementation, one must implement an operator/constructor to convert the variable type to a String object
template <typename T>
class TweakableT : public Tweakable
{
public:
TweakableT(const char* a_name, T& a_var, const T a_inc) : m_name(a_name), m_var(a_var), m_inc(a_inc) {}
virtual const char* GetName() const { return m_name; }
virtual const char* GetValue() const { return (const char*)String(m_var); }
virtual void Increment() { m_var += m_inc; }
virtual void Decrement() { m_var -= m_inc; }
virtual void Select() { }
virtual bool AutoRepeat() { return false; }
private:
const char* m_name;
T& m_var;
const T m_inc;
};
// Specializations for the bool case
void TweakableT<bool>::Select() { m_var = (m_var) ? false : true; }
void TweakableT<bool>::Increment() { m_var = (m_var) ? false : true; }
void TweakableT<bool>::Decrement() { m_var = (m_var) ? false : true; }
bool TweakableT<bool>::AutoRepeat() { return false; }
// A static list of const char * strings in an array can be rotated through with this tweakable implmentation
class StringListTweakable : public Tweakable
{
public:
StringListTweakable(const char* a_name, const char** a_list, int a_elements, int& a_index) : m_name(a_name), m_StringList(a_list), m_stringListSize(a_elements), m_stringListIndex(a_index) {}
~StringListTweakable() {}
const char* GetName() const { return m_name; }
const char* GetValue() const { return DuplicateString((m_stringListIndex > 0 && m_stringListIndex < m_stringListSize) ? m_StringList[m_stringListIndex] : "<invalid index>"); };
void Increment() { m_stringListIndex = (m_stringListIndex == m_stringListSize - 1) ? 0 : m_stringListIndex + 1; }
void Decrement() { m_stringListIndex = (m_stringListIndex == 0) ? m_stringListSize - 1 : m_stringListIndex - 1; }
void Select() { }
bool AutoRepeat() { return false; }
private:
const char* m_name;
const char** m_StringList;
int m_stringListSize;
int& m_stringListIndex;
};
// Convenience macro for adding Tweakable variables
#define NEW_TWEAKABLE(var, inc) (new TweakableT<decltype(var)>(#var, var, inc))
#define NEW_LIST_TWEAKABLE(var, index) (new StringListTweakable(#var, var, ARRAYSIZE(var), index))
// Manage a list of tweakables, printing and updating the input when called at appropriate times
class Tweakables
{
public:
Tweakables();
virtual ~Tweakables();
void Add(Tweakable* a_tweakable);
Tweakable* FindByName(const char* a_tweakableName);
void Print(float x, float y, float w, float h);
void UpdateInput();
private:
uint32_t m_currentTweakable;
gtArraySimple<Tweakable*> m_tweakables;
};
#endif // TWEAKABLE_H