1.8
Creating New Widgets

This chapter discusses how to extend and customize widgets.

Before creating new widget types, make sure you understand Theme to know what you can already change about how a widget looks and operates. You can change colors, fonts, and even the complete draw function of existing widget instances and types without having to create custom widget types.

EGT is designed to support extension by way of custom widgets and functionality. Essentially, to change the behavior or look and feel of an existing widget, create a derived class of a similar and existing widget and reimplement the functions that should be changed. If you are creating a completely new type of widget, you would inherit directly from Widget or one of the other base widget types.

An example of this happening is fluent throughout the EGT library itself. For example, egt::CheckBox implements a standard traditional checkBox control that shows an X when the checkBox is egt::v1::CheckBox::checked(). However, to change the look and keep the same logical operation, egt::ToggleBox derives from egt::CheckBox and effectively changes the draw() method.

The new widget class that only intends to change the look of a widget would look something like this:

class ToggleBox : public CheckBox
{
public:
// standard draw function, which usually calls Drawer<T>::draw()
virtual void draw(Painter& painter, const Rect& rect) override
{
Drawer<ToggleBox>::draw(*this, painter, rect);
}
// default_draw() function to do real drawing which allows this widget to be themeable
static void default_draw(ToggleBox& widget, Painter& painter, const Rect& rect);
};
RectType< DefaultDim, detail::Compatible::normal > Rect
Helper type alias.
Definition: geometry.h:1016

This new widget is basically a CheckBox, but we want to change how it looks. The new ToggleBox::default_draw() method looks like this:

widget.draw_box(painter, Palette::ColorId::bg, Palette::ColorId::border);
auto b = widget.content_area();
if (widget.checked())
{
Rect rect = b;
rect.width(rect.width() / 2);
rect.x(rect.x() + rect.width());
widget.theme().draw_box(painter,
Theme::FillFlag::blend,
rect,
widget.color(Palette::ColorId::border),
widget.color(Palette::ColorId::button_bg),
0,
0,
widget.border_radius());
}
else
{
Rect rect = b;
rect.width(rect.width() / 2);
if (widget.enable_disable())
{
widget.theme().draw_box(painter,
Theme::FillFlag::blend,
rect,
widget.color(Palette::ColorId::border, Palette::GroupId::disabled),
widget.color(Palette::ColorId::button_bg, Palette::GroupId::disabled),
0,
0,
widget.border_radius());
}
else
{
widget.theme().draw_box(painter,
Theme::FillFlag::blend,
rect,
widget.color(Palette::ColorId::border),
widget.color(Palette::ColorId::button_bg),
0,
0,
widget.border_radius());
}
}
if (!widget.on_text().empty())
{
Rect rect = b;
rect.width(rect.width() / 2);
rect.x(rect.x() + rect.width());
if (widget.checked())
painter.set(widget.color(Palette::ColorId::button_text));
else
painter.set(widget.color(Palette::ColorId::button_text, Palette::GroupId::disabled));
painter.set(widget.font());
auto size = painter.text_size(widget.on_text());
Rect target = detail::align_algorithm(size,
rect,
AlignFlag::center);
painter.draw(target.point());
painter.draw(widget.on_text());
}
if (!widget.off_text().empty())
{
Rect rect = b;
rect.width(rect.width() / 2);
painter.set(widget.color(Palette::ColorId::button_text, Palette::GroupId::disabled));
painter.set(widget.font());
auto size = painter.text_size(widget.off_text());
Rect target = detail::align_algorithm(size,
rect,
AlignFlag::center);
painter.draw(target.point());
painter.draw(widget.off_text());
}
constexpr EGT_NODISCARD Dim width() const noexcept
Get the width value.
Definition: geometry.h:906
constexpr std::size_t size(const T(&)[N]) noexcept
Return the size of an array (c++17's std::size()).
Definition: utils.h:70