class WidgetInfoBase
{
public:
WidgetInfoBase(const char* a_decl) {}
};
#define CONCAT2(x,y) \
x##y
#define CONCAT(x,y) \
CONCAT2(x,y)
#define DECLARE_WIDGET_INFO(name, x) \
struct name : public WidgetInfoBase { \
name() : WidgetInfoBase(x) {} \
} CONCAT(g_register,name);
#define DECLARE_WIDGET(decl) \
decl; \
DECLARE_WIDGET_INFO( CONCAT(WidgetInfo,__COUNTER__) , #decl)
#include "CommonWidgets.h"
#include "Painter.h"
BEGIN_NAMESPACE
enum ButtonState
{
DisabledState,
NormalState,
DefaultButtonState,
HoverState,
DepressedState
};
CheckableWidget::CheckableWidget(Widget* a_parent, AbstractProperty<bool>& a_value)
: AbstractValueWidget<bool>(a_parent, a_value), checked(a_value)
{
}
HBox::HBox(Widget* a_parent)
: Widget(a_parent, true, 0, 0, 10, 10)
{
}
VBox::VBox(Widget* a_parent)
: Widget(a_parent, false, 0, 0, 10, 10)
{
}
GroupBoxSpacer::GroupBoxSpacer(Widget* a_parent)
: Widget(a_parent, false, 0, 0, 10, 10)
{
}
void GroupBoxSpacer::sizeOptions(SizeOptions& a_sizeOptions)
{
SizeOptions opts = { { 1, 8 }, { 1, 8 }, { 65536, 8 } };
a_sizeOptions = opts;
}
GroupBox::GroupBox(Widget* a_parent, AbstractProperty<String>& a_title)
: AbstractValueWidget<String>(a_parent, a_title), title(a_title), m_titleSpace(this)
{
layoutMargin = 15;
}
void GroupBox::paintEvent(PaintEvent& a_event)
{
Painter p(this);
String str = title();
int w, h;
p.setPen(0x000000);
p.setFontFamily("Segoe UI"); // "MS Shell Dlg 2" "MS Shell Dlg"
p.setFontSize(15);
p.textExtents(w, h, str.data());
p.drawText(15, 0, str.data());
p.setPen(0xC0C0C0);
p.drawLine(3, h/2, 12, h/2);
p.drawLine(3, h/2, 3, height() - 3);
p.drawLine(3, height() - 3, width() -3, height() - 3);
p.drawLine(width() -3, h/2, width() -3, height() - 3);
p.drawLine(w + 15 + 3, h/2, width() -3, h/2);
}
HSpace::HSpace(Widget* a_parent)
: Widget(a_parent, true, 0, 0, 10, 10)
{
}
void HSpace::sizeOptions(SizeOptions& a_sizeOptions)
{
SizeOptions opts = { { 64, 10 }, { 128, 10 }, { 65536, 65536 } };
a_sizeOptions = opts;
}
VSpace::VSpace(Widget* a_parent)
: Widget(a_parent, false, 0, 0, 10, 10)
{
}
void VSpace::sizeOptions(SizeOptions& a_sizeOptions)
{
SizeOptions opts = { { 10, 64 }, { 10, 128 }, { 65536, 65536 } };
a_sizeOptions = opts;
}
Label::Label(Widget* a_parent, AbstractProperty<String>& a_text)
: AbstractValueWidget<String>(a_parent, a_text), text(a_text)
{
flags = WF_EraseBackground;
//connect(text.valueChanged, this, &Widget::eraseBackground);
disabled = false;
}
void Label::sizeOptions(SizeOptions& a_sizeOptions)
{
int len = (int)text().toUtf8().size();
SizeOptions opts = { { len*8, 20 }, { len*16+32, 32 }, { len*16+300, 48 } };
a_sizeOptions = opts;
}
void Label::paintEvent(PaintEvent& a_event)
{
Painter p(this);
p.setPen(disabled() ? 0xFF838383 : 0xFF000000);
p.setFontFamily("Segoe UI"); // "MS Shell Dlg 2" "MS Shell Dlg"
p.setFontSize(15);
p.drawText(1, 1, text().data());
}
Button::Button(Widget* a_parent, const char* a_text)
: Widget(a_parent, false, 0, 0, 100, 32)
{
text = a_text;
m_mouseDownInButton = false;
m_mouseOverButton = false;
disabled = false;
isDefault = false;
hasFocus = false;
// If any properties change, refresh the button
connect(text.valueChanged, this, &Button::refresh);
connect(disabled.valueChanged, this, &Button::refresh);
connect(isDefault.valueChanged, this, &Button::refresh);
connect(hasFocus.valueChanged, this, &Button::refresh);
}
void Button::refresh(Variant& newValue)
{
repaint();
}
void Button::sizeOptions(SizeOptions& a_sizeOptions)
{
int len = (int)text().toUtf8().size();
SizeOptions opts = { { len*8, 20 }, { len*12+16, 21 }, { len*12+32, 24 } };
a_sizeOptions = opts;
}
void Button::paintEvent(PaintEvent& a_event)
{
Painter p(this);
bool drawFocusRect = !disabled() && hasFocus();
ButtonState state = NormalState;
if (disabled())
state = DisabledState;
else if (m_mouseOverButton && m_mouseDownInButton)
state = DepressedState;
else if (m_mouseOverButton != m_mouseDownInButton)
state = HoverState;
else if (isDefault() || hasFocus())
state = DefaultButtonState;
uint32_t bgA1Cols[5] = { 0xefefef, 0xf0f0f0, 0xf0f0f0, 0xecf4fc, 0xdaecfc };
uint32_t bgB1Cols[5] = { 0xefefef, 0xe5e5e5, 0xe5e5e5, 0xdcecfc, 0xc4e0fc };
uint32_t edgeCols[5] = { 0xd9d9d9, 0xacacac, 0x3399ff, 0x7eb4ea, 0x569de5 };
uint32_t textCols[5] = { 0x838383, 0x000000, 0x000000, 0x000000, 0x000000 };
p.setBrush(edgeCols[(int)state]);
p.drawRectangle(0, 0, width(), height());
//p.setBrush(bgA1Cols[(int)state]);
//p.drawRectangle(1, 1, width() - 2, height() - 2);
CUTE_Gradient gradient = { CUTE_LINEAR_GRADIENT, bgA1Cols[(int)state], bgB1Cols[(int)state], 0, { { 1, 0, 1 } } };
gradient.m_data.m_linear.m_y2 = height() - 1;
p.drawGradient(1, 1, gradient, width() - 2, height() - 2);
p.setPen(textCols[(int)state]);
p.setFontFamily("Segoe UI"); // "MS Shell Dlg 2" "MS Shell Dlg"
p.setFontSize(15);
int textWidth, textheight;
p.textExtents(textWidth, textheight, text().data());
p.drawText((width() - textWidth) / 2, (height() - textheight) / 2, text().data());
if (drawFocusRect)
p.drawFocusRectangle(2, 2, width() - 4, height() - 4);
}
void Button::mouseEnterEvent(MouseEvent& a_event)
{
m_mouseOverButton = true;
repaint();
}
void Button::mouseLeaveEvent(MouseEvent& a_event)
{
m_mouseOverButton = false;
repaint();
}
void Button::mouseEvent(MouseEvent& a_event)
{
if (!a_event.m_oldButtons && a_event.m_buttons)
m_mouseDownInButton = true;
if (a_event.m_oldButtons && !a_event.m_buttons)
{
// mouse up while in button
if (m_mouseDownInButton && m_mouseOverButton && !disabled())
activated();
}
if (!a_event.m_buttons)
m_mouseDownInButton = false;
if (a_event.m_oldButtons != a_event.m_buttons)
repaint();
}
struct GimpExportedImage
{
unsigned int width;
unsigned int height;
unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */
unsigned char pixel_data[11 * 11 * 4 + 1];
};
static const GimpExportedImage g_checkBoxTickImage =
{
11, 11, 4,
"\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
"\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
"\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
"\0\377\377\377\0\377\377\377\0\377\377\377\0\177\177\177\200\337\337\337"
"\40\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
"\377\377\377\0\377\377\377\0\377\377\377\0\257\257\257P\0\0\0\377\17\17\17"
"\360\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
"\0\377\377\377\0\377\377\377\0\357\357\357\20\17\17\17\360\0\0\0\377\177"
"\177\177\200\377\377\377\0\377\377\377\0\377\377\377\0\177\177\177\200\317"
"\317\3170\377\377\377\0\377\377\377\0OOO\260\0\0\0\377///\320\377\377\377"
"\0\377\377\377\0\377\377\377\0\237\237\237`\0\0\0\377\17\17\17\360\317\317"
"\3170\257\257\257P\0\0\0\377\0\0\0\377\317\317\3170\377\377\377\0\377\377"
"\377\0\377\377\377\0\357\357\357\20///\320\0\0\0\377\0\0\0\377\17\17\17\360"
"\0\0\0\377\177\177\177\200\377\377\377\0\377\377\377\0\377\377\377\0\377"
"\377\377\0\377\377\377\0\357\357\357\20???\300\0\0\0\377\0\0\0\377///\320"
"\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
"\377\377\0\377\377\377\0\377\377\377\0___\240\0\0\0\377\317\317\3170\377"
"\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
"\377\0\377\377\377\0\377\377\377\0\377\377\377\0\317\317\3170\377\377\377"
"\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
"\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
"\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0",
};
static const GimpExportedImage g_comboBoxHandleImage =
{
7, 4, 4,
"\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\352"
"\352\352\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\352\352\352"
"\0\352\352\352\0\352\352\352\0\0\0\0\377\0\0\0\377\0\0\0\377\352\352\352"
"\0\352\352\352\0\352\352\352\0\352\352\352\0\352\352\352\0\0\0\0\377\352"
"\352\352\0\352\352\352\0\352\352\352\0",
};
static void drawGimpExportedImage(Painter& p, int x, int y, const GimpExportedImage& img)
{
p.drawPixelBuffer(x, y, img.pixel_data, img.width, img.height, img.bytes_per_pixel);
}
CheckBox::CheckBox(Widget* a_parent, AbstractProperty<bool>& a_checked, const char* a_text)
: CheckableWidget(a_parent, a_checked)
{
text = a_text;
m_mouseDownInButton = false;
m_mouseOverButton = false;
disabled = false;
isDefault = false;
hasFocus = false;
checked = false;
}
void CheckBox::sizeOptions(SizeOptions& a_sizeOptions)
{
int len = (int)text().toUtf8().size();
SizeOptions opts = { { len*8+16, 20 }, { len*12+24, 21 }, { len*12+32, 24 } };
a_sizeOptions = opts;
}
void CheckBox::paintEvent(PaintEvent& a_event)
{
Painter p(this);
bool drawFocusRect = !disabled() && hasFocus();
ButtonState state = NormalState;
if (disabled())
state = DisabledState;
else if (m_mouseOverButton && m_mouseDownInButton)
state = DepressedState;
else if (m_mouseOverButton != m_mouseDownInButton)
state = HoverState;
//else if (isDefault() || hasFocus())
//state = DefaultButtonState;
uint32_t bgA1Cols[5] = { 0xe6e6e6, 0xffffff, 0, 0xf3f9ff, 0xd9ecff };
uint32_t edgeCols[5] = { 0xbcbcbc, 0x707070, 0, 0x3399ff, 0x007cde };
uint32_t textCols[5] = { 0x838383, 0x000000, 0, 0x000000, 0x000000 };
p.setBrush(edgeCols[(int)state]);
p.drawRectangle(3, 3, 13, 13);
p.setBrush(bgA1Cols[(int)state]);
p.drawRectangle(4, 4, 11, 11);
if (checked())
drawGimpExportedImage(p, 4, 4, g_checkBoxTickImage);
p.setPen(textCols[(int)state]);
p.setFontFamily("Segoe UI"); // "MS Shell Dlg 2" "MS Shell Dlg"
p.setFontSize(15);
p.drawText(18, 2, text().data());
if (drawFocusRect)
{
int textWidth, textheight;
p.textExtents(textWidth, textheight, text().data());
p.drawFocusRectangle(18, 2, textWidth + 1, textheight);
}
}
void CheckBox::toggle()
{
printf("check box toggled\n");
checked = !checked();
}
void CheckBox::mouseEnterEvent(MouseEvent& a_event)
{
m_mouseOverButton = true;
repaint();
}
void CheckBox::mouseLeaveEvent(MouseEvent& a_event)
{
m_mouseOverButton = false;
repaint();
}
void CheckBox::mouseEvent(MouseEvent& a_event)
{
if (!a_event.m_oldButtons && a_event.m_buttons)
m_mouseDownInButton = true;
if (a_event.m_oldButtons && !a_event.m_buttons)
{
// mouse up while in button
if (m_mouseDownInButton && m_mouseOverButton && !disabled())
toggle();
}
if (!a_event.m_buttons)
m_mouseDownInButton = false;
if (a_event.m_oldButtons != a_event.m_buttons)
repaint();
}
RadioButton::RadioButton(Widget* a_parent, AbstractProperty<bool>& a_checked, const char* a_text)
: CheckableWidget(a_parent, a_checked)
{
text = a_text;
m_mouseDownInButton = false;
m_mouseOverButton = false;
disabled = false;
isDefault = false;
hasFocus = false;
checked = false;
}
void RadioButton::sizeOptions(SizeOptions& a_sizeOptions)
{
int len = (int)text().toUtf8().size();
SizeOptions opts = { { len*8, 20 }, { len*12+16, 21 }, { len*12+32, 24 } };
a_sizeOptions = opts;
}
void RadioButton::paintEvent(PaintEvent& a_event)
{
Painter p(this);
bool drawFocusRect = !disabled() && hasFocus();
ButtonState state = NormalState;
if (disabled())
state = DisabledState;
else if (m_mouseOverButton && m_mouseDownInButton)
state = DepressedState;
else if (m_mouseOverButton != m_mouseDownInButton)
state = HoverState;
//else if (isDefault() || hasFocus())
//state = DefaultButtonState;
uint32_t bgA1Cols[5] = { 0xe6e6e6, 0xffffff, 0, 0xf3f9ff, 0xd9ecff };
uint32_t edgeCols[5] = { 0xbcbcbc, 0x707070, 0, 0x3399ff, 0x007cde };
uint32_t textCols[5] = { 0x838383, 0x000000, 0, 0x000000, 0x000000 };
p.setBrush(0xff000000 | edgeCols[(int)state]);
p.drawEllipse(3, 3, 13, 13);
p.setBrush(0xff000000 | bgA1Cols[(int)state]);
p.drawEllipse(4, 4, 11, 11);
if (checked()) {
p.setBrush(0xff000000 | textCols[(int)state]);
p.drawEllipse(6, 6, 6, 6);
}
p.setPen(textCols[(int)state]);
p.setFontFamily("Segoe UI"); // "MS Shell Dlg 2" "MS Shell Dlg"
p.setFontSize(15);
p.drawText(18, 2, text().data());
if (drawFocusRect)
{
int textWidth, textheight;
p.textExtents(textWidth, textheight, text().data());
p.drawFocusRectangle(18, 2, textWidth + 1, textheight);
}
}
void RadioButton::toggle()
{
printf("radio button toggled\n");
if (!checked())
checked = true; // Only set to on from clicks, off happens from selecting a different radio button
//checked = !checked();
}
void RadioButton::mouseEnterEvent(MouseEvent& a_event)
{
m_mouseOverButton = true;
repaint();
}
void RadioButton::mouseLeaveEvent(MouseEvent& a_event)
{
m_mouseOverButton = false;
repaint();
}
void RadioButton::mouseEvent(MouseEvent& a_event)
{
if (!a_event.m_oldButtons && a_event.m_buttons)
m_mouseDownInButton = true;
if (a_event.m_oldButtons && !a_event.m_buttons)
{
// mouse up while in button
if (m_mouseDownInButton && m_mouseOverButton && !disabled())
toggle();
}
if (!a_event.m_buttons)
m_mouseDownInButton = false;
if (a_event.m_oldButtons != a_event.m_buttons)
repaint();
}
LineEdit::LineEdit(Widget* a_parent, AbstractProperty<String>& a_text)
: AbstractValueWidget<String>(a_parent, a_text), text(a_text)
{
m_mouseOver = false;
disabled = false;
hasFocus = false;
m_carrotPosition = 0;
// If any properties change, refresh the button
connect(text.valueChanged, this, &LineEdit::refresh);
connect(disabled.valueChanged, this, &LineEdit::refresh);
connect(hasFocus.valueChanged, this, &LineEdit::refresh);
m_timerId = startTimer(500);
}
void LineEdit::sizeOptions(SizeOptions& a_sizeOptions)
{
int len = (int)text().toUtf8().size();
SizeOptions opts = { { len*8, 24 }, { len*12+16, 28 }, { len*12+32, 32 } };
a_sizeOptions = opts;
}
void LineEdit::timerEvent(TimerEvent& a_event)
{
if (a_event.m_timerId == m_timerId)
{
m_carrotOn = !m_carrotOn;
repaint();
}
}
void LineEdit::keyEvent(KeyEvent& a_event)
{
if ( a_event.m_state != KS_Pressed )
return;
std::string t = text().toUtf8();
if ( a_event.m_key == Key_BackSpace )
{
if ( m_carrotPosition )
{
m_carrotPosition--;
std::string t2;
if (m_carrotPosition != t.size())
t2 = t.substr(m_carrotPosition+1, -1);
text = t.substr(0, m_carrotPosition) + t2;
}
}
else if ( a_event.m_key == Key_Delete )
{
if ( m_carrotPosition != t.size() )
text = t.substr(0, m_carrotPosition) +
t.substr(m_carrotPosition+1, -1);
}
else if ( a_event.m_key == Key_Left )
m_carrotPosition--;
else if ( a_event.m_key == Key_Right )
m_carrotPosition++;
else if ( a_event.m_key >= 'a' || a_event.m_key <= 'z' )
{
if ( m_carrotPosition != t.size() )
text = t.substr(0, m_carrotPosition) +
char(a_event.m_key) + t.substr(m_carrotPosition, -1);
else
text = t + char(a_event.m_key);
m_carrotPosition++;
}
m_carrotPosition = clamp<int>(m_carrotPosition, 0, (int)text().toUtf8().size());
m_carrotOn = true;
repaint();
}
void LineEdit::paintEvent(PaintEvent& a_event)
{
Painter p(this);
// bool drawFocusRect = !disabled() && hasFocus();
int state = 1;//NormalState;
if (disabled())
state = 0;//DisabledState;
else if (m_mouseOver || hasFocus())
state = 2;//ActiveState;
uint32_t edgeCols[5] = { 0xd9d9d9, 0xacacac, 0x3399ff, 0x7eb4ea, 0x569de5 };
uint32_t textCols[5] = { 0x838383, 0x000000, 0x000000, 0x000000, 0x000000 };
p.setBrush(edgeCols[(int)state]);
p.drawRectangle(0, 0, width(), height());
p.setBrush(0xffffff);
p.drawRectangle(1, 1, width() - 2, height() - 2);
p.setPen(textCols[(int)state]);
p.setFontFamily("Segoe UI"); // "MS Shell Dlg 2" "MS Shell Dlg"
p.setFontSize(15);
int textWidth, textheight;
p.textExtents(textWidth, textheight, text().data());
p.drawText(6, (height() - textheight) / 2, text().data());
int carrotX = 0;
p.textExtents(carrotX, textheight, text().toUtf8().substr(0, m_carrotPosition).c_str());
if ( m_carrotOn )
p.drawLine(carrotX + 6, 4, carrotX + 6, height() - 4);
}
void LineEdit::mouseEnterEvent(MouseEvent& a_event)
{
m_mouseOver = true;
repaint();
}
void LineEdit::mouseLeaveEvent(MouseEvent& a_event)
{
m_mouseOver = false;
repaint();
}
void LineEdit::mouseEvent(MouseEvent& a_event)
{
if (!a_event.m_oldButtons && a_event.m_buttons)
{
hasFocus = true;
}
}
static bool isPointInsideRectangle(const Point& a_point, const Rectangle& a_rect)
{
if ( a_point.m_x > a_rect.m_x
&& a_point.m_x < (a_rect.m_x + a_rect.m_width)
&& a_point.m_y > a_rect.m_y
&& a_point.m_y < (a_rect.m_y + a_rect.m_height) )
return true;
return false;
}
Slider::Slider(Widget* a_parent, AbstractProperty<int>& a_sliderValue)
: AbstractValueWidget<int>(a_parent, a_sliderValue)
{
value = 0;
disabled = false;
m_mouseOverSliderHandle = false;
m_mouseDownInSliderHandle = false;
hasFocus = false;
}
void Slider::sizeOptions(SizeOptions& a_sizeOptions)
{
SizeOptions opts = { { 32, 21 }, { 128, 21 }, { 65536, 21 } };
a_sizeOptions = opts;
}
void Slider::paintEvent(PaintEvent& a_event)
{
Painter p(this);
// bool drawFocusRect = !disabled() && hasFocus();
ButtonState state = NormalState;
if (disabled())
state = DisabledState;
else if (m_mouseOverSliderHandle && m_mouseDownInSliderHandle)
state = DepressedState;
else if (m_mouseOverSliderHandle != m_mouseDownInSliderHandle)
state = HoverState;
else if (hasFocus())
state = DefaultButtonState;
uint32_t bgA1Cols[5] = { 0xefefef, 0xf0f0f0, 0xf0f0f0, 0xecf4fc, 0xdaecfc };
uint32_t bgB1Cols[5] = { 0xefefef, 0xe5e5e5, 0xe5e5e5, 0xdcecfc, 0xc4e0fc };
uint32_t edgeCols[5] = { 0xd9d9d9, 0xacacac, 0x3399ff, 0x7eb4ea, 0x569de5 };
uint32_t textCols[5] = { 0x838383, 0x000000, 0x000000, 0x000000, 0x000000 };
// Draw the groove
p.setBrush(0xd9d9d9);
p.drawRectangle(0, (height()-4)/2, width(), 4);
p.setBrush(0xf0f0f0);
p.drawRectangle(1, ((height()-4)/2)+1, width()-2, 2);
// Draw the handle
int pixelPos = value();
p.setBrush(edgeCols[(int)state]);
p.drawRectangle(pixelPos, 0, 10, height());
CUTE_Gradient gradient = { CUTE_LINEAR_GRADIENT, bgA1Cols[(int)state], bgB1Cols[(int)state], 0, { { 1, 0, 1 } } };
gradient.m_data.m_linear.m_y2 = height() - 1;
p.drawGradient(pixelPos+1, 1, gradient, 8, height() - 2);
}
Rectangle Slider::getSliderHandleRectangle()
{
Rectangle rect = { {{ value(), 0 }}, {{ 8, height() }} };
return rect;
}
void Slider::mouseLeaveEvent(MouseEvent& a_event)
{
m_mouseOverSliderHandle = false;
repaint();
}
void Slider::mouseEvent(MouseEvent& a_event)
{
m_mouseOverSliderHandle = isPointInsideRectangle(a_event.m_position, getSliderHandleRectangle());
if (!a_event.m_buttons)
m_mouseDownInSliderHandle = false;
if (a_event.m_oldButtons && a_event.m_buttons && m_mouseDownInSliderHandle)
{
int newValue = value() + a_event.m_x - a_event.m_oldX;
value = clamp<int>(newValue, 0, width()-10);
}
if (!a_event.m_oldButtons && a_event.m_buttons)
{
m_mouseDownInSliderHandle = m_mouseOverSliderHandle;
// TODO: focus rect is only when tab key is pressed and tabbing through widgets
hasFocus = true;
}
repaint();
}
ComboBox::ComboBox(Widget* a_parent, AbstractProperty<int>& a_value)
: AbstractValueWidget<int>(a_parent, a_value), currentIndex(a_value)
{
value = 0;
text = "";
m_mouseDownInButton = false;
m_mouseOverButton = false;
disabled = false;
isDefault = false;
hasFocus = false;
// If any properties change, refresh the button
connect(text.valueChanged, this, &ComboBox::refresh);
connect(disabled.valueChanged, this, &ComboBox::refresh);
connect(isDefault.valueChanged, this, &ComboBox::refresh);
connect(hasFocus.valueChanged, this, &ComboBox::refresh);
}
void ComboBox::refresh(Variant& newValue)
{
repaint();
}
void ComboBox::sizeOptions(SizeOptions& a_sizeOptions)
{
int len = (int)text().toUtf8().size();
SizeOptions opts = { { len*8, 24 }, { len*12+16, 24 }, { 65536, 24 } };
a_sizeOptions = opts;
}
void ComboBox::paintEvent(PaintEvent& a_event)
{
Painter p(this);
bool drawFocusRect = !disabled() && hasFocus();
ButtonState state = NormalState;
if (disabled())
state = DisabledState;
else if (m_mouseOverButton && m_mouseDownInButton)
state = DepressedState;
else if (m_mouseOverButton != m_mouseDownInButton)
state = HoverState;
else if (isDefault() || hasFocus())
state = DefaultButtonState;
uint32_t bgA1Cols[5] = { 0xefefef, 0xf0f0f0, 0xf0f0f0, 0xecf4fc, 0xdaecfc };
uint32_t bgB1Cols[5] = { 0xefefef, 0xe5e5e5, 0xe5e5e5, 0xdcecfc, 0xc4e0fc };
uint32_t edgeCols[5] = { 0xd9d9d9, 0xacacac, 0x3399ff, 0x7eb4ea, 0x569de5 };
uint32_t textCols[5] = { 0x838383, 0x000000, 0x000000, 0x000000, 0x000000 };
p.setBrush(edgeCols[(int)state]);
p.drawRectangle(0, 0, width(), height());
CUTE_Gradient gradient = { CUTE_LINEAR_GRADIENT, bgA1Cols[(int)state], bgB1Cols[(int)state], 0, { { 1, 0, 1 } } };
gradient.m_data.m_linear.m_y2 = height() - 1;
p.drawGradient(1, 1, gradient, width() - 2, height() - 2);
p.setPen(textCols[(int)state]);
p.setFontFamily("Segoe UI"); // "MS Shell Dlg 2" "MS Shell Dlg"
p.setFontSize(15);
int textWidth, textheight;
p.textExtents(textWidth, textheight, text().data());
p.drawText(4, (height() - textheight) / 2, text().data());
drawGimpExportedImage(p, width() - 14, (height()-3)/2, g_comboBoxHandleImage);
if (drawFocusRect)
p.drawFocusRectangle(2, 2, width() - 4, height() - 4);
}
void ComboBox::mouseEnterEvent(MouseEvent& a_event)
{
m_mouseOverButton = true;
repaint();
}
void ComboBox::mouseLeaveEvent(MouseEvent& a_event)
{
m_mouseOverButton = false;
repaint();
}
void ComboBox::mouseEvent(MouseEvent& a_event)
{
if (!a_event.m_oldButtons && a_event.m_buttons)
m_mouseDownInButton = true;
if (a_event.m_oldButtons && !a_event.m_buttons)
{
// mouse up while in button
if (m_mouseDownInButton && m_mouseOverButton && !disabled())
activated();
}
if (!a_event.m_buttons)
m_mouseDownInButton = false;
if (a_event.m_oldButtons != a_event.m_buttons)
repaint();
}
void ComboBox::addItem(String a_itemText)
{
m_items.push_back(a_itemText);
if (m_items.size() >= (size_t)value())
text = m_items[value()];
}
void ComboBox::keyEvent(KeyEvent& a_event)
{
if ( a_event.m_state != KS_Pressed )
return;
int item = value();
if ( a_event.m_key == Key_Up )
item--;
if ( a_event.m_key == Key_Down )
item++;
value = clamp<int>(item, 0, (int)m_items.size() - 1);
text = m_items[value()];
repaint();
}
ProgressBar::ProgressBar(Widget* a_parent, AbstractProperty<int>& a_value)
: AbstractValueWidget<int>(a_parent, a_value), progress(a_value)
{
value = 30;
// If any properties change, refresh the button
connect(progress.valueChanged, this, &ProgressBar::refresh);
}
void ProgressBar::refresh(Variant& newValue)
{
repaint();
}
void ProgressBar::sizeOptions(SizeOptions& a_sizeOptions)
{
SizeOptions opts = { { 64, 26 }, { 128, 26 }, { 65536, 26 } };
a_sizeOptions = opts;
}
void ProgressBar::paintEvent(PaintEvent& a_event)
{
Painter p(this);
uint32_t border = 0xbcbcbc;
uint32_t progressCol = 0x06b025;
uint32_t incompleteCol = 0xe6e6e6;
int valueInPixels = (value() * width()) / 100;
p.setBrush(border);
p.drawRectangle(0, 0, width(), height());
p.setBrush(progressCol);
p.drawRectangle(1, 1, valueInPixels, height()-2);
p.setBrush(incompleteCol);
p.drawRectangle(valueInPixels+1, 1, width()-valueInPixels-2, height()-2);
}
END_NAMESPACE