diff --git a/FinalProject/Deng.ttf b/FinalProject/Deng.ttf
new file mode 100644
index 0000000..1eb188e
Binary files /dev/null and b/FinalProject/Deng.ttf differ
diff --git a/FinalProject/Dengb.ttf b/FinalProject/Dengb.ttf
new file mode 100644
index 0000000..4873453
Binary files /dev/null and b/FinalProject/Dengb.ttf differ
diff --git a/FinalProject/Dengl.ttf b/FinalProject/Dengl.ttf
new file mode 100644
index 0000000..3d6ca1f
Binary files /dev/null and b/FinalProject/Dengl.ttf differ
diff --git a/FinalProject/FinalProject.vcxproj b/FinalProject/FinalProject.vcxproj
index d35a107..fa35487 100644
--- a/FinalProject/FinalProject.vcxproj
+++ b/FinalProject/FinalProject.vcxproj
@@ -114,6 +114,7 @@
+
@@ -127,6 +128,7 @@
+
@@ -157,8 +159,10 @@
+
+
@@ -167,7 +171,7 @@
-
+
diff --git a/FinalProject/FinalProject.vcxproj.filters b/FinalProject/FinalProject.vcxproj.filters
index 31e4ab0..9409877 100644
--- a/FinalProject/FinalProject.vcxproj.filters
+++ b/FinalProject/FinalProject.vcxproj.filters
@@ -174,6 +174,12 @@
Source Files\OpenGL Abstractions
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\Pages\Scene Editor\Object Setter
+
@@ -212,9 +218,6 @@
Header Files\Utils
-
- Header Files\Qt Widgets\Pages\Scene Editor\Object Setter
-
Header Files\OpenGL Abstractions
@@ -274,6 +277,15 @@
Header Files\Qt Widgets\Pages\Scene Editor\Object Selector
+
+ Header Files\Qt Widgets\Pages\Scene Editor\Object Setter
+
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\Pages\Scene Editor\Object Setter
+
diff --git a/FinalProject/corbel.ttf b/FinalProject/corbel.ttf
new file mode 100644
index 0000000..34fd5aa
Binary files /dev/null and b/FinalProject/corbel.ttf differ
diff --git a/FinalProject/editorpage.cpp b/FinalProject/editorpage.cpp
index fc720af..6dfdd98 100644
--- a/FinalProject/editorpage.cpp
+++ b/FinalProject/editorpage.cpp
@@ -36,6 +36,12 @@ EditorPage::EditorPage(QWidget* parent) :
_modelSelector = new ModelSelector(_mainWidget);
_mainLayout->addWidget(_modelSelector);
_modelSelector->show();
+
+ // Generate editing layout
+ _editingLayout = new QVBoxLayout(_mainWidget);
+ _editingLayout->setContentsMargins(0, 0, 0, 0);
+ _editingLayout->setSpacing(16);
+ _mainLayout->addLayout(_editingLayout);
// Generate scene viewer
_sceneViewerContainer = new RoundedCornerWidget(_mainWidget);
@@ -47,11 +53,27 @@ EditorPage::EditorPage(QWidget* parent) :
_sceneViewer = new SceneViewer(_sceneViewerContainer->mainWidget());
_sceneViewerContainerLayout->addWidget(_sceneViewer);
_sceneViewer->show();
- _mainLayout->addWidget(_sceneViewerContainer);
+ _editingLayout->addWidget(_sceneViewerContainer);
_sceneViewerContainer->show();
+ // Generate model setter
+ _modelSetter = new ModelSetter(_mainWidget);
+ _modelSetter->setMaximumHeight(150);
+ _editingLayout->addWidget(_modelSetter);
+ _modelSetter->show();
+ _modelSetter->setObjectName("ModelSetter");
+ _modelSetter->setStyleSheet("#ModelSetter { background-color: #f0f0f0; border-radius: 10px; }");
+
// Connect signals
connect(_modelSelector, &ModelSelector::onObjectSelected, _sceneViewer, &SceneViewer::addObject);
+ connect(_sceneViewer, &SceneViewer::onSelect, _modelSetter, &ModelSetter::update);
+ connect(_sceneViewer, &SceneViewer::onUpdate, _modelSetter, &ModelSetter::update);
+ connect(_modelSetter, &ModelSetter::onAdjustStart, _sceneViewer, &SceneViewer::setDragFlag);
+ connect(_modelSetter, &ModelSetter::onAdjustEnd, _sceneViewer, &SceneViewer::clearDragFlag);
+ connect(_modelSetter, &ModelSetter::onAdjust, this, [=]() {
+ _sceneViewer->update();
+ });
+ connect(_modelSetter, &ModelSetter::onDeleteObject, _sceneViewer, &SceneViewer::deleteObject);
}
EditorPage::~EditorPage() {}
diff --git a/FinalProject/editorpage.h b/FinalProject/editorpage.h
index acf41cf..3b28202 100644
--- a/FinalProject/editorpage.h
+++ b/FinalProject/editorpage.h
@@ -8,6 +8,7 @@
#include "sceneviewer.h"
#include "roundedcornerwidget.h"
#include "modelselector.h"
+#include "modelsetter.h"
class EditorPage : public PageWidget {
@@ -35,10 +36,14 @@ private:
ModelSelector* _modelSelector = nullptr;
+ QVBoxLayout* _editingLayout = nullptr;
+
RoundedCornerWidget* _sceneViewerContainer = nullptr;
QVBoxLayout* _sceneViewerContainerLayout = nullptr;
SceneViewer* _sceneViewer = nullptr;
+ ModelSetter* _modelSetter = nullptr;
+
public:
virtual PushButton* getPageIconButton(QWidget* context) override;
virtual PushButton* getPageTextButton(QWidget* context) override;
diff --git a/FinalProject/fragmentshader.glsl b/FinalProject/fragmentshader.glsl
index 648e6c4..11a251f 100644
--- a/FinalProject/fragmentshader.glsl
+++ b/FinalProject/fragmentshader.glsl
@@ -30,8 +30,8 @@ struct PointLight {
struct SpotLight {
vec3 position;
vec3 direction;
- float cutOff;
- float outerCutOff;
+ float innercutoff;
+ float outercutoff;
float constant;
float linear;
@@ -143,8 +143,8 @@ vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
// spotlight intensity
float theta = dot(lightDir, normalize(-light.direction));
- float epsilon = light.cutOff - light.outerCutOff;
- float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
+ float epsilon = light.innercutoff - light.outercutoff;
+ float intensity = clamp((theta - light.outercutoff) / epsilon, 0.0, 1.0);
// combine results
vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords));
diff --git a/FinalProject/main.cpp b/FinalProject/main.cpp
index 1b8bb85..ca8deb9 100644
--- a/FinalProject/main.cpp
+++ b/FinalProject/main.cpp
@@ -11,6 +11,10 @@ int main(int argc, char *argv[])
QFontDatabase::addApplicationFont(":/fonts/font_awesome_6_regular_free.otf"); // Add font awesome font to application
QFontDatabase::addApplicationFont(":/fonts/font_awesome_6_solid_free.otf"); // Add font awesome font to application
+ QFontDatabase::addApplicationFont(":/fonts/corbel.ttf"); // Add corbel font to application
+ QFontDatabase::addApplicationFont(":/fonts/Deng.ttf"); // Add Deng Xian font to application
+ QFontDatabase::addApplicationFont(":/fonts/Dengb.ttf"); // Add Deng Xian Bold font to application
+ QFontDatabase::addApplicationFont(":/fonts/Dengl.ttf"); // Add Deng Xian Light font to application
MainWindow w;
w.setMouseTracking(true);
diff --git a/FinalProject/mainwindow.qrc b/FinalProject/mainwindow.qrc
index 9b8663a..53f6c03 100644
--- a/FinalProject/mainwindow.qrc
+++ b/FinalProject/mainwindow.qrc
@@ -14,5 +14,9 @@
font_awesome_6_regular_free.otf
font_awesome_6_solid_free.otf
+ corbel.ttf
+ Deng.ttf
+ Dengb.ttf
+ Dengl.ttf
diff --git a/FinalProject/modelattrslide.cpp b/FinalProject/modelattrslide.cpp
new file mode 100644
index 0000000..2d839e9
--- /dev/null
+++ b/FinalProject/modelattrslide.cpp
@@ -0,0 +1,79 @@
+#include "modelattrslide.h"
+
+ModelAttributeSlide::ModelAttributeSlide(const QString& label, float min, float max, int step, QWidget* parent) :
+ QWidget(parent)
+{
+ // Create main stretch layout
+ _stretchLayout = new QHBoxLayout(this);
+ _stretchLayout->setContentsMargins(0, 0, 0, 0);
+ _stretchLayout->setSpacing(0);
+ setLayout(_stretchLayout);
+
+ // Create Slider
+ _slider = new Slider(min, max, step, this);
+ _stretchLayout->addWidget(_slider);
+ _slider->show();
+
+ // Create Label
+ _label = new QLabel(label, this);
+ _label->setMinimumWidth(56);
+ _label->setFont(QFont("Corbel", 11));
+ _label->show();
+ _slider->mainLayout()->insertWidget(0, _label);
+ _slider->mainLayout()->insertSpacing(1, 8);
+
+ // Create Value label
+ _val = new QLabel(this);
+ _val->setMinimumWidth(32);
+ _val->setFont(QFont("Corbel", 11));
+ _val->setText(QString::number(_slider->val(), 'f', 1));
+ _val->show();
+ _slider->mainLayout()->addSpacing(8);
+ _slider->mainLayout()->addWidget(_val);
+
+ // Connect
+ connect(_slider, &Slider::onChanged, this, &ModelAttributeSlide::onChanged);
+ connect(_slider, &Slider::onSetValue, this, [=]() {
+ _val->setText(QString::number(_slider->val(), 'f', 1));
+ });
+ connect(_slider, &Slider::onDragStart, this, &ModelAttributeSlide::onChangeStart);
+ connect(_slider, &Slider::onDragEnd, this, &ModelAttributeSlide::onChangeEnd);
+}
+
+ModelAttributeSlide::~ModelAttributeSlide()
+{}
+
+void ModelAttributeSlide::setLabel(const QString& label)
+{
+ _label->setText(label);
+}
+
+void ModelAttributeSlide::setMin(float min)
+{
+ _slider->setMin(min);
+}
+
+void ModelAttributeSlide::setMax(float max)
+{
+ _slider->setMax(max);
+}
+
+void ModelAttributeSlide::setStep(float step)
+{
+ _slider->setStep(step);
+}
+
+void ModelAttributeSlide::setValue(float val)
+{
+ _slider->setValue(val);
+}
+
+void ModelAttributeSlide::setTransformation(std::function transform, std::function inverse)
+{
+ _slider->setTransformation(transform, inverse);
+}
+
+void ModelAttributeSlide::setEnabled(bool enable)
+{
+ _slider->setEnabled(enable);
+}
diff --git a/FinalProject/modelattrslide.h b/FinalProject/modelattrslide.h
new file mode 100644
index 0000000..a7bda63
--- /dev/null
+++ b/FinalProject/modelattrslide.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "slider.h"
+
+class ModelAttributeSlide : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ModelAttributeSlide(const QString& label, float min, float max, int step, QWidget* parent = 0);
+ ~ModelAttributeSlide();
+
+private:
+ QHBoxLayout* _stretchLayout;
+ QLabel* _label;
+ QLabel* _val;
+ Slider* _slider;
+
+public:
+ // Getter APIs
+ float val() const { return _slider->val(); }
+ float lev() const { return _slider->lev(); }
+
+ // Setter APIs
+ void setLabel(const QString& label);
+ 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 enable = true);
+
+signals:
+ void onChanged(float val);
+ void onChangeStart();
+ void onChangeEnd();
+};
\ No newline at end of file
diff --git a/FinalProject/modelsetter.cpp b/FinalProject/modelsetter.cpp
index 6f70f09..9c4c1c1 100644
--- a/FinalProject/modelsetter.cpp
+++ b/FinalProject/modelsetter.cpp
@@ -1 +1,532 @@
-#pragma once
+#include "modelsetter.h"
+
+ModelSetter::ModelSetter(QWidget* parent) : ModelSetter(nullptr, parent) {}
+
+ModelSetter::ModelSetter(Renderable* object, QWidget* parent) :
+ QWidget(parent), _object(object)
+{
+ // Set background color and border radius
+ setAttribute(Qt::WA_StyledBackground, true);
+
+ // Create main layout
+ _mainLayout = new QHBoxLayout(this);
+ _mainLayout->setContentsMargins(12, 8, 12, 8);
+ _mainLayout->setSpacing(8);
+ setLayout(_mainLayout);
+
+ // Create Object Settings Panel
+ {
+ // Create container widget
+ QWidget* _objectSettingPanel = new QWidget(this);
+ _objectSettingPanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _mainLayout->addWidget(_objectSettingPanel);
+ _objectSettingPanel->show();
+
+ // Create container widget layout
+ _objectSettingLayout = new QVBoxLayout(_objectSettingPanel);
+ _objectSettingLayout->setContentsMargins(0, 0, 0, 0);
+ _objectSettingLayout->setSpacing(4);
+ _objectSettingPanel->setLayout(_objectSettingLayout);
+
+ // Create attribute adjusters
+ _scale = new ModelAttributeSlide("Scale", 0.1, 10, 100, _objectSettingPanel);
+ _rotateX = new ModelAttributeSlide("RotateX", 0, 360, 3600, _objectSettingPanel);
+ _rotateY = new ModelAttributeSlide("RotateY", 0, 360, 3600, _objectSettingPanel);
+ _rotateZ = new ModelAttributeSlide("RotateZ", 0, 360, 3600, _objectSettingPanel);
+
+ // Add attribute adjusters to layout
+ _objectSettingLayout->addWidget(_scale);
+ _objectSettingLayout->addWidget(_rotateX);
+ _objectSettingLayout->addWidget(_rotateY);
+ _objectSettingLayout->addWidget(_rotateZ);
+ _scale->show();
+ _rotateX->show();
+ _rotateY->show();
+ _rotateZ->show();
+
+ // Connect
+ connect(_scale, &ModelAttributeSlide::onChangeStart, this, &ModelSetter::onAdjustStart);
+ connect(_scale, &ModelAttributeSlide::onChangeEnd, this, &ModelSetter::onAdjustEnd);
+ connect(_scale, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr) {
+ _object->setScale(_scale->val());
+ emit onAdjust();
+ }
+ });
+ connect(_scale, &ModelAttributeSlide::onChangeEnd, this, [=]() {
+ if (_object != nullptr) {
+ _object->updateBoundary();
+ }
+ });
+
+ connect(_rotateX, &ModelAttributeSlide::onChangeStart, this, &ModelSetter::onAdjustStart);
+ connect(_rotateX, &ModelAttributeSlide::onChangeEnd, this, &ModelSetter::onAdjustEnd);
+ connect(_rotateX, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr) {
+ setRotate();
+ emit onAdjust();
+ }
+ });
+ connect(_rotateX, &ModelAttributeSlide::onChangeEnd, this, [=]() {
+ if (_object != nullptr) {
+ _object->updateBoundary();
+ }
+ });
+
+ connect(_rotateY, &ModelAttributeSlide::onChangeStart, this, &ModelSetter::onAdjustStart);
+ connect(_rotateY, &ModelAttributeSlide::onChangeEnd, this, &ModelSetter::onAdjustEnd);
+ connect(_rotateY, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr) {
+ setRotate();
+ emit onAdjust();
+ }
+ });
+ connect(_rotateY, &ModelAttributeSlide::onChangeEnd, this, [=]() {
+ if (_object != nullptr) {
+ _object->updateBoundary();
+ }
+ });
+
+ connect(_rotateZ, &ModelAttributeSlide::onChangeStart, this, &ModelSetter::onAdjustStart);
+ connect(_rotateZ, &ModelAttributeSlide::onChangeEnd, this, &ModelSetter::onAdjustEnd);
+ connect(_rotateZ, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr) {
+ setRotate();
+ emit onAdjust();
+ }
+ });
+ connect(_rotateZ, &ModelAttributeSlide::onChangeEnd, this, [=]() {
+ if (_object != nullptr) {
+ _object->updateBoundary();
+ }
+ });
+ }
+
+ // Create light switches
+ {
+ // Create container widget
+ _lightSwitchPanel = new QWidget(this);
+ _lightSwitchPanel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
+ _lightSwitchPanel->setMinimumWidth(36);
+ _mainLayout->addWidget(_lightSwitchPanel);
+ _lightSwitchPanel->show();
+
+ // Create container widget layout
+ _lightSwitchLayout = new QVBoxLayout(_lightSwitchPanel);
+ _lightSwitchLayout->setContentsMargins(0, 0, 0, 0);
+ _lightSwitchLayout->setAlignment(Qt::AlignCenter);
+ _lightSwitchLayout->setSpacing(4);
+ _lightSwitchPanel->setLayout(_lightSwitchLayout);
+
+ // Create light switches
+ _lightSwitch = new PushButton(nullptr, _lightSwitchPanel);
+ _lightSwitch->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
+ _lightSwitch->setIndicatorPosition(PushButton::LUI_BTN_POS_BOTTOM);
+ _lightSwitchIcon = new QLabel(_lightSwitch);
+ _lightSwitchIcon->setFont(QFont("Font Awesome 6 Free Regular", 12));
+ _lightSwitchIcon->setText("\uf0eb");
+ _lightSwitchIcon->setAlignment(Qt::AlignCenter);
+ _lightSwitch->setChildWidget(_lightSwitchIcon);
+
+ _lightColorPanel = new PushButton(nullptr, _lightSwitchPanel);
+ _lightColorPanel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
+ _lightColorPanel->setIndicatorPosition(PushButton::LUI_BTN_POS_BOTTOM);
+ _lightColorPanelIcon = new QLabel(_lightColorPanel);
+ _lightColorPanelIcon->setFont(QFont("Font Awesome 6 Free Solid", 12));
+ _lightColorPanelIcon->setText("\uf53f");
+ _lightColorPanelIcon->setAlignment(Qt::AlignCenter);
+ _lightColorPanel->setStyleSheet("QWidget#indicator{border: 1px solid #5c5c5c;}");
+ _lightColorPanel->setChildWidget(_lightColorPanelIcon);
+
+ // Add light switches to layout
+ _lightSwitchLayout->addWidget(_lightSwitch);
+ _lightSwitchLayout->addWidget(_lightColorPanel);
+ _lightSwitch->show();
+ _lightColorPanel->show();
+
+ // Connect
+ connect(_lightSwitch, &PushButton::onClick, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ // Disable light
+ _object->disposeLight();
+ _lightSwitch->deselect();
+ _lightSwitchIcon->setFont(QFont("Font Awesome 6 Free Regular", 12));
+ _lightColorPanel->setEnabled(false);
+ _lightDistance->setEnabled(false);
+ _lightRotateTheta->setEnabled(false);
+ _lightRotatePhi->setEnabled(false);
+ _lightCutoffAngle->setEnabled(false);
+ _lightR->setEnabled(false);
+ _lightG->setEnabled(false);
+ _lightB->setEnabled(false);
+ _lightColorPanel->deselect();
+ _lightSettingPanel->show();
+ _lightColorSettingPanel->hide();
+ _colorPaletteOn = false;
+ emit onAdjust();
+ }
+ else if (_object != nullptr) {
+ // Enable light
+ _object->makeLight();
+ _lightSwitch->select();
+ _lightSwitchIcon->setFont(QFont("Font Awesome 6 Free Solid", 12));
+ _lightColorPanel->setEnabled(true);
+ _lightColorPanel->setColorScheme(QColor(_object->originalLight()->lightColor().x, _object->originalLight()->lightColor().y, _object->originalLight()->lightColor().z));
+ _lightDistance->setEnabled(true);
+ _lightCutoffAngle->setEnabled(true);
+ _lightR->setEnabled(true);
+ _lightG->setEnabled(true);
+ _lightB->setEnabled(true);
+ _lightR->setValue(_object->originalLight()->lightColor().r * 255.0f);
+ _lightG->setValue(_object->originalLight()->lightColor().g * 255.0f);
+ _lightB->setValue(_object->originalLight()->lightColor().b * 255.0f);
+ _lightColorPanel->setColorScheme(QColor(
+ _object->originalLight()->lightColor().r * 255.0f,
+ _object->originalLight()->lightColor().g * 255.0f,
+ _object->originalLight()->lightColor().b * 255.0f
+ ));
+ _lightColorPanel->select();
+ _lightDistance->setValue(_object->originalLight()->idealDistance());
+ _lightRotateTheta->setValue(glm::degrees(glm::acos(_object->originalLight()->lightDirection().y)));
+ _lightRotatePhi->setValue(glm::degrees(glm::atan(_object->originalLight()->lightDirection().x / _object->originalLight()->lightDirection().z)));
+ _lightCutoffAngle->setValue(_object->originalLight()->cutOffAngle());
+ if (_lightCutoffAngle->val() != 180.0f) {
+ _lightRotateTheta->setEnabled(true);
+ _lightRotatePhi->setEnabled(true);
+ }
+ else {
+ _lightRotateTheta->setEnabled(false);
+ _lightRotatePhi->setEnabled(false);
+ }
+ emit onAdjust();
+ }
+ });
+ connect(_lightColorPanel, &PushButton::onClick, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ if (!_colorPaletteOn) {
+ _lightSettingPanel->hide();
+ _lightColorSettingPanel->show();
+ _colorPaletteOn = true;
+ }
+ else {
+ _lightSettingPanel->show();
+ _lightColorSettingPanel->hide();
+ _colorPaletteOn = false;
+ }
+ }
+ });
+ }
+
+ // Create light setting panel
+ {
+ // Create container widget
+ _lightSettingPanel = new QWidget(this);
+ _lightSettingPanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _mainLayout->addWidget(_lightSettingPanel);
+ _lightSettingPanel->show();
+
+ // Create container widget layout
+ _lightSettingLayout = new QVBoxLayout(_lightSettingPanel);
+ _lightSettingLayout->setContentsMargins(0, 0, 0, 0);
+ _lightSettingLayout->setSpacing(4);
+ _lightSettingPanel->setLayout(_lightSettingLayout);
+
+ // Create attribute adjusters
+ _lightDistance = new ModelAttributeSlide("Distance", 10, 3025, 145, _lightSettingPanel);
+ _lightDistance->setTransformation(
+ [](float x) {
+ float y;
+ if (x <= 90) {
+ y = 10 + x;
+ }
+ else {
+ y = 100 + glm::pow(x - 90, 2.0f);
+ }
+ return y;
+ },
+ [](float y) {
+ float x;
+ if (y <= 100) {
+ x = y - 10;
+ }
+ else {
+ x = glm::sqrt(y - 100) + 90;
+ }
+ return x;
+ }
+ );
+ _lightRotateTheta = new ModelAttributeSlide("Rotate\u03B8", 0, 360, 3600, _lightSettingPanel);
+ _lightRotatePhi = new ModelAttributeSlide("Rotate\u03C6", 0, 360, 3600, _lightSettingPanel);
+ _lightCutoffAngle = new ModelAttributeSlide("Cutoff", 0, 180, 1800, _lightSettingPanel);
+
+ // Add attribute adjusters to layout
+ _lightSettingLayout->addWidget(_lightDistance);
+ _lightSettingLayout->addWidget(_lightRotateTheta);
+ _lightSettingLayout->addWidget(_lightRotatePhi);
+ _lightSettingLayout->addWidget(_lightCutoffAngle);
+ _lightDistance->show();
+ _lightRotateTheta->show();
+ _lightRotatePhi->show();
+ _lightCutoffAngle->show();
+
+ // Connect
+ connect(_lightDistance, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ _object->originalLight()->setIdealDistance(_lightDistance->val());
+ emit onAdjust();
+ }
+ });
+ connect(_lightRotateTheta, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ setLightDir();
+ emit onAdjust();
+ }
+ });
+ connect(_lightRotatePhi, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ setLightDir();
+ emit onAdjust();
+ }
+ });
+ connect(_lightCutoffAngle, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ _object->originalLight()->setCutOffAngle(_lightCutoffAngle->val());
+ if (_lightCutoffAngle->val() != 180.0f) {
+ _lightRotateTheta->setEnabled(true);
+ _lightRotatePhi->setEnabled(true);
+ }
+ else {
+ _lightRotateTheta->setEnabled(false);
+ _lightRotatePhi->setEnabled(false);
+ }
+ emit onAdjust();
+ }
+ });
+ }
+
+ // Create color setting panel
+ {
+ // Create container widget
+ _lightColorSettingPanel = new QWidget(this);
+ _lightColorSettingPanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _mainLayout->addWidget(_lightColorSettingPanel);
+ _lightColorSettingPanel->show();
+
+ // Create container widget layout
+ _lightColorSettingPanelLayout = new QVBoxLayout(_lightColorSettingPanel);
+ _lightColorSettingPanelLayout->setContentsMargins(0, 0, 0, 0);
+ _lightColorSettingPanelLayout->setSpacing(8);
+ _lightColorSettingPanel->setLayout(_lightColorSettingPanelLayout);
+
+ // Create color adjusters
+ _lightR = new ModelAttributeSlide("R", 0, 255, 2550, _lightColorSettingPanel);
+ _lightG = new ModelAttributeSlide("G", 0, 255, 2550, _lightColorSettingPanel);
+ _lightB = new ModelAttributeSlide("B", 0, 255, 2550, _lightColorSettingPanel);
+
+ // Add color adjusters to layout
+ _lightColorSettingPanelLayout->addWidget(_lightR);
+ _lightColorSettingPanelLayout->addWidget(_lightG);
+ _lightColorSettingPanelLayout->addWidget(_lightB);
+ _lightR->show();
+ _lightG->show();
+ _lightB->show();
+
+ // Connect
+ connect(_lightR, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ setLightColor();
+ _lightColorPanel->setColorScheme(QColor(_lightR->val(), _lightG->val(), _lightB->val()));
+ emit onAdjust();
+ }
+ });
+ connect(_lightG, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ setLightColor();
+ _lightColorPanel->setColorScheme(QColor(_lightR->val(), _lightG->val(), _lightB->val()));
+ emit onAdjust();
+ }
+ });
+ connect(_lightB, &ModelAttributeSlide::onChanged, this, [=]() {
+ if (_object != nullptr && _object->hasLight()) {
+ setLightColor();
+ _lightColorPanel->setColorScheme(QColor(_lightR->val(), _lightG->val(), _lightB->val()));
+ emit onAdjust();
+ }
+ });
+ }
+
+ // Create delete button
+ {
+ _deleteBtn = new PushButton(nullptr, this);
+ _deleteBtn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ _deleteIcon = new QLabel(_deleteBtn);
+ _deleteIcon->setFont(QFont("Font Awesome 6 Free Regular", 12));
+ _deleteIcon->setText("\uf2ed");
+ _deleteIcon->setAlignment(Qt::AlignCenter);
+ _deleteBtn->setChildWidget(_deleteIcon);
+ _deleteBtn->setColorScheme(QColor(171, 59, 58));
+ _deleteBtn->setIndicatorColor(QColor(171, 59, 58, 0));
+
+ // Add delete button to layout
+ _mainLayout->addWidget(_deleteBtn);
+ _deleteBtn->show();
+
+ // Connect
+ connect(_deleteBtn, &PushButton::onClick, this, [=]() {
+ emit onDeleteObject();
+ });
+ }
+
+ // Update values
+ update(_object);
+}
+
+ModelSetter::~ModelSetter() {}
+
+void ModelSetter::setRotate() {
+ if (_object == nullptr) {
+ return;
+ }
+ // Around X-axis
+ _object->setRotation(glm::vec3(1.0f, 0.0f, 0.0f), _rotateX->val());
+ // Around Y-axis
+ _object->rotate(glm::vec3(0.0f, 1.0f, 0.0f), _rotateY->val());
+ // Around Z-axis
+ _object->rotate(glm::vec3(0.0f, 0.0f, 1.0f), _rotateZ->val());
+}
+
+void ModelSetter::setLightDir() {
+ if (_object == nullptr || !_object->hasLight()) {
+ return;
+ }
+ _object->originalLight()->setLightDirection(glm::normalize(glm::vec3(
+ cos(glm::radians(_lightRotateTheta->val())) * sin(glm::radians(_lightRotatePhi->val())),
+ sin(glm::radians(_lightRotateTheta->val())),
+ cos(glm::radians(_lightRotateTheta->val())) * cos(glm::radians(_lightRotatePhi->val()))
+ )));
+}
+
+void ModelSetter::setLightColor() {
+ if (_object == nullptr || !_object->hasLight()) {
+ return;
+ }
+ _object->originalLight()->setLightColor(glm::vec3(_lightR->val() / 255.0f, _lightG->val() / 255.0f, _lightB->val() / 255.0f));
+}
+
+void ModelSetter::update(Renderable* object) {
+ // Update settings panel by object's real value
+ if (object == nullptr) {
+ // Disable all settings
+ _scale->setEnabled(false);
+ _rotateX->setEnabled(false);
+ _rotateY->setEnabled(false);
+ _rotateZ->setEnabled(false);
+ _lightDistance->setEnabled(false);
+ _lightRotateTheta->setEnabled(false);
+ _lightRotatePhi->setEnabled(false);
+ _lightCutoffAngle->setEnabled(false);
+ _lightR->setEnabled(false);
+ _lightG->setEnabled(false);
+ _lightB->setEnabled(false);
+ _lightSwitch->setEnabled(false);
+ _lightColorPanel->setEnabled(false);
+ _deleteBtn->setEnabled(false);
+ _lightSwitchIcon->setFont(QFont("Font Awesome 6 Free Regular", 12));
+ _lightSwitch->deselect();
+ _lightColorPanel->deselect();
+ _deleteBtn->setEnabled(false);
+ }
+ else {
+ // Update settings
+ if (_object == nullptr) {
+ _scale->setEnabled();
+ }
+ _scale->setValue(object->scaleVal().x);
+
+ if (_object == nullptr) {
+ _rotateX->setEnabled();
+ _rotateY->setEnabled();
+ _rotateZ->setEnabled();
+ }
+ // Extract x, y, z axis rotation from rotation matrix
+ glm::mat4 rotationMatrix = object->rotation();
+ float rotateX = glm::degrees(glm::asin(-rotationMatrix[1][2]));
+ float rotateY = glm::degrees(glm::atan(rotationMatrix[0][2] / rotationMatrix[2][2]));
+ float rotateZ = glm::degrees(glm::atan(rotationMatrix[1][0] / rotationMatrix[1][1]));
+ _rotateX->setValue(rotateX);
+ _rotateY->setValue(rotateY);
+ _rotateZ->setValue(rotateZ);
+
+ if (_object == nullptr) {
+ _lightSwitch->setEnabled();
+ }
+ if (object->hasLight()) {
+ _lightSwitch->select();
+ }
+ else {
+ _lightSwitch->deselect();
+ }
+
+ if (object->hasLight()) {
+ if (_object == nullptr || !_object->hasLight()) {
+ // Enable light related settings
+ _lightDistance->setEnabled();
+ _lightRotateTheta->setEnabled();
+ _lightRotatePhi->setEnabled();
+ _lightCutoffAngle->setEnabled();
+ _lightR->setEnabled();
+ _lightG->setEnabled();
+ _lightB->setEnabled();
+ _lightColorPanel->setEnabled();
+ _lightSwitchIcon->setFont(QFont("Font Awesome 6 Free Solid", 12));
+ }
+ // Update light related settings
+ _lightDistance->setValue(object->originalLight()->idealDistance());
+ _lightRotateTheta->setValue(glm::degrees(glm::acos(object->originalLight()->lightDirection().y)));
+ _lightRotatePhi->setValue(glm::degrees(glm::atan(object->originalLight()->lightDirection().x / object->originalLight()->lightDirection().z)));
+ _lightCutoffAngle->setValue(object->originalLight()->cutOffAngle());
+ if (_lightCutoffAngle->val() != 180.0f) {
+ _lightRotateTheta->setEnabled(true);
+ _lightRotatePhi->setEnabled(true);
+ }
+ else {
+ _lightRotateTheta->setEnabled(false);
+ _lightRotatePhi->setEnabled(false);
+ }
+ _lightR->setValue(object->originalLight()->lightColor().r * 255.0f);
+ _lightG->setValue(object->originalLight()->lightColor().g * 255.0f);
+ _lightB->setValue(object->originalLight()->lightColor().b * 255.0f);
+ _lightColorPanel->select();
+ _lightColorPanel->setColorScheme(QColor(
+ object->originalLight()->lightColor().r * 255.0f,
+ object->originalLight()->lightColor().g * 255.0f,
+ object->originalLight()->lightColor().b * 255.0f
+ ));
+ }
+ else {
+ if (_object != nullptr && _object->hasLight()) {
+ // Disable light related settings
+ _lightDistance->setEnabled(false);
+ _lightRotateTheta->setEnabled(false);
+ _lightRotatePhi->setEnabled(false);
+ _lightCutoffAngle->setEnabled(false);
+ _lightR->setEnabled(false);
+ _lightG->setEnabled(false);
+ _lightB->setEnabled(false);
+ _lightColorPanel->setEnabled(false);
+ _lightSwitchIcon->setFont(QFont("Font Awesome 6 Free Regular", 12));
+ _lightSwitch->deselect();
+ _lightColorPanel->deselect();
+ }
+ }
+ _deleteBtn->setEnabled();
+ }
+
+ if (_object != object || _object == nullptr) {
+ _colorPaletteOn = false;
+ _lightColorSettingPanel->hide();
+ _lightSettingPanel->show();
+ }
+
+ _object = object;
+}
diff --git a/FinalProject/modelsetter.h b/FinalProject/modelsetter.h
index 71fd808..7a65ae3 100644
--- a/FinalProject/modelsetter.h
+++ b/FinalProject/modelsetter.h
@@ -1,3 +1,73 @@
#pragma once
#include
+#include
+#include
+
+#include "renderable.h"
+#include "pushbutton.h"
+#include "modelattrslide.h"
+
+class ModelSetter : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ModelSetter(QWidget* parent = 0);
+ ModelSetter(Renderable* object, QWidget* parent = 0);
+ ~ModelSetter();
+
+private:
+ // UI Elemenets
+ QHBoxLayout* _mainLayout;
+
+ QWidget* _objectSettingPanel;
+ QVBoxLayout* _objectSettingLayout;
+
+ QWidget* _lightSettingButtons;
+ QVBoxLayout* _lightSettingsButtonsLayout;
+
+ QWidget* _lightSettingPanel;
+ QVBoxLayout* _lightSettingLayout;
+
+ QWidget* _lightColorSettingPanel;
+ QVBoxLayout* _lightColorSettingPanelLayout;
+
+ ModelAttributeSlide* _scale;
+ ModelAttributeSlide* _rotateX;
+ ModelAttributeSlide* _rotateY;
+ ModelAttributeSlide* _rotateZ;
+ ModelAttributeSlide* _lightDistance;
+ ModelAttributeSlide* _lightRotateTheta;
+ ModelAttributeSlide* _lightRotatePhi;
+ ModelAttributeSlide* _lightCutoffAngle;
+ ModelAttributeSlide* _lightR;
+ ModelAttributeSlide* _lightG;
+ ModelAttributeSlide* _lightB;
+ QWidget* _lightSwitchPanel;
+ QVBoxLayout* _lightSwitchLayout;
+ PushButton* _lightSwitch;
+ QLabel* _lightSwitchIcon;
+ PushButton* _lightColorPanel;
+ QLabel* _lightColorPanelIcon;
+ PushButton* _deleteBtn;
+ QLabel* _deleteIcon;
+
+ // State
+ Renderable* _object = nullptr;
+ bool _colorPaletteOn = false;
+
+private:
+ void setRotate();
+ void setLightDir();
+ void setLightColor();
+
+public:
+ void update(Renderable* object);
+
+signals:
+ void onAdjustStart();
+ void onAdjustEnd();
+ void onAdjust();
+ void onDeleteObject();
+};
diff --git a/FinalProject/pushbutton.cpp b/FinalProject/pushbutton.cpp
index 0470b86..5ed448d 100644
--- a/FinalProject/pushbutton.cpp
+++ b/FinalProject/pushbutton.cpp
@@ -84,6 +84,10 @@ void PushButton::generateColor(QColor colorScheme) {
}
void PushButton::enterEvent(QEnterEvent* event) {
+ if (!_enabled) {
+ return;
+ }
+
setCursor(Qt::PointingHandCursor);
_backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
@@ -141,6 +145,10 @@ void PushButton::enterEvent(QEnterEvent* event) {
}
void PushButton::leaveEvent(QEvent* event) {
+ if (!_enabled) {
+ return;
+ }
+
setCursor(Qt::ArrowCursor);
if (_selected) {
@@ -203,6 +211,10 @@ void PushButton::leaveEvent(QEvent* event) {
}
void PushButton::mousePressEvent(QMouseEvent* event) {
+ if (!_enabled) {
+ return;
+ }
+
_backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _pressedColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
QPropertyAnimation* indicatorShrinkLength = new QPropertyAnimation(_indicator, "geometry", this);
@@ -601,6 +613,49 @@ void PushButton::deselect() {
_selected = false;
}
+void PushButton::setEnabled(bool enabled) {
+ if (enabled == _enabled) {
+ return;
+ }
+
+ if (enabled) {
+ _enabled = true;
+ // Restore colors
+ _backgroundColor = _restoredColor[0];
+ _hoverColor = _restoredColor[1];
+ _pressedColor = _restoredColor[2];
+ _selectedColor = _restoredColor[3];
+ _indicatorColor = _restoredColor[4];
+ if (_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _pressedColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+ else if (_hovered) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+ else if (_selected) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _selectedColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+ else {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+ _indicator->setStyleSheet("QWidget#indicator{background-color:" + _indicatorColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number((float)_indicatorWidth / 2) + "px;}");
+ }
+ else {
+ _enabled = false;
+ _pressed = false;
+ _hovered = false;
+ // Store color
+ _restoredColor[0] = _backgroundColor;
+ _restoredColor[1] = _hoverColor;
+ _restoredColor[2] = _pressedColor;
+ _restoredColor[3] = _selectedColor;
+ _restoredColor[4] = _indicatorColor;
+ // Set disabled colors
+ setColorScheme(QColor(200, 200, 200));
+ }
+}
+
void PushButton::setRadius(int radius) {
// get current style sheet
QString styleSheet = _backgroundWidget->styleSheet();
diff --git a/FinalProject/pushbutton.h b/FinalProject/pushbutton.h
index 8ac2f6d..ab96288 100644
--- a/FinalProject/pushbutton.h
+++ b/FinalProject/pushbutton.h
@@ -39,6 +39,7 @@ private:
QColor _hoverColor;
QColor _pressedColor;
QColor _selectedColor;
+ QColor _restoredColor[5];
QWidget* _indicator;
LUI_BTN_INDICATOR_POS _indicatorPosition = LUI_BTN_POS_LEFT;
@@ -51,6 +52,7 @@ private:
QGraphicsOpacityEffect* _indicatorEffect;
// Button state
+ bool _enabled = true;
bool _hovered = false;
bool _pressed = false;
bool _selected = false;
@@ -72,6 +74,7 @@ public:
// Operation APIs
void select();
void deselect();
+ void setEnabled(bool enabled = true);
// Attribute setter APIs
void setRadius(int radius);
diff --git a/FinalProject/renderable.cpp b/FinalProject/renderable.cpp
index 9f890d5..219558e 100644
--- a/FinalProject/renderable.cpp
+++ b/FinalProject/renderable.cpp
@@ -59,6 +59,13 @@ void Renderable::makeLight() {
_light = new ScopedLight(glm::vec3(0.0f));
}
+void Renderable::disposeLight() {
+ if (_light != nullptr) {
+ delete _light;
+ _light = nullptr;
+ }
+}
+
void Renderable::render(ShaderProgram shader) {
// Check if initialized
if (_model == nullptr) {
diff --git a/FinalProject/renderable.h b/FinalProject/renderable.h
index e8b268b..eed1222 100644
--- a/FinalProject/renderable.h
+++ b/FinalProject/renderable.h
@@ -32,6 +32,10 @@ public:
~Renderable();
public:
+ glm::vec3 position() const { return _position; }
+ glm::mat4 rotation() const { return _rotation; }
+ glm::vec3 scaleVal() const { return _scale; }
+
void setModel(Model* model);
void move(glm::vec3 deltaVec);
void setPosition(glm::vec3 position);
@@ -44,6 +48,7 @@ public:
ScopedLight* originalLight() const; // pass out the light object to scene manager to set light attributes
bool hasLight() const { return _light != nullptr; }
void makeLight(); // create a light source in the object
+ void disposeLight(); // remove the light source in the object
const Boundary& boundary() const { return _boundary; }
diff --git a/FinalProject/sceneviewer.cpp b/FinalProject/sceneviewer.cpp
index c16781a..dc915eb 100644
--- a/FinalProject/sceneviewer.cpp
+++ b/FinalProject/sceneviewer.cpp
@@ -280,6 +280,7 @@ void SceneViewer::mouseReleaseEvent(QMouseEvent* event) {
else {
_selectedObject = _pressedObject;
_hideBound = false;
+ emit onSelect(_selectedObject);
}
// Reset pressed object
@@ -314,6 +315,7 @@ void SceneViewer::mouseMoveEvent(QMouseEvent* event) {
_selectedObject->rotate(_camera.up(), delta.x * 0.01f);
// Rotate around camera right
_selectedObject->rotate(_camera.right(), delta.y * 0.01f);
+ emit onUpdate(_selectedObject);
}
break;
}
@@ -332,6 +334,7 @@ void SceneViewer::mouseMoveEvent(QMouseEvent* event) {
// Scale object
glm::vec2 delta = glm::vec2(event->x() - _lastMousePosition.x(), event->y() - _lastMousePosition.y());
_selectedObject->scale(-delta.y * 0.01f);
+ emit onUpdate(_selectedObject);
}
else {
// Set dragged
@@ -356,6 +359,7 @@ void SceneViewer::mouseMoveEvent(QMouseEvent* event) {
}
else {
moveOperatingObject(ray);
+ emit onUpdate(_selectedObject);
}
break;
}
@@ -490,4 +494,29 @@ void SceneViewer::addObject(Model* model) {
_operatingObject = newObject;
_objects.push_back(newObject);
parentWidget()->update();
+ emit onSelect(_selectedObject);
+}
+
+void SceneViewer::deleteObject() {
+ if (_selectedObject == nullptr) {
+ return;
+ }
+ makeCurrent();
+ for (auto it = _objects.begin(); it != _objects.end(); ++it) {
+ if (*it == _selectedObject) {
+ _objects.erase(it);
+ break;
+ }
+ }
+ delete _selectedObject;
+ if (_hoveredObject == _selectedObject) {
+ _hoveredObject = nullptr;
+ }
+ if (_pressedObject == _selectedObject) {
+ _pressedObject = nullptr;
+ }
+ _selectedObject = nullptr;
+ _operatingObject = nullptr;
+ emit onSelect(nullptr);
+ parentWidget()->update();
}
diff --git a/FinalProject/sceneviewer.h b/FinalProject/sceneviewer.h
index 1c0b855..71585e1 100644
--- a/FinalProject/sceneviewer.h
+++ b/FinalProject/sceneviewer.h
@@ -86,6 +86,19 @@ protected:
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void keyReleaseEvent(QKeyEvent* event) override;
+public:
+ void setDragFlag() {
+ _hideBound = true;
+ parentWidget()->update();
+ }
+ void clearDragFlag() {
+ _hideBound = false;
+ if (_selectedObject != nullptr)
+ _selectedObject->updateBoundary();
+ parentWidget()->update();
+ }
+ void deleteObject();
+
signals:
void onHover(Renderable* object);
void onSelect(Renderable* object);
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();
+};