#pragma once
#include "Common.h"
#include "Widget.h"
#include "Property.h"
BEGIN_NAMESPACE
// Generally this doesn't end up being too useful right now
// What does it buy you?
// The property is the thing being interacted with, not the widget class
template <typename T>
class AbstractValueWidget : public Widget
{
// TODO, define like this instead:
// template <typename T>
// class AbstractValueWidget : public Inherit<Widget, AbstractValueWidget<T> >
// problem is that it forces a type of constructor we can't support as we need a parameter to set the value reference with
// we can only have constructors with the same parameters as the parent type supports.
public:
AbstractValueWidget(Widget* a_parent, AbstractProperty<T>& a_value)
: Widget(a_parent, 0, LP_Vertical, 0, 0, 10, 10), value(a_value)
{
connect(value.valueChanged, this, &AbstractValueWidget<T>::refresh);
}
void refresh()
{
repaint();
}
// In this case, we store and expose an AbstractProperty since the value held here is used
// to reflect some state from somewhere else in the program
AbstractProperty<T>& value;
};
namespace details
{
extern Property<bool> g_unassignedBool;
extern Property<String> g_unassignedString;
}
// checked and value are now synonym for the same value
// A bit of overhead in having the extra reference
class CheckableWidget : public AbstractValueWidget<bool>
{
public:
CheckableWidget(Object* a_parent, const char* a_name);
CheckableWidget(Widget* a_parent, AbstractProperty<bool>& a_value);
// = details::g_unassignedBool);
//void setProperty(AbstractProperty<bool>& a_value);
AbstractProperty<bool>& checked;
};
// If use this, it breaks various code-completion / tagging systems
#define CLASS(T, ParentT) \
class T : public Inherit<ParentT, T>
class HBox : public Inherit<Widget, HBox>
{
public:
HBox(Object* a_parent, const char* a_name) : HBox(ObjectPtrCast<Widget>(a_parent)) {}
HBox(Widget* a_parent);
// works with code-completion
bool blah(int a, int b) { return true; }
int test;
// doesn't work with code-completion
START_MEMBERS
REFLECT_MEMBER_FUNCTION(blah, "Test member function reflected")
REFLECT_MEMBER(test, "Test int reflected")
END_MEMBERS
};
class VBox : public Inherit<Widget, VBox>
{
public:
VBox(Object* a_parent, const char* a_name) : VBox(ObjectPtrCast<Widget>(a_parent)) {}
VBox(Widget* a_parent);
int test2;
START_MEMBERS
ADD_MEMBER(int, test, "Test int member")
END_MEMBERS
};
// Very simple helper class to create a simple grid
class Grid : public HBox
{
public:
Grid(Widget* parent, int columns) : HBox(parent)
{
v = new VBox*[columns];
for (int i = 0; i < columns; i++)
v[i] = new VBox(this);
}
~Grid()
{
delete[] v;
}
VBox **v;
};
class GroupBoxSpacer : public Widget
{
public:
GroupBoxSpacer(Widget* a_parent);
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
};
class GroupBox : public AbstractValueWidget<String>
{
public:
GroupBox(Widget* a_parent, AbstractProperty<String>& a_title);
AbstractProperty<String>& title;
Property<bool> disabled;
protected:
void paintEvent(PaintEvent& a_event);
private:
GroupBoxSpacer m_titleSpace;
};
class HSpace : public Widget
{
public:
HSpace(Widget* a_parent);
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
};
class VSpace : public Widget
{
public:
VSpace(Widget* a_parent);
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
};
class Label : public AbstractValueWidget<String>
{
public:
Label(Widget* a_parent, AbstractProperty<String>& a_text);
Property<bool> disabled;
// In this case, we store and expose an AbstractProperty since the value held here is used
// to reflect some state from somewhere else in the program - eg we can bind the text to display from somewhere
AbstractProperty<String>& text;
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
};
class Button : public Widget
{
public:
Button(Widget* a_parent, const char* a_text = "");
Action<bool> activated;
Property<String> text;
// TODO: Perhaps these should move to a base class - they seem pretty common to most widgets
Property<bool> disabled;
Property<bool> hasFocus;
Property<bool> isDefault;
protected:
void refresh();
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
void mouseEnterEvent(MouseEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
bool m_mouseDownInButton;
bool m_mouseOverButton;
};
class CheckBox : public CheckableWidget
{
public:
CheckBox(Widget* a_parent, AbstractProperty<bool>& a_checked, const char* a_text = "");
virtual void toggle();
Property<String> text;
Property<bool> disabled;
Property<bool> hasFocus;
Property<bool> isDefault;
void onTextChanged(String a_text);
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
void mouseEnterEvent(MouseEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
bool m_mouseDownInButton;
bool m_mouseOverButton;
};
class RadioButton : public CheckableWidget
{
public:
RadioButton(Widget* a_parent, AbstractProperty<bool>& a_checked, const char* a_text = "");
virtual void toggle();
Property<String> text;
Property<bool> disabled;
Property<bool> hasFocus;
Property<bool> isDefault;
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
void mouseEnterEvent(MouseEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
bool m_mouseDownInButton;
bool m_mouseOverButton;
};
class LineEdit : public AbstractValueWidget<String>
{
public:
LineEdit(Widget* a_parent, AbstractProperty<String>& a_text);
AbstractProperty<String>& text;
Property<bool> disabled;
Property<bool> hasFocus;
protected:
void refresh2() { repaint(); }
void sizeOptions(SizeOptions& a_sizeOptions);
void timerEvent(TimerEvent& a_event);
void keyEvent(KeyEvent& a_event);
void paintEvent(PaintEvent& a_event);
void mouseEnterEvent(MouseEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
bool m_mouseOver;
TimerId m_timerId;
bool m_carrotOn;
size_t m_carrotPosition;
};
// At the moment, it is a value from 0 to 65535 which can be returned as a float from 0.0 to 1.0
// Perhaps could become something like: num / denom, or a min / max thing so has a bias but
// can be returned as a 0->1 value. Idea could be pixels with a minX and maxX of the slider bar
// and a value which is the pixel position along it.
class Range
{
public:
// These are all as if in pixel values
void setMin(uint32_t m) { min = m; }
void setMax(uint32_t m) { max = m; }
void setValue(uint32_t v) { val = v; }
uint32_t getValue() { return val; }
// Return the value as if it was between another range, a_min -> a_max
uint32_t getTranslatedValue(uint32_t a_min, uint32_t a_max)
{
int x = int(*this);
x = (x * (a_max - a_min)) >> 16;
return x + a_min;
}
// Normalized values from 0 -> 65535 and 0.0 -> 1.0 respectively
operator int() const { return ((val - min) << 16) / (max - min); }
operator float() const { int x = int(*this); return float(x) / 65535.0f; }
private:
uint32_t val = 0;
uint32_t min = 0;
uint32_t max = 1UL<<31UL;
};
class SliderR : public AbstractValueWidget<Range>
{
public:
SliderR(Widget* a_parent, AbstractProperty<Range>& a_sliderValue);
// TODO: min/max value the slider value is scaled to
Property<bool> disabled;
Property<bool> hasFocus;
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
bool m_mouseOverSliderHandle;
bool m_mouseDownInSliderHandle;
int sliderWidth();
Rectangle sliderHandleRectangle();
};
class Slider : public AbstractValueWidget<int>
{
public:
Slider(Widget* a_parent, AbstractProperty<int>& a_sliderValue);
// TODO: min/max value the slider value is scaled to
Property<bool> disabled;
Property<bool> hasFocus;
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
bool m_mouseOverSliderHandle;
bool m_mouseDownInSliderHandle;
int sliderWidth();
Rectangle sliderHandleRectangle();
};
class GridSlider
{
public:
GridSlider(Grid* g, AbstractProperty<int>& value, const char* name)
{
label = name;
new Label(g->v[0], label);
new Slider(g->v[1], value);
}
Property<String> label;
};
// TODO: inherit from a 'RangeControl' base class which is a value between a given min/max values
class ScrollBar : public AbstractValueWidget<int>
{
public:
ScrollBar(Widget* a_parent, AbstractProperty<int>& a_scrollValue);
Property<int> minValue; // when the scrollbar is at the top, value will be minValue
Property<int> maxValue; // when the scrollbar is at the bottom, value will be maxValue
Property<bool> proportional; // if true, the scrollbar handle is sized according to pageSize
Property<int> pageSize; // amount to change value by when clicking in gutter.
Property<int> lineSize; // amount to inc/dec value by when pressing the up/down buttons
Property<bool> disabled;
Property<bool> hasFocus;
protected:
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
int getScrollBarHandleHeight();
Rectangle getUpHandleRectangle();
Rectangle getDownHandleRectangle();
Rectangle getScrollHandleRectangle();
bool m_onHandle = false;
//int m_moveStartY = -1;
int m_moveStartValue = -1;
};
// If MVC, this is the M
class ItemList
{
public:
ItemList(AbstractProperty<int>& a_value)
: currentIndex(a_value)
{
}
AbstractProperty<int>& currentIndex;
Property<String> text;
Action<int> changed;
void addItem(String a_itemText)
{
int value = currentIndex.value();
m_items.push_back(a_itemText);
if (m_items.size() >= (size_t)value)
text = m_items[value];
}
void removeItem(String a_itemText)
{
// TODO
}
//private:
Vector<String> m_items;
};
// If MVC, this is the VC
class ListView : public Widget
{
public:
ListView(Widget* a_parent, ItemList& a_list);
protected:
void refresh()
{
repaint();
}
void keyEvent(KeyEvent& a_event);
void paintEvent(PaintEvent& a_event);
void mouseEnterEvent(MouseEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
private:
ItemList& m_list;
bool m_mouseOver;
Point m_mousePos;
};
// TODO: possibly could inherit from Button too
// and button could be a toggle/checkable button, so could
// inherit from AbstractValueWidget<bool> / CheckableWidget
// One solution is to make a base button class that various
// button related classes inherit from.
class ComboBox : public AbstractValueWidget<int>
{
public:
ComboBox(Widget* a_parent, AbstractProperty<int>& a_value);
Signal activated;
Signal deactivated;
//Property<String> text;
Property<bool> disabled;
Property<bool> hasFocus;
Property<bool> isDefault;
//AbstractProperty<int>& currentIndex;
void addItem(String a_itemText);
void listChanged();
protected:
void refresh2();
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
void mouseEnterEvent(MouseEvent& a_event);
void mouseLeaveEvent(MouseEvent& a_event);
void mouseEvent(MouseEvent& a_event);
void keyEvent(KeyEvent& a_event);
private:
bool m_mouseDownInButton;
bool m_mouseOverButton;
bool m_active = false;
// Option List should be a property - a property list
//Vector<String> m_items;
Widget* m_dropDownMenu = nullptr;
ItemList m_list;
};
class ProgressBar : public AbstractValueWidget<int>
{
public:
ProgressBar(Widget* a_parent, AbstractProperty<int>& a_value);
Property<bool> disabled;
AbstractProperty<int>& progress;
protected:
void refresh2();
void sizeOptions(SizeOptions& a_sizeOptions);
void paintEvent(PaintEvent& a_event);
};
END_NAMESPACE