From c89e4456682f65bdca5c97efbb4033eec7ec501c Mon Sep 17 00:00:00 2001 From: Linloir <3145078758@qq.com> Date: Mon, 19 Dec 2022 17:05:36 +0800 Subject: [PATCH] [UI][ADD] Slider widget - Non-linear value transformation - Color scheme selection - Redesigned signals --- FinalProject/slider.cpp | 288 ++++++++++++++++++++++++++++++++++++++++ FinalProject/slider.h | 72 ++++++++++ 2 files changed, 360 insertions(+) create mode 100644 FinalProject/slider.cpp create mode 100644 FinalProject/slider.h diff --git a/FinalProject/slider.cpp b/FinalProject/slider.cpp new file mode 100644 index 0000000..2a154fc --- /dev/null +++ b/FinalProject/slider.cpp @@ -0,0 +1,288 @@ +#include + +#include "slider.h" + +Slider::Slider(float min, float max, int step, QWidget* parent) : + QWidget(parent), _min(min), _max(max), _step(step) +{ + // Set map functions + _transformFunc = [this](float x) { + // Map x of [0, step] to [_min, _max] + float y = x / _step * (_max - _min) + _min; + return y; + }; + _inversionFunc = [this](float y) { + // Map y of [_min, _max] to [0, step] + float x = (y - _min) / (_max - _min) * _step; + return x; + }; + // Generate colors + generateColor(_defaultSchemeColor); + // Create main layout + _mainLayout = new QHBoxLayout(this); + _mainLayout->setContentsMargins(4, 4, 4, 4); + _mainLayout->setSpacing(0); + setLayout(_mainLayout); + // Create slider + _slider = new QSlider(Qt::Horizontal, this); + _slider->setMinimum(0); + _slider->setMaximum(_step); + _slider->setSingleStep(1); + // Set slider style sheet + QString grooveStyle = "QSlider::groove:horizontal {" + "height:6px;" + "border-radius:3px;" + "}"; + QString sliderStyle = "QSlider::handle:horizontal {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _handleColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString sliderHoverStyle = "QSlider::handle:horizontal:hover {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _hoverColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString sliderPressStyle = "QSlider::handle:horizontal:pressed {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _pressColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString subStyle = "QSlider::sub-page:horizontal {" + "background:" + _subColor.name(QColor::HexArgb) + ";" + "border-radius:3px;" + "}"; + QString addStyle = "QSlider::add-page:horizontal {" + "background:" + _addColor.name(QColor::HexArgb) + ";" + "border-radius:3px;" + "}"; + _slider->setStyleSheet(grooveStyle + sliderStyle + sliderHoverStyle + sliderPressStyle + subStyle + addStyle); + // Create decrease button + _decreaseBtn = new PushButton(nullptr, this); + _decreaseBtn->setColorScheme(_defaultSchemeColor); + _decreaseBtn->setFixedSize(24, 24); + _decreaseBtn->setRadius(8); + _decreaseBtn->setMargin(0, 0, 0, 3); + _decreaseBtn->setIndicatorColor(QColor(255, 255, 255, 0)); + // Create decrease label + _decreaseIcon = new QLabel(_decreaseBtn); + _decreaseIcon->setFont(QFont("Font Awesome 6 Free Solid", 6)); + _decreaseIcon->setText("\uf068"); + _decreaseIcon->setAlignment(Qt::AlignCenter); + _decreaseBtn->setChildWidget(_decreaseIcon); + _decreaseIcon->show(); + // Create increase button + _increaseBtn = new PushButton(nullptr, this); + _increaseBtn->setColorScheme(_defaultSchemeColor); + _increaseBtn->setFixedSize(24, 24); + _increaseBtn->setRadius(8); + _increaseBtn->setMargin(0, 0, 0, 3); + _increaseBtn->setIndicatorColor(QColor(255, 255, 255, 0)); + // Create increase label + _increaseIcon = new QLabel(_increaseBtn); + _increaseIcon->setFont(QFont("Font Awesome 6 Free Solid", 6)); + _increaseIcon->setText("\uf067"); + _increaseIcon->setAlignment(Qt::AlignCenter); + _increaseBtn->setChildWidget(_increaseIcon); + _increaseIcon->show(); + // Add to main layout + _mainLayout->addWidget(_decreaseBtn); + _mainLayout->addSpacing(4); + _mainLayout->addWidget(_slider); + _mainLayout->addSpacing(4); + _mainLayout->addWidget(_increaseBtn); + _decreaseBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + _slider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + _increaseBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + _decreaseBtn->show(); + _slider->show(); + _increaseBtn->show(); + // Connect signals and slots + connect(_decreaseBtn, &PushButton::onClick, this, [this]() { + // Set current value + _slider->setValue(_slider->value() - 1); + emit onChanged(_transformFunc(_slider->value())); + emit onSetValue(_transformFunc(_slider->value())); + emit onDragEnd(); + }); + connect(_increaseBtn, &PushButton::onClick, this, [this]() { + // Set current value + _slider->setValue(_slider->value() + 1); + emit onChanged(_transformFunc(_slider->value())); + emit onSetValue(_transformFunc(_slider->value())); + emit onDragEnd(); + }); + connect(_slider, &QSlider::valueChanged, this, [this](int value) { + // Judge whether the slider is changed by dragging or function + if (_slider->isSliderDown()) { + // Value changed by user + emit onChanged(_transformFunc(value)); + } + emit onSetValue(_transformFunc(value)); + }); + connect(_slider, &QSlider::sliderPressed, this, &Slider::onDragStart); + connect(_slider, &QSlider::sliderReleased, this, &Slider::onDragEnd); +} + +Slider::~Slider() +{} + +void Slider::generateColor(QColor schemeColor) { + _subColor = schemeColor; + _addColor = QColor(216, 216, 216); + _handleColor = QColor(194, 194, 194); + _hoverColor = schemeColor.lighter(20); + float hoverBlendRatio = 0.2; + _hoverColor = QColor( + _hoverColor.red() * hoverBlendRatio + _handleColor.red() * (1 - hoverBlendRatio), + _hoverColor.green() * hoverBlendRatio + _handleColor.green() * (1 - hoverBlendRatio), + _hoverColor.blue() * hoverBlendRatio + _handleColor.blue() * (1 - hoverBlendRatio) + ); + _pressColor = schemeColor.lighter(20); + float pressBlendRatio = 0.5; + _pressColor = QColor( + _pressColor.red() * pressBlendRatio + _handleColor.red() * (1 - pressBlendRatio), + _pressColor.green() * pressBlendRatio + _handleColor.green() * (1 - pressBlendRatio), + _pressColor.blue() * pressBlendRatio + _handleColor.blue() * (1 - pressBlendRatio) + ); +} + +void Slider::setColorScheme(QColor color) { + generateColor(color); + // Change style sheet + QString grooveStyle = "QSlider::groove:horizontal {" + "height:6px;" + "border-radius:3px;" + "}"; + QString sliderStyle = "QSlider::handle:horizontal {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _handleColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString sliderHoverStyle = "QSlider::handle:horizontal:hover {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _hoverColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString sliderPressStyle = "QSlider::handle:horizontal:pressed {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _pressColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString subStyle = "QSlider::sub-page:horizontal {" + "background:" + _subColor.name(QColor::HexArgb) + ";" + "border-radius:3px;" + "}"; + QString addStyle = "QSlider::add-page:horizontal {" + "background:" + _addColor.name(QColor::HexArgb) + ";" + "border-radius:3px;" + "}"; + _slider->setStyleSheet(grooveStyle + sliderStyle + sliderHoverStyle + sliderPressStyle + subStyle + addStyle); + // Change button color + _decreaseBtn->setColorScheme(color); + _decreaseBtn->setIndicatorColor(QColor(255, 255, 255, 0)); + _increaseBtn->setColorScheme(color); + _increaseBtn->setIndicatorColor(QColor(255, 255, 255, 0)); +} + +void Slider::setMin(float min) { + _min = min; +} + +void Slider::setMax(float max) { + _max = max; +} + +void Slider::setStep(float step) { + _step = step; + _slider->setMaximum(step); +} + +void Slider::setValue(float value) { + _slider->setValue((int)_inversionFunc(value)); +} + +void Slider::setTransformation(std::function transformFunc, std::function inversionFunc) { + _transformFunc = transformFunc; + _inversionFunc = inversionFunc; +} + +void Slider::setEnabled(bool enabled) { + if (enabled == _enabled) { + return; + } + _enabled = enabled; + _slider->setEnabled(enabled); + _decreaseBtn->setEnabled(enabled); + _increaseBtn->setEnabled(enabled); + if (!enabled) { + // Store colors + _restoredColor[0] = _subColor; + _restoredColor[1] = _addColor; + _restoredColor[2] = _handleColor; + _restoredColor[3] = _hoverColor; + _restoredColor[4] = _pressColor; + // Change colors + setColorScheme(QColor(200, 200, 200)); + } + else { + // Restore colors + _subColor = _restoredColor[0]; + _addColor = _restoredColor[1]; + _handleColor = _restoredColor[2]; + _hoverColor = _restoredColor[3]; + _pressColor = _restoredColor[4]; + // Change style sheet + QString grooveStyle = "QSlider::groove:horizontal {" + "height:6px;" + "border-radius:3px;" + "}"; + QString sliderStyle = "QSlider::handle:horizontal {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _handleColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString sliderHoverStyle = "QSlider::handle:horizontal:hover {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _hoverColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString sliderPressStyle = "QSlider::handle:horizontal:pressed {" + "width:12px;" + "margin-bottom:-3px;" + "margin-top:-3px;" + "background:" + _pressColor.name(QColor::HexArgb) + ";" + "border-radius:6px;" + "}"; + QString subStyle = "QSlider::sub-page:horizontal {" + "background:" + _subColor.name(QColor::HexArgb) + ";" + "border-radius:3px;" + "}"; + QString addStyle = "QSlider::add-page:horizontal {" + "background:" + _addColor.name(QColor::HexArgb) + ";" + "border-radius:3px;" + "}"; + _slider->setStyleSheet(grooveStyle + sliderStyle + sliderHoverStyle + sliderPressStyle + subStyle + addStyle); + // Change button color + _decreaseBtn->setColorScheme(_subColor); + _decreaseBtn->setIndicatorColor(QColor(255, 255, 255, 0)); + _increaseBtn->setColorScheme(_subColor); + _increaseBtn->setIndicatorColor(QColor(255, 255, 255, 0)); + } +} diff --git a/FinalProject/slider.h b/FinalProject/slider.h new file mode 100644 index 0000000..fbbf212 --- /dev/null +++ b/FinalProject/slider.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include + +#include "pushbutton.h" + +class Slider : public QWidget +{ + Q_OBJECT + +public: + Slider(float min, float max, int step, QWidget* parent = 0); + ~Slider(); + +private: + // Settings + float _min; + float _max; + int _step; // step count of slider value + + // State + bool _enabled = true; + + // UI Settings + const QColor _defaultSchemeColor = QColor(58, 143, 183); + QColor _subColor; + QColor _addColor; + QColor _handleColor; + QColor _hoverColor; + QColor _pressColor; + QColor _restoredColor[5]; + + // UI Layout + QHBoxLayout* _mainLayout; + QSlider* _slider; + PushButton* _decreaseBtn; + QLabel* _decreaseIcon; + PushButton* _increaseBtn; + QLabel* _increaseIcon; + + // Transformation function + std::function _transformFunc; // Transform the slider value to actual value + std::function _inversionFunc; // Transform the actual value to slider value + +private: + void generateColor(QColor schemeColor); + +public: + // Getter APIs + QHBoxLayout* mainLayout() const { return _mainLayout; } + float val() const { return _transformFunc(_slider->value()); } + float lev() const { return _slider->value(); } + + // Setter APIs + void setColorScheme(QColor color); + void setMin(float min); + void setMax(float max); + void setStep(float max); + void setValue(float val); // Set the actual value + void setTransformation(std::function transform, std::function inverse); + + void setEnabled(bool enabled = true); + +signals: + void onChanged(float newVal); // Triggers only when user changes the value + void onSetValue(float newVal); // Triggers when user changes the value or setValue() is called + void onDragStart(); + void onDragEnd(); +};