diff --git a/src/ui/ToggleButton.cpp b/src/ui/ToggleButton.cpp
new file mode 100644
index 00000000..755f528f
--- /dev/null
+++ b/src/ui/ToggleButton.cpp
@@ -0,0 +1,211 @@
+#include <QApplication>
+#include <QColor>
+#include <QEvent>
+#include <QPainter>
+
+#include "ToggleButton.h"
+
+void
+Toggle::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+Toggle::Toggle(QWidget *parent)
+ : QAbstractButton{parent}
+{
+ init();
+
+ connect(this, &QAbstractButton::toggled, this, &Toggle::setState);
+}
+
+void
+Toggle::setState(bool isEnabled)
+{
+ thumb_->setShift(isEnabled ? Position::Right : Position::Left);
+ setupProperties();
+}
+
+void
+Toggle::init()
+{
+ track_ = new ToggleTrack(this);
+ thumb_ = new ToggleThumb(this);
+
+ setCursor(QCursor(Qt::PointingHandCursor));
+ setCheckable(true);
+ setChecked(false);
+
+ setState(false);
+ setupProperties();
+
+ QCoreApplication::processEvents();
+}
+
+void
+Toggle::setupProperties()
+{
+ if (isEnabled()) {
+ Position position = thumb_->shift();
+
+ thumb_->setThumbColor(trackColor());
+
+ if (position == Position::Left)
+ track_->setTrackColor(activeColor());
+ else if (position == Position::Right)
+ track_->setTrackColor(inactiveColor());
+ }
+
+ update();
+}
+
+void
+Toggle::setDisabledColor(const QColor &color)
+{
+ disabledColor_ = color;
+ setupProperties();
+}
+
+void
+Toggle::setActiveColor(const QColor &color)
+{
+ activeColor_ = color;
+ setupProperties();
+}
+
+void
+Toggle::setInactiveColor(const QColor &color)
+{
+ inactiveColor_ = color;
+ setupProperties();
+}
+
+void
+Toggle::setTrackColor(const QColor &color)
+{
+ trackColor_ = color;
+ setupProperties();
+}
+
+ToggleThumb::ToggleThumb(Toggle *parent)
+ : QWidget{parent}
+ , toggle_{parent}
+ , position_{Position::Right}
+ , offset_{0}
+{
+ parent->installEventFilter(this);
+}
+
+void
+ToggleThumb::setShift(Position position)
+{
+ if (position_ != position) {
+ position_ = position;
+ updateOffset();
+ }
+}
+
+bool
+ToggleThumb::eventFilter(QObject *obj, QEvent *event)
+{
+ const QEvent::Type type = event->type();
+
+ if (QEvent::Resize == type || QEvent::Move == type) {
+ setGeometry(toggle_->rect().adjusted(8, 8, -8, -8));
+ updateOffset();
+ }
+
+ return QWidget::eventFilter(obj, event);
+}
+
+void
+ToggleThumb::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ QBrush brush;
+ brush.setStyle(Qt::SolidPattern);
+ brush.setColor(toggle_->isEnabled() ? thumbColor_ : Qt::white);
+
+ painter.setBrush(brush);
+ painter.setPen(Qt::NoPen);
+
+ int s;
+ QRectF r;
+
+ s = height() - 10;
+ r = QRectF(5 + offset_, 5, s, s);
+
+ painter.drawEllipse(r);
+
+ if (!toggle_->isEnabled()) {
+ brush.setColor(toggle_->disabledColor());
+ painter.setBrush(brush);
+ painter.drawEllipse(r);
+ }
+}
+
+void
+ToggleThumb::updateOffset()
+{
+ const QSize s(size());
+ offset_ = position_ == Position::Left ? static_cast<qreal>(s.width() - s.height()) : 0;
+ update();
+}
+
+ToggleTrack::ToggleTrack(Toggle *parent)
+ : QWidget{parent}
+ , toggle_{parent}
+{
+ Q_ASSERT(parent);
+
+ parent->installEventFilter(this);
+}
+
+void
+ToggleTrack::setTrackColor(const QColor &color)
+{
+ trackColor_ = color;
+ update();
+}
+
+bool
+ToggleTrack::eventFilter(QObject *obj, QEvent *event)
+{
+ const QEvent::Type type = event->type();
+
+ if (QEvent::Resize == type || QEvent::Move == type) {
+ setGeometry(toggle_->rect());
+ }
+
+ return QWidget::eventFilter(obj, event);
+}
+
+void
+ToggleTrack::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ QBrush brush;
+ if (toggle_->isEnabled()) {
+ brush.setColor(trackColor_);
+ painter.setOpacity(0.8);
+ } else {
+ brush.setColor(toggle_->disabledColor());
+ painter.setOpacity(0.6);
+ }
+
+ brush.setStyle(Qt::SolidPattern);
+ painter.setBrush(brush);
+ painter.setPen(Qt::NoPen);
+
+ const int h = height() / 2;
+ const QRect r(0, h / 2, width(), h);
+ painter.drawRoundedRect(r.adjusted(14, 4, -14, -4), h / 2 - 4, h / 2 - 4);
+}
|