diff --git a/FinalProject/FinalProject.vcxproj b/FinalProject/FinalProject.vcxproj
index 75e1a28..f3f0de1 100644
--- a/FinalProject/FinalProject.vcxproj
+++ b/FinalProject/FinalProject.vcxproj
@@ -103,23 +103,32 @@
+
+
+
+
+
+
+
+
+
+
-
@@ -128,18 +137,28 @@
+
+
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/FinalProject/FinalProject.vcxproj.filters b/FinalProject/FinalProject.vcxproj.filters
index 20d5f67..7638a20 100644
--- a/FinalProject/FinalProject.vcxproj.filters
+++ b/FinalProject/FinalProject.vcxproj.filters
@@ -13,10 +13,6 @@
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
-
- {99349809-55BA-4b9d-BF79-8FDBB0286EB3}
- ui
-
{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}
ts
@@ -39,14 +35,53 @@
{602fc1ed-84a6-4d4a-ad71-5e7415d8d429}
+
+ {dd0c6d73-36ab-44ac-9780-4c378faa5774}
+
+
+ {d0eeae28-809f-4e8e-b1e0-d5d7c5cca3e7}
+
+
+ {0ee9dd70-07d0-44ec-9cb5-37478f6d9ede}
+
+
+ {c6368565-9e0a-487b-953c-6c4bc9e3f655}
+
+
+ {2ce6e2ec-f0bf-4d49-8b11-cdf0f31693c5}
+
+
+ {f8b17ddd-9689-45cc-84a1-5c321b0290b0}
+
+
+ {469e46b8-328a-474b-bf82-19c86b980e82}
+
+
+ {1351d841-7665-48d3-9850-da0738823396}
+
+
+ {5e6e1e89-1c77-4a89-92cc-d64ea2546961}
+
+
+ {d2b9abbc-ef10-4990-96a1-a88f9c217dcf}
+
+
+ {d6a38868-baed-45d5-9cbf-3faa3b563fa9}
+
+
+ {4b5e61f5-3dfe-44d7-bfcc-cf5b70528b16}
+
+
+ {995296ff-ac0c-4996-9692-c0e017e5fc03}
+
+
+ {6c5ac0df-26ad-40ad-860c-3d0372b81094}
+
Resource Files
-
- Form Files
-
@@ -85,21 +120,51 @@
Source Files\Qt Widgets
-
- Source Files\Qt Widgets
-
-
- Source Files\Qt Widgets
-
-
- Source Files\Qt Widgets
-
-
- Source Files\Qt Widgets
-
Source Files\Utils
+
+ Source Files\Qt Widgets\Pages\Scene Editor
+
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\Pages\About Page
+
+
+ Source Files\Qt Widgets\GUI Components
+
+
+ Source Files\Qt Widgets\Pages\Scene Editor\Object Selector
+
+
+ Source Files\Qt Widgets\Pages\Scene Editor\Object Selector
+
+
+ Source Files\Qt Widgets\Pages\Scene Editor\Object Setter
+
+
+ Source Files\Qt Widgets\Pages\Scene Editor\Scene Viewer
+
+
+ Source Files\Qt Widgets\Pages\Scene Editor\Object Selector
+
@@ -117,12 +182,6 @@
Header Files\OpenGL Abstractions
-
- Header Files\Qt Widgets
-
-
- Header Files\Qt Widgets
-
Header Files\OpenGL Abstractions
@@ -141,20 +200,56 @@
Header Files\Utils
-
- Header Files\Qt Widgets
-
Header Files\Utils
+
+ Header Files\Qt Widgets\Pages\Scene Editor\Object Setter
+
-
- Header Files\Qt Widgets
-
Header Files\Qt Widgets
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\Pages\Scene Editor
+
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\Pages\About Page
+
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\GUI Components
+
+
+ Header Files\Qt Widgets\Pages\Scene Editor\Scene Viewer
+
+
+ Header Files\Qt Widgets\Pages\Scene Editor\Object Selector
+
+
+ Header Files\Qt Widgets\Pages\Scene Editor\Object Selector
+
+
+ Header Files\Qt Widgets\Pages\Scene Editor\Object Selector
+
diff --git a/FinalProject/aboutpage.cpp b/FinalProject/aboutpage.cpp
new file mode 100644
index 0000000..62b2631
--- /dev/null
+++ b/FinalProject/aboutpage.cpp
@@ -0,0 +1,135 @@
+#include
+
+#include "aboutpage.h"
+
+AboutPage::AboutPage(QWidget* parent) :
+ PageWidget(parent)
+{
+ _contentWidget->setMouseTracking(true);
+
+ // Construct title layout
+ _titleLayout = new QVBoxLayout(_contentWidget);
+ _titleLayout->setContentsMargins(28, 46, 28, 28);
+ _titleLayout->setSpacing(18);
+ _titleLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ _contentWidget->setLayout(_titleLayout);
+
+ // Construct title
+ _titleLabel = new QLabel("ABOUT", _contentWidget);
+ _titleLabel->setFont(_titleFont);
+ _titleLayout->addWidget(_titleLabel);
+ _titleLabel->show();
+
+ // Construct main layout
+ _mainWidget = new QWidget(_contentWidget);
+ _mainWidget->setObjectName("mainWidget");
+ _mainWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ _mainWidget->setStyleSheet("QWidget#mainWidget { background-color: #efefef; border-radius: 8px; }");
+ _mainLayout = new QVBoxLayout(_mainWidget);
+ _mainLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ _mainLayout->setContentsMargins(0, 8, 0, 8);
+ _mainLayout->setSpacing(8);
+ _mainWidget->setLayout(_mainLayout);
+ _titleLayout->addWidget(_mainWidget);
+ _mainWidget->show();
+
+ // Construct contents
+ _authorLabel = new LineEditWidget(_mainWidget);
+ _authorLabel->mainLayout()->insertWidget(0, new QLabel("Author"));
+ _authorLabel->mainLayout()->insertSpacing(1, 8);
+ _authorLabel->setBackgroundColor(QColor(0, 0, 0, 0));
+ _authorLabel->setHoverColor(QColor(0, 0, 0, 10));
+ _authorLabel->setPressedColor(QColor(0, 0, 0, 20));
+ _authorLabel->setText("Linloir");
+ _authorLabel->setEnabled(false);
+ _mainLayout->addWidget(_authorLabel);
+ _authorLabel->show();
+
+ _versionLabel = new LineEditWidget(_mainWidget);
+ _versionLabel->mainLayout()->insertWidget(0, new QLabel("Version"));
+ _versionLabel->mainLayout()->insertSpacing(1, 8);
+ _versionLabel->setBackgroundColor(QColor(0, 0, 0, 0));
+ _versionLabel->setHoverColor(QColor(0, 0, 0, 10));
+ _versionLabel->setPressedColor(QColor(0, 0, 0, 20));
+ _versionLabel->setText("1.0.0");
+ _versionLabel->setEnabled(false);
+ _mainLayout->addWidget(_versionLabel);
+ _versionLabel->show();
+
+ _dateLabel = new LineEditWidget(_mainWidget);
+ _dateLabel->mainLayout()->insertWidget(0, new QLabel("Last Update"));
+ _dateLabel->mainLayout()->insertSpacing(1, 8);
+ _dateLabel->setBackgroundColor(QColor(0, 0, 0, 0));
+ _dateLabel->setHoverColor(QColor(0, 0, 0, 10));
+ _dateLabel->setPressedColor(QColor(0, 0, 0, 20));
+ _dateLabel->setText("2020/12/31");
+ _dateLabel->setEnabled(false);
+ _mainLayout->addWidget(_dateLabel);
+ _dateLabel->show();
+}
+
+AboutPage::~AboutPage() {}
+
+PushButton* AboutPage::getPageIconButton(QWidget* context) {
+ // Check for existed button
+ if (_iconButton != nullptr) {
+ return _iconButton;
+ }
+
+ // Generate new icon button
+ _iconButton = new PushButton(nullptr, context);
+ _iconButton->setMargin(20, 18, 16, 18);
+ _iconButtonLabel = new QLabel(_iconButton);
+ _iconButtonLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ QFont iconButtonFont("Font Awesome 6 Free Regular", 12);
+ iconButtonFont.setStyleStrategy(QFont::PreferAntialias);
+ _iconButtonLabel->setFont(iconButtonFont);
+ _iconButtonLabel->setText("\uf059");
+ _iconButtonLabel->setAlignment(Qt::AlignLeft);
+ _iconButton->setChildWidget(_iconButtonLabel);
+
+ // Return newly generated icon
+ return _iconButton;
+}
+
+PushButton* AboutPage::getPageTextButton(QWidget* context) {
+ // Check for existed button
+ if (_textButton != nullptr) {
+ return _textButton;
+ }
+
+ // Generate new text button
+ _textButton = new PushButton(nullptr, context);
+ _textButton->setMargin(20, 18, 16, 18);
+ _textButtonWidget = new QWidget(_textButton);
+ _textButtonLayout = new QHBoxLayout(_textButtonWidget);
+ _textButtonLayout->setContentsMargins(0, 0, 0, 0);
+ _textButtonLayout->setSpacing(12);
+ _textButtonWidget->setLayout(_textButtonLayout);
+
+ // Generate text button contents
+ _textButtonIcon = new QLabel(_textButtonWidget);
+ QFont textButtonFont("Font Awesome 6 Free Regular", 12);
+ textButtonFont.setStyleStrategy(QFont::PreferQuality);
+ _textButtonIcon->setFont(textButtonFont);
+ _textButtonIcon->setText("\uf059"); // set icon to "pen" icon
+ _textButtonIcon->setAlignment(Qt::AlignLeft);
+
+ _textButtonLabel = new QLabel(_textButtonWidget);
+ _textButtonLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ _textButtonLabel->setText("About");
+ _textButtonLabel->setAlignment(Qt::AlignLeft);
+
+ // Add text button contents to layout
+ _textButtonLayout->addWidget(_textButtonIcon);
+ _textButtonLayout->addWidget(_textButtonLabel);
+ _textButtonIcon->show();
+ _textButtonLabel->show();
+
+ // Set text button child widget
+ _textButton->setChildWidget(_textButtonWidget);
+ _textButtonWidget->show();
+
+ // Return newly generated text button
+ return _textButton;
+}
diff --git a/FinalProject/aboutpage.h b/FinalProject/aboutpage.h
new file mode 100644
index 0000000..3a242d6
--- /dev/null
+++ b/FinalProject/aboutpage.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "pagewidget.h"
+#include "lineeditwidget.h"
+
+class AboutPage : public PageWidget {
+
+ Q_OBJECT
+
+public:
+ AboutPage(QWidget* parent = 0);
+ ~AboutPage();
+
+private:
+ // Push button icons
+ QLabel* _iconButtonLabel = nullptr;
+ QWidget* _textButtonWidget = nullptr;
+ QHBoxLayout* _textButtonLayout = nullptr;
+ QLabel* _textButtonIcon = nullptr;
+ QLabel* _textButtonLabel = nullptr;
+
+ // UI elements
+ QVBoxLayout* _titleLayout = nullptr;
+ const QFont _titleFont = QFont("DengXian", 26, QFont::ExtraLight);
+ QLabel* _titleLabel = nullptr;
+
+ QWidget* _mainWidget = nullptr;
+ QVBoxLayout* _mainLayout = nullptr;
+
+ LineEditWidget* _authorLabel = nullptr;
+ LineEditWidget* _versionLabel = nullptr;
+ LineEditWidget* _dateLabel = nullptr;
+
+public:
+ virtual PushButton* getPageIconButton(QWidget* context) override;
+ virtual PushButton* getPageTextButton(QWidget* context) override;
+};
diff --git a/FinalProject/editorpage.cpp b/FinalProject/editorpage.cpp
new file mode 100644
index 0000000..a10bc68
--- /dev/null
+++ b/FinalProject/editorpage.cpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include
+
+#include "editorpage.h"
+
+EditorPage::EditorPage(QWidget* parent) :
+ PageWidget(parent)
+{
+ _contentWidget->setMouseTracking(true);
+
+ // Construct title layout
+ _titleLayout = new QVBoxLayout(_contentWidget);
+ _titleLayout->setContentsMargins(28, 46, 28, 28);
+ _titleLayout->setSpacing(18);
+ _titleLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ _contentWidget->setLayout(_titleLayout);
+
+ // Construct title
+ _titleLabel = new QLabel("EDITOR", _contentWidget);
+ _titleLabel->setFont(_titleFont);
+ _titleLayout->addWidget(_titleLabel);
+ _titleLabel->show();
+
+ // Construct main layout
+ _mainWidget = new QWidget(_contentWidget);
+ _mainWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _mainLayout = new QHBoxLayout(_mainWidget);
+ _mainLayout->setContentsMargins(0, 0, 0, 0);
+ _mainLayout->setSpacing(16);
+ _mainWidget->setLayout(_mainLayout);
+ _titleLayout->addWidget(_mainWidget);
+ _mainWidget->show();
+
+ // Generate model selector
+ _modelSelector = new ModelSelector(_mainWidget);
+ _mainLayout->addWidget(_modelSelector);
+ _modelSelector->show();
+
+ // Generate scene viewer
+ _sceneViewerContainer = new RoundedCornerWidget(_mainWidget);
+ _sceneViewerContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _sceneViewerContainerLayout = new QVBoxLayout(_sceneViewerContainer->mainWidget());
+ _sceneViewerContainerLayout->setContentsMargins(0, 0, 0, 0);
+ _sceneViewerContainerLayout->setSpacing(0);
+ _sceneViewerContainer->mainWidget()->setLayout(_sceneViewerContainerLayout);
+ _sceneViewer = new SceneViewer(_sceneViewerContainer->mainWidget());
+ _sceneViewerContainerLayout->addWidget(_sceneViewer);
+ _sceneViewer->show();
+ _mainLayout->addWidget(_sceneViewerContainer);
+ _sceneViewerContainer->show();
+}
+
+EditorPage::~EditorPage() {}
+
+PushButton* EditorPage::getPageIconButton(QWidget* context) {
+ // Check for existed button
+ if (_iconButton != nullptr) {
+ return _iconButton;
+ }
+
+ // Generate new icon button
+ _iconButton = new PushButton(nullptr, context);
+ _iconButton->setMargin(20, 18, 16, 18);
+ _iconButtonLabel = new QLabel(_iconButton);
+ _iconButtonLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ QFont iconButtonFont("Font Awesome 6 Free Solid", 12);
+ iconButtonFont.setStyleStrategy(QFont::PreferAntialias);
+ _iconButtonLabel->setFont(iconButtonFont);
+ _iconButtonLabel->setText("\uf304"); // set icon to "pen" icon
+ _iconButtonLabel->setAlignment(Qt::AlignLeft);
+ _iconButton->setChildWidget(_iconButtonLabel);
+
+ // Return newly generated icon
+ return _iconButton;
+}
+
+PushButton* EditorPage::getPageTextButton(QWidget* context) {
+ // Check for existed button
+ if (_textButton != nullptr) {
+ return _textButton;
+ }
+
+ // Generate new text button
+ _textButton = new PushButton(nullptr, context);
+ _textButton->setMargin(20, 18, 16, 18);
+ _textButtonWidget = new QWidget(_textButton);
+ _textButtonLayout = new QHBoxLayout(_textButtonWidget);
+ _textButtonLayout->setContentsMargins(0, 0, 0, 0);
+ _textButtonLayout->setSpacing(12);
+ _textButtonWidget->setLayout(_textButtonLayout);
+
+ // Generate text button contents
+ _textButtonIcon = new QLabel(_textButtonWidget);
+ QFont textButtonFont("Font Awesome 6 Free Solid", 12);
+ textButtonFont.setStyleStrategy(QFont::PreferQuality);
+ _textButtonIcon->setFont(textButtonFont);
+ _textButtonIcon->setText("\uf304"); // set icon to "pen" icon
+ _textButtonIcon->setAlignment(Qt::AlignLeft);
+
+ _textButtonLabel = new QLabel(_textButtonWidget);
+ _textButtonLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ _textButtonLabel->setText("Editor");
+ _textButtonLabel->setAlignment(Qt::AlignLeft);
+
+ // Add text button contents to layout
+ _textButtonLayout->addWidget(_textButtonIcon);
+ _textButtonLayout->addWidget(_textButtonLabel);
+ _textButtonIcon->show();
+ _textButtonLabel->show();
+
+ // Set text button child widget
+ _textButton->setChildWidget(_textButtonWidget);
+ _textButtonWidget->show();
+
+ // Return newly generated text button
+ return _textButton;
+}
diff --git a/FinalProject/editorpage.h b/FinalProject/editorpage.h
new file mode 100644
index 0000000..acf41cf
--- /dev/null
+++ b/FinalProject/editorpage.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "pagewidget.h"
+#include "sceneviewer.h"
+#include "roundedcornerwidget.h"
+#include "modelselector.h"
+
+class EditorPage : public PageWidget {
+
+ Q_OBJECT
+
+public:
+ EditorPage(QWidget* parent = 0);
+ ~EditorPage();
+
+private:
+ // Push button icons
+ QLabel* _iconButtonLabel = nullptr;
+ QWidget* _textButtonWidget = nullptr;
+ QHBoxLayout* _textButtonLayout = nullptr;
+ QLabel* _textButtonIcon = nullptr;
+ QLabel* _textButtonLabel = nullptr;
+
+ // UI elements
+ QVBoxLayout* _titleLayout = nullptr;
+ const QFont _titleFont = QFont("DengXian", 26, QFont::ExtraLight);
+ QLabel* _titleLabel = nullptr;
+
+ QWidget* _mainWidget = nullptr;
+ QHBoxLayout* _mainLayout = nullptr;
+
+ ModelSelector* _modelSelector = nullptr;
+
+ RoundedCornerWidget* _sceneViewerContainer = nullptr;
+ QVBoxLayout* _sceneViewerContainerLayout = nullptr;
+ SceneViewer* _sceneViewer = nullptr;
+
+public:
+ virtual PushButton* getPageIconButton(QWidget* context) override;
+ virtual PushButton* getPageTextButton(QWidget* context) override;
+};
diff --git a/FinalProject/font_awesome_6_regular_free.otf b/FinalProject/font_awesome_6_regular_free.otf
new file mode 100644
index 0000000..5c3bf98
Binary files /dev/null and b/FinalProject/font_awesome_6_regular_free.otf differ
diff --git a/FinalProject/font_awesome_6_solid_free.otf b/FinalProject/font_awesome_6_solid_free.otf
new file mode 100644
index 0000000..6851a08
Binary files /dev/null and b/FinalProject/font_awesome_6_solid_free.otf differ
diff --git a/FinalProject/framelesswindow.cpp b/FinalProject/framelesswindow.cpp
new file mode 100644
index 0000000..bd77c39
--- /dev/null
+++ b/FinalProject/framelesswindow.cpp
@@ -0,0 +1,373 @@
+#include
+#include
+#include
+#include
+
+#include "framelesswindow.h"
+#include "logger.h"
+
+#define MAX_MOUSE_MOVEMENT 300
+
+FramelessWindow::FramelessWindow(int cornerRadius, unsigned int attributes, QWidget* parent)
+ : _cornerRadius(cornerRadius), _attributes((LUI_WINDOW_ATTRIBUTES)attributes), QWidget(parent)
+{
+ setAttribute(Qt::WA_TranslucentBackground);
+ setWindowFlags(Qt::FramelessWindowHint);
+ setFocusPolicy(Qt::StrongFocus);
+ setMouseTracking(true);
+ setFocus();
+
+ // Create and properly set real displayed window widget
+ _stretchLayout = new QVBoxLayout(this);
+ _stretchLayout->setContentsMargins(30, 30, 30, 30);
+ _windowWidget = new QWidget(this);
+ _windowWidget->setObjectName("windowWidget");
+ _windowWidget->setMouseTracking(true);
+ _stretchLayout->addWidget(_windowWidget);
+ _windowWidget->show();
+ setLayout(_stretchLayout);
+
+ // Set style sheet for window widget
+ QString windowWidgetStyleSheet = "QWidget#windowWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_cornerRadius) + "px;}";
+ _windowWidget->setStyleSheet(windowWidgetStyleSheet);
+
+ // Set shadow for window widget
+ _windowShadow = new QGraphicsDropShadowEffect(_windowWidget);
+ _windowShadow->setBlurRadius(30);
+ _windowShadow->setColor(QColor(0, 0, 0));
+ _windowShadow->setOffset(0, 0);
+ _windowWidget->setGraphicsEffect(_windowShadow);
+
+ // Create window control buttons container widget & its layout
+ _windowBtnWidget = new QWidget(_windowWidget);
+ _windowBtnWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ _windowBtnWidget->setMouseTracking(true);
+ _windowBtnLayout = new QHBoxLayout(_windowBtnWidget);
+ _windowBtnLayout->setContentsMargins(0, 0, 0, 0);
+ _windowBtnLayout->setSpacing(10);
+ _windowBtnLayout->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ _windowBtnWidget->setLayout(_windowBtnLayout);
+
+ // Create window control buttons
+ _minimizeBtn = new QPushButton(_windowBtnWidget);
+ _maximizeBtn = new QPushButton(_windowBtnWidget);
+ _closeBtn = new QPushButton(_windowBtnWidget);
+
+ _minimizeBtn->setFixedSize(12, 12);
+ _maximizeBtn->setFixedSize(12, 12);
+ _closeBtn->setFixedSize(12, 12);
+
+ _minimizeBtn->setStyleSheet("QPushButton{border-radius: 6px; background-color: #c2c2c2;}"
+ "QPushButton:hover{background-color: #e98b2a;}");
+ _maximizeBtn->setStyleSheet("QPushButton{border-radius: 6px; background-color: #c2c2c2;}"
+ "QPushButton:hover{background-color: #2d6d4b;}");
+ _closeBtn->setStyleSheet("QPushButton{border-radius: 6px; background-color: #c2c2c2;}"
+ "QPushButton:hover{background-color: #ab3b3a;}");
+
+ _windowBtnLayout->addWidget(_minimizeBtn);
+ _windowBtnLayout->addWidget(_maximizeBtn);
+ _windowBtnLayout->addWidget(_closeBtn);
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_MINIMIZE) == 0) {
+ _minimizeBtn->show();
+ }
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_MAXIMIZE) == 0) {
+ _maximizeBtn->show();
+ }
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_CLOSE) == 0) {
+ _closeBtn->show();
+ }
+
+ // Connect window control buttons
+ connect(_minimizeBtn, &QPushButton::clicked, this, &QWidget::showMinimized);
+ connect(_maximizeBtn, &QPushButton::clicked, this, &FramelessWindow::controlWindowScale);
+ connect(_closeBtn, &QPushButton::clicked, this, &QWidget::close);
+}
+
+FramelessWindow::FramelessWindow(QWidget* parent)
+ : FramelessWindow(0, LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_NO_ATTRIBUTES, parent)
+{
+}
+
+FramelessWindow::FramelessWindow(int cornerRadius, QWidget* parent)
+ : FramelessWindow(cornerRadius, LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_NO_ATTRIBUTES, parent)
+{
+}
+
+FramelessWindow::FramelessWindow(unsigned int attributes, QWidget* parent)
+ : FramelessWindow(0, attributes, parent)
+{
+}
+
+FramelessWindow::~FramelessWindow() {}
+
+void FramelessWindow::showEvent(QShowEvent* event) {
+ // Initialize window UI after window is shown
+ initializeWindowUI();
+}
+
+void FramelessWindow::initializeWindowUI() {
+ if (_initialized) {
+ return;
+ }
+
+ // Create a round cornered mask for window widget
+ QPainterPath path;
+ path.addRoundedRect(_windowWidget->rect(), _cornerRadius - 1, _cornerRadius - 1);
+ QRegion region(path.toFillPolygon().toPolygon());
+ _windowWidget->setMask(region);
+
+ // Create a border for window widget (in order to hide zigzagged edges)
+ _windowBorder = new QWidget(this);
+ _windowBorder->setObjectName("windowBorder");
+ QString windowBorderStyleSheet =
+ "QWidget#windowBorder{background-color:#00FFFFFF;border:1.5px solid " + _borderColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_cornerRadius) + "px;}";
+ _windowBorder->setStyleSheet(windowBorderStyleSheet);
+ _windowBorder->setAttribute(Qt::WA_TransparentForMouseEvents);
+ _windowBorder->move(_windowWidget->pos() - QPoint(1, 1));
+ _windowBorder->resize(_windowWidget->size() + QSize(2, 2));
+ _windowBorder->show();
+
+ // Move button widget to the top right of the window widget
+ _windowBtnWidget->move(_windowWidget->width() - _windowBtnWidget->width() - 18, 18);
+ _windowBtnWidget->show();
+ _windowBtnWidget->raise();
+
+ // Set initialized state
+ _initialized = true;
+}
+
+void FramelessWindow::resizeEvent(QResizeEvent* event) {
+ // Resize window border
+ if (_windowBorder != nullptr) {
+ _windowBorder->move(_windowWidget->pos() - QPoint(1, 1));
+ _windowBorder->resize(_windowWidget->size() + QSize(2, 2));
+ }
+
+ // Resize window mask
+ QPainterPath path;
+ path.addRoundedRect(_windowWidget->rect(), _cornerRadius - 1, _cornerRadius - 1);
+ QRegion region(path.toFillPolygon().toPolygon());
+ _windowWidget->setMask(region);
+
+ // Move button widget to the top right of the window widget
+ _windowBtnWidget->move(_windowWidget->width() - _windowBtnWidget->width() - 18, 18);
+}
+
+void FramelessWindow::controlWindowScale() {
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_MAXIMIZE) != 0) {
+ return;
+ }
+ if (!_maximized) {
+ _lastWindowGeometry = frameGeometry();
+
+ Logger::debug("Maximizing window:");
+ Logger::debug("[+] current window position: " + std::to_string(x()) + ", " + std::to_string(y()));
+ Logger::debug("[+] current window size: " + std::to_string(width()) + ", " + std::to_string(height()));
+ Logger::debug("[+] current geometry: " + std::to_string(_lastWindowGeometry.x()) + ", " + std::to_string(_lastWindowGeometry.y()) + ", " + std::to_string(_lastWindowGeometry.width()) + ", " + std::to_string(_lastWindowGeometry.height()));
+ Logger::debug("[+] current window widget position: " + std::to_string(_windowWidget->x()) + ", " + std::to_string(_windowWidget->y()));
+ Logger::debug("[+] current window widget size: " + std::to_string(_windowWidget->width()) + ", " + std::to_string(_windowWidget->height()));
+ Logger::debug("[+] current window border position: " + std::to_string(_windowBorder->x()) + ", " + std::to_string(_windowBorder->y()));
+ Logger::debug("[+] current window border size: " + std::to_string(_windowBorder->width()) + ", " + std::to_string(_windowBorder->height()));
+
+ _windowShadow->setEnabled(false);
+ _windowBorder->hide();
+ QString windowWidgetStyleSheet = "QWidget#windowWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";}";
+ _windowWidget->setStyleSheet(windowWidgetStyleSheet);
+
+ _stretchLayout->setContentsMargins(0, 0, 0, 0);
+
+ showMaximized();
+
+ QPainterPath path;
+ path.addRect(_windowWidget->rect());
+ QRegion mask(path.toFillPolygon().toPolygon());
+ _windowWidget->setMask(mask);
+
+ _maximized = true;
+ }
+ else {
+ _stretchLayout->setContentsMargins(30, 30, 30, 30);
+
+ showNormal();
+
+ resize(_lastWindowGeometry.size());
+ move(_lastWindowGeometry.topLeft());
+
+ _windowShadow->setEnabled(true);
+ _windowBorder->show();
+ QString windowWidgetStyleSheet = "QWidget#windowWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_cornerRadius) + "px;}";
+ _windowWidget->setStyleSheet(windowWidgetStyleSheet);
+
+ QPainterPath path;
+ path.addRoundedRect(_windowWidget->rect(), _cornerRadius - 1, _cornerRadius - 1);
+ QRegion mask(path.toFillPolygon().toPolygon());
+ _windowWidget->setMask(mask);
+
+ Logger::debug("Restoring window:");
+ Logger::debug("[+] current window position: " + std::to_string(x()) + ", " + std::to_string(y()));
+ Logger::debug("[+] current window size: " + std::to_string(width()) + ", " + std::to_string(height()));
+ Logger::debug("[+] current geometry: " + std::to_string(frameGeometry().x()) + ", " + std::to_string(frameGeometry().y()) + ", " + std::to_string(frameGeometry().width()) + ", " + std::to_string(frameGeometry().height()));
+ Logger::debug("[+] current window widget position: " + std::to_string(_windowWidget->x()) + ", " + std::to_string(_windowWidget->y()));
+ Logger::debug("[+] current window widget size: " + std::to_string(_windowWidget->width()) + ", " + std::to_string(_windowWidget->height()));
+
+ _maximized = false;
+ }
+}
+
+void FramelessWindow::updateMouseState(QMouseEvent* event) {
+ _mouseState = MOUSE_STATE_NONE;
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_RESIZE) != 0) {
+ return;
+ }
+ if (_maximized) {
+ setCursor(Qt::ArrowCursor);
+ return;
+ }
+ // Set mouse state according to abs distance from window border
+ if (abs(event->globalPos().x() - (_windowWidget->frameGeometry().left() + frameGeometry().left())) < 5) {
+ _mouseState |= MOUSE_STATE_RESIZE_LEFT;
+ }
+ if (abs(event->globalPos().x() - (_windowWidget->frameGeometry().right() + frameGeometry().left())) < 5) {
+ _mouseState |= MOUSE_STATE_RESIZE_RIGHT;
+ }
+ if (abs(event->globalPos().y() - (_windowWidget->frameGeometry().top() + frameGeometry().top())) < 5) {
+ _mouseState |= MOUSE_STATE_RESIZE_TOP;
+ }
+ if (abs(event->globalPos().y() - (_windowWidget->frameGeometry().bottom() + frameGeometry().top())) < 5) {
+ _mouseState |= MOUSE_STATE_RESIZE_BOTTOM;
+ }
+ // Set cursor shape according to mouse state
+ switch (_mouseState) {
+ case MOUSE_STATE_RESIZE_LEFT:
+ case MOUSE_STATE_RESIZE_RIGHT:
+ setCursor(Qt::SizeHorCursor);
+ break;
+ case MOUSE_STATE_RESIZE_TOP:
+ case MOUSE_STATE_RESIZE_BOTTOM:
+ setCursor(Qt::SizeVerCursor);
+ break;
+ case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_TOP:
+ case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_BOTTOM:
+ setCursor(Qt::SizeFDiagCursor);
+ break;
+ case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_BOTTOM:
+ case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_TOP:
+ setCursor(Qt::SizeBDiagCursor);
+ break;
+ default:
+ setCursor(Qt::ArrowCursor);
+ break;
+ }
+}
+
+void FramelessWindow::mousePressEvent(QMouseEvent* event) {
+ if (event->button() == Qt::LeftButton) {
+ _mousePressed = true;
+ _lastMousePosition = event->globalPos().toPointF();
+ }
+}
+
+void FramelessWindow::mouseReleaseEvent(QMouseEvent* event) {
+ _mousePressed = false;
+ QScreen* screen = QGuiApplication::screenAt(event->globalPos());
+ Logger::debug("Current screen geometry:");
+ Logger::debug("[+] screen position: " + std::to_string(screen->geometry().x()) + ", " + std::to_string(screen->geometry().y()));
+ Logger::debug("[+] screen size: " + std::to_string(screen->geometry().width()) + ", " + std::to_string(screen->geometry().height()));
+ if (abs(event->globalPos().y() - screen->geometry().top()) < 5) {
+ controlWindowScale();
+ }
+ updateMouseState(event);
+}
+
+void FramelessWindow::mouseMoveEvent(QMouseEvent* event) {
+ Logger::debug("Detected mouse move");
+ Logger::debug("[+] mouse global position : " + std::to_string(event->globalPos().x()) + ", " + std::to_string(event->globalPos().y()));
+ Logger::debug("[+] window geometry: " + std::to_string(frameGeometry().x()) + ", " + std::to_string(frameGeometry().y()) + ", " + std::to_string(frameGeometry().width()) + ", " + std::to_string(frameGeometry().height()));
+ Logger::debug("[+] widget frame geometry: " + std::to_string(_windowWidget->frameGeometry().x()) + ", " + std::to_string(_windowWidget->frameGeometry().y()));
+ Logger::debug("[+] widget frame size: " + std::to_string(_windowWidget->frameGeometry().width()) + ", " + std::to_string(_windowWidget->frameGeometry().height()));
+ if (event->buttons() == Qt::NoButton) {
+ _mousePressed = false;
+ }
+ if (abs(event->globalPos().x() - _lastMousePosition.x()) > MAX_MOUSE_MOVEMENT) {
+ // Maybe moving window across monitors, avoid window disappear
+ _lastMousePosition = event->globalPos().toPointF();
+ }
+ if (abs(event->globalPos().y() - _lastMousePosition.y()) > MAX_MOUSE_MOVEMENT) {
+ // Maybe moving window across monitors, avoid window disappear
+ _lastMousePosition = event->globalPos().toPointF();
+ }
+ if (!_mousePressed) {
+ updateMouseState(event);
+ }
+ else {
+ // Resize window according to mouse state
+ switch (_mouseState) {
+ case MOUSE_STATE_RESIZE_LEFT:
+ resize(frameGeometry().width() - event->globalPos().x() + frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height());
+ move(event->globalPos().x() - _windowWidget->frameGeometry().left(), frameGeometry().top());
+ break;
+ case MOUSE_STATE_RESIZE_RIGHT:
+ resize(event->globalPos().x() - frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height());
+ break;
+ case MOUSE_STATE_RESIZE_TOP:
+ resize(frameGeometry().width(), frameGeometry().height() - event->globalPos().y() + frameGeometry().top() + _windowWidget->frameGeometry().top());
+ move(frameGeometry().left(), event->globalPos().y() - _windowWidget->frameGeometry().top());
+ break;
+ case MOUSE_STATE_RESIZE_BOTTOM:
+ resize(frameGeometry().width(), event->globalPos().y() - frameGeometry().top() + _windowWidget->frameGeometry().top());
+ break;
+ case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_TOP:
+ resize(frameGeometry().width() - event->globalPos().x() + frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height() - event->globalPos().y() + frameGeometry().top() + _windowWidget->frameGeometry().top());
+ move(event->globalPos().x() - _windowWidget->frameGeometry().left(), event->globalPos().y() - _windowWidget->frameGeometry().top());
+ break;
+ case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_BOTTOM:
+ resize(frameGeometry().width() - event->globalPos().x() + frameGeometry().left() + _windowWidget->frameGeometry().left(), event->globalPos().y() - frameGeometry().top() + _windowWidget->frameGeometry().top());
+ move(event->globalPos().x() - _windowWidget->frameGeometry().left(), frameGeometry().top());
+ break;
+ case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_TOP:
+ resize(event->globalPos().x() - frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height() - event->globalPos().y() + frameGeometry().top() + _windowWidget->frameGeometry().top());
+ move(frameGeometry().left(), event->globalPos().y() - _windowWidget->frameGeometry().top());
+ break;
+ case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_BOTTOM:
+ resize(event->globalPos().x() - frameGeometry().left() + _windowWidget->frameGeometry().left(), event->globalPos().y() - frameGeometry().top() + _windowWidget->frameGeometry().top());
+ break;
+ default:
+ if (_maximized) {
+ qreal wRatio = (qreal)event->pos().x() / (qreal)_windowWidget->width();
+ controlWindowScale();
+ move(event->globalPos().x() - _windowWidget->width() * wRatio, event->globalPos().y() - 52);
+ }
+ else {
+ move(frameGeometry().left() + event->globalPos().x() - _lastMousePosition.x(), frameGeometry().top() + event->globalPos().y() - _lastMousePosition.y());
+ }
+ break;
+ }
+ _lastMousePosition = event->globalPos().toPointF();
+ }
+}
+
+FramelessWindow::LUI_WINDOW_ATTRIBUTES FramelessWindow::getWindowAttributes() {
+ return _attributes;
+}
+
+void FramelessWindow::setWindowAttributes(unsigned int attributes) {
+ _attributes = (LUI_WINDOW_ATTRIBUTES)attributes;
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_MINIMIZE) == 0) {
+ _minimizeBtn->show();
+ }
+ else {
+ _minimizeBtn->hide();
+ }
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_MAXIMIZE) == 0) {
+ _maximizeBtn->show();
+ }
+ else {
+ _maximizeBtn->hide();
+ }
+ if ((_attributes & LUI_WINDOW_ATTRIBUTES::LUI_WINDOW_DISABLE_CLOSE) == 0) {
+ _closeBtn->show();
+ }
+ else {
+ _closeBtn->hide();
+ }
+}
diff --git a/FinalProject/framelesswindow.h b/FinalProject/framelesswindow.h
new file mode 100644
index 0000000..bfebee1
--- /dev/null
+++ b/FinalProject/framelesswindow.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+class FramelessWindow : public QWidget {
+
+ Q_OBJECT
+
+public:
+ // Window attributes
+ enum LUI_WINDOW_ATTRIBUTES {
+ LUI_WINDOW_NO_ATTRIBUTES = 0,
+ LUI_WINDOW_DISABLE_CLOSE = 1 << 0,
+ LUI_WINDOW_DISABLE_MAXIMIZE = 1 << 1,
+ LUI_WINDOW_DISABLE_MINIMIZE = 1 << 2,
+ LUI_WINDOW_DISABLE_RESIZE = 1 << 3
+ };
+
+public:
+ FramelessWindow(QWidget* parent = 0);
+ FramelessWindow(int cornerRadius, QWidget* parent = 0);
+ FramelessWindow(unsigned int attributes, QWidget* parent = 0);
+ FramelessWindow(int cornerRadius, unsigned int attributes, QWidget* parent = 0);
+ ~FramelessWindow();
+
+protected:
+ // UI control variables
+ const int _cornerRadius = 0;
+ const QColor _backgroundColor = QColor(251, 251, 251);
+ const QColor _borderColor = QColor(104, 104, 104);
+
+ // Window initialize
+ bool _initialized = false; // prevent double initialization when restore from minimized state
+ void initializeWindowUI();
+ virtual void showEvent(QShowEvent* event) override;
+
+ // Widget list
+ QVBoxLayout* _stretchLayout = nullptr;
+
+ QWidget* _windowWidget = nullptr;
+ QWidget* _windowBorder = nullptr;
+ QGraphicsDropShadowEffect* _windowShadow = nullptr;
+
+ QWidget* _windowBtnWidget = nullptr;
+ QHBoxLayout* _windowBtnLayout = nullptr;
+ QPushButton* _minimizeBtn = nullptr;
+ QPushButton* _maximizeBtn = nullptr;
+ QPushButton* _closeBtn = nullptr;
+
+ // Window size control
+ LUI_WINDOW_ATTRIBUTES _attributes = LUI_WINDOW_NO_ATTRIBUTES;
+ bool _maximized = false;
+ QRect _lastWindowGeometry;
+
+ virtual void resizeEvent(QResizeEvent* event) override;
+ void controlWindowScale();
+
+ // User interaction control
+ enum MOUSE_STATE {
+ MOUSE_STATE_NONE = 0,
+ MOUSE_STATE_RESIZE_LEFT = 1 << 0,
+ MOUSE_STATE_RESIZE_TOP = 1 << 1,
+ MOUSE_STATE_RESIZE_RIGHT = 1 << 2,
+ MOUSE_STATE_RESIZE_BOTTOM = 1 << 3
+ };
+ bool _mousePressed = false;
+ int _mouseState = MOUSE_STATE_NONE;
+ QPointF _lastMousePosition;
+ void updateMouseState(QMouseEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event) override;
+ virtual void mouseReleaseEvent(QMouseEvent* event) override;
+ virtual void mouseMoveEvent(QMouseEvent* event) override;
+
+public:
+ QWidget* windowWidget() const { return _windowWidget; }
+ LUI_WINDOW_ATTRIBUTES getWindowAttributes();
+ void setWindowAttributes(unsigned int attributes);
+};
\ No newline at end of file
diff --git a/FinalProject/lineeditwidget.cpp b/FinalProject/lineeditwidget.cpp
new file mode 100644
index 0000000..ca4ec86
--- /dev/null
+++ b/FinalProject/lineeditwidget.cpp
@@ -0,0 +1,427 @@
+#include
+#include
+
+#include "lineeditwidget.h"
+
+LineEditWidget::LineEditWidget(QWidget* parent) :
+ QWidget(parent)
+{
+ // Generate colors
+ generateColor(_defaultColorScheme);
+
+ // Initialize ui
+ initializeUI();
+
+ // Connect line edit events
+ connect(_editor, &QLineEdit::returnPressed, this, &LineEditWidget::endEdit);
+ connect(_editor, &QLineEdit::editingFinished, this, &LineEditWidget::endEdit);
+ connect(_editor, &QLineEdit::textChanged, this, &LineEditWidget::onTextChanged);
+}
+
+LineEditWidget::~LineEditWidget() {
+}
+
+void LineEditWidget::initializeUI() {
+ // Construct and set main layout
+ _mainLayout = new QHBoxLayout(this);
+ _mainLayout->setContentsMargins(12, 0, 12, 0);
+ _mainLayout->setSpacing(8);
+ setLayout(_mainLayout);
+
+ // Construct editor container widget
+ _editorWidget = new QWidget(this);
+ _mainLayout->addWidget(_editorWidget);
+ _editorWidget->show();
+
+ // Construct editor layout to stretch editor widget
+ _editorWidgetLayout = new QHBoxLayout(_editorWidget);
+ _editorWidgetLayout->setContentsMargins(0, 10, 0, 10);
+ _editorWidgetLayout->setSpacing(0);
+ _editorWidget->setLayout(_editorWidgetLayout);
+
+ // Install event filter to editor widget to resize indicator
+ _editorWidget->installEventFilter(this);
+
+ // Construct real text editor
+ _editor = new QLineEdit(_editorWidget);
+ _editor->setText("");
+ _editor->setFont(_defaultFont);
+ _editor->setAlignment(Qt::AlignRight);
+ _editor->setReadOnly(true);
+ _editor->setAttribute(Qt::WA_TransparentForMouseEvents, true);
+ _editor->setStyleSheet("QLineEdit{color:#2c2c2c;background-color:#00ffffff;border-style:none;}");
+ //_editor->setFixedHeight(_editor->fontMetrics().lineSpacing()); // Restrict one line
+ _editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ _editorWidgetLayout->addWidget(_editor);
+ _editor->show();
+
+ // Construct indicator widget
+ _indicator = new QWidget(_editorWidget);
+ _indicator->setObjectName("indicator");
+ _indicator->setStyleSheet("QWidget#indicator{background-color:" + _indicatorColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number((float)_indicatorWidth / 2) + "px;}");
+ _indicator->resize(_indicatorWidth, _indicatorWidth);
+ _indicator->move(_editorWidget->width() - _indicatorWidth, _editorWidget->height() - _indicatorWidth - _indicatorSpacing);
+ _indicatorEffect = new QGraphicsOpacityEffect(_indicator);
+ _indicatorEffect->setOpacity(0);
+ _indicator->setGraphicsEffect(_indicatorEffect);
+ _indicator->show();
+
+ // Construct background widget
+ _backgroundWidget = new QWidget(this);
+ _backgroundWidget->resize(size());
+ _backgroundWidget->setObjectName("backgroundWidget");
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ _backgroundWidget->lower();
+ _backgroundWidget->show();
+
+ // Set size policy
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+}
+
+void LineEditWidget::generateColor(QColor colorScheme) {
+ _backgroundColor = colorScheme.lighter(120);
+ _backgroundColor.setAlpha(5);
+ _hoverColor = colorScheme.lighter(120);
+ _hoverColor.setAlpha(40);
+ _pressedColor = colorScheme.lighter(120);
+ _pressedColor.setAlpha(50);
+ _indicatorColor = colorScheme;
+}
+
+void LineEditWidget::startEdit() {
+ if (_editing) {
+ return;
+ }
+
+ // Set editing flag
+ _editing = true;
+
+ // Enable qlineedit widget
+ _editor->setReadOnly(false);
+ _editor->setAttribute(Qt::WA_TransparentForMouseEvents, false);
+ _editor->setFocus();
+ _editor->setCursorPosition(_editor->text().length());
+
+ // Minorly move cursor to update cursor icon
+ QCursor::setPos(QCursor::pos() + QPoint(1, 0));
+ QCursor::setPos(QCursor::pos() + QPoint(-1, 0));
+
+ // Add grow and fade in animation for indicator
+ QParallelAnimationGroup* startEditAnimation = new QParallelAnimationGroup(this);
+ QPropertyAnimation* growAnimation = new QPropertyAnimation(_indicator, "geometry");
+ QPropertyAnimation* fadeInAnimation = new QPropertyAnimation(_indicatorEffect, "opacity");
+ growAnimation->setDuration(200);
+ growAnimation->setEasingCurve(QEasingCurve::OutQuad);
+ fadeInAnimation->setDuration(200);
+ fadeInAnimation->setEasingCurve(QEasingCurve::OutQuad);
+ growAnimation->setStartValue(_indicator->geometry());
+ growAnimation->setEndValue(QRect(
+ 0,
+ _editorWidget->height() - _indicatorWidth - _indicatorSpacing,
+ _editorWidget->width(),
+ _indicatorWidth
+ ));
+ fadeInAnimation->setStartValue(_indicatorEffect->opacity());
+ fadeInAnimation->setEndValue(0.999);
+ startEditAnimation->addAnimation(growAnimation);
+ startEditAnimation->addAnimation(fadeInAnimation);
+ startEditAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ // Emit signal
+ emit onStartEditing(_editor->text());
+}
+
+void LineEditWidget::endEdit() {
+ if (!_editing) {
+ return;
+ }
+
+ // Set editing flag
+ _editing = false;
+
+ // Disable qlineedit widget
+ _editor->setReadOnly(true);
+ _editor->setAttribute(Qt::WA_TransparentForMouseEvents, true);
+ _editor->setSelection(0, 0);
+
+ // Add shrink and fade out animation for indicator
+ QParallelAnimationGroup* endEditAnimation = new QParallelAnimationGroup(this);
+ QPropertyAnimation* shrinkAnimation = new QPropertyAnimation(_indicator, "geometry");
+ QPropertyAnimation* fadeOutAnimation = new QPropertyAnimation(_indicatorEffect, "opacity");
+ shrinkAnimation->setDuration(200);
+ shrinkAnimation->setEasingCurve(QEasingCurve::OutQuad);
+ fadeOutAnimation->setDuration(200);
+ fadeOutAnimation->setEasingCurve(QEasingCurve::OutQuad);
+ shrinkAnimation->setStartValue(_indicator->geometry());
+ shrinkAnimation->setEndValue(QRect(
+ _editorWidget->width() - _indicatorWidth,
+ _editorWidget->height() - _indicatorWidth - _indicatorSpacing,
+ _indicatorWidth,
+ _indicatorWidth
+ ));
+ fadeOutAnimation->setStartValue(_indicatorEffect->opacity());
+ fadeOutAnimation->setEndValue(0);
+ endEditAnimation->addAnimation(shrinkAnimation);
+ endEditAnimation->addAnimation(fadeOutAnimation);
+ endEditAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ // Emit signal
+ emit onEndEditing(_editor->text());
+}
+
+void LineEditWidget::showEvent(QShowEvent* event) {
+ // Call on parent
+ QWidget::showEvent(event);
+
+ // Check initialize state
+ if (_initialized) {
+ return;
+ }
+
+ // Initialize size dependent widgets
+ _backgroundWidget->resize(size());
+ if (_editing) {
+ _indicator->move(0, _editorWidget->height() - _indicatorWidth - _indicatorSpacing);
+ _indicator->resize(_editorWidget->width(), _indicatorWidth);
+ }
+ else {
+ _indicator->move(_editorWidget->width() - _indicatorWidth, _editorWidget->height() - _indicatorWidth - _indicatorSpacing);
+ _indicator->resize(_indicatorWidth, _indicatorWidth);
+ }
+
+ // Set initialized flag
+ _initialized = true;
+}
+
+void LineEditWidget::enterEvent(QEnterEvent* event) {
+ // Check on enabled
+ if (_enabled) {
+ setCursor(Qt::PointingHandCursor);
+ }
+
+ // Change background color
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+
+ // Set hover flag
+ _hovered = true;
+}
+
+void LineEditWidget::leaveEvent(QEvent* event) {
+ setCursor(Qt::ArrowCursor);
+
+ // Change background color
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+
+ // Set hover flag
+ _hovered = false;
+ _pressed = false;
+}
+
+void LineEditWidget::mousePressEvent(QMouseEvent* event) {
+ // Change background color
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _pressedColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+
+ // Set pressed flag
+ _pressed = true;
+}
+
+void LineEditWidget::mouseReleaseEvent(QMouseEvent* event) {
+ // Change background color
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+
+ // Trigger on click
+ if (_pressed) {
+ if (_enabled) {
+ if (_editing) {
+ endEdit();
+ }
+ else {
+ startEdit();
+ }
+ }
+ else {
+ if (_editing) {
+ endEdit();
+ }
+ }
+ }
+
+ // Set pressed flag
+ _pressed = false;
+}
+
+void LineEditWidget::focusInEvent(QFocusEvent* event) {
+ // Call on parent
+ QWidget::focusInEvent(event);
+
+ // Check on enabled
+ if (!_enabled) {
+ return;
+ }
+
+ // Change background color
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+
+ // Start edit
+ if (!_editing) {
+ startEdit();
+ }
+}
+
+void LineEditWidget::focusOutEvent(QFocusEvent* event) {
+ // Call on parent
+ QWidget::focusOutEvent(event);
+
+ // Change background color
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+
+ // End edit
+ if (_editing) {
+ endEdit();
+ }
+}
+
+void LineEditWidget::resizeEvent(QResizeEvent* event) {
+ // Check initialize state
+ if (!_initialized) {
+ return;
+ }
+
+ // Resize background widget
+ _backgroundWidget->resize(size());
+}
+
+bool LineEditWidget::eventFilter(QObject* object, QEvent* event) {
+ // Resize indicator when editor widget size changed
+ if (object == _editorWidget) {
+ if (event->type() == QEvent::Resize) {
+ if (_editing) {
+ _indicator->move(0, _editorWidget->height() - _indicatorWidth - _indicatorSpacing);
+ _indicator->resize(_editorWidget->width(), _indicatorWidth);
+ }
+ else {
+ _indicator->move(_editorWidget->width() - _indicatorWidth, _editorWidget->height() - _indicatorWidth - _indicatorSpacing);
+ _indicator->resize(_indicatorWidth, _indicatorWidth);
+ }
+ }
+ }
+
+ // Call on parent
+ return QWidget::eventFilter(object, event);
+}
+
+void LineEditWidget::setText(const QString& text) {
+ // Set text
+ _editor->setText(text);
+}
+
+void LineEditWidget::setPlaceholderText(const QString& text) {
+ // Set placeholder text
+ _editor->setPlaceholderText(text);
+}
+
+void LineEditWidget::setValidator(const QValidator* validator) {
+ // Set validator
+ _editor->setValidator(validator);
+}
+
+void LineEditWidget::setEnabled(bool enabled) {
+ // Set enabled
+ _enabled = enabled;
+
+ // Check for current state
+ if (_editing && !_enabled) {
+ endEdit();
+ }
+}
+
+void LineEditWidget::setMargins(QMargins margins) {
+ // Set margins
+ _mainLayout->setContentsMargins(margins);
+}
+
+void LineEditWidget::setMargins(int left, int top, int right, int bottom) {
+ // Set margins
+ _mainLayout->setContentsMargins(left, top, right, bottom);
+}
+
+void LineEditWidget::setBackgroundColor(QColor color) {
+ // Set background color
+ _backgroundColor = color;
+
+ // Check for current state
+ if (!_hovered && !_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ }
+}
+
+void LineEditWidget::setHoverColor(QColor color) {
+ // Set hover color
+ _hoverColor = color;
+
+ // Check for current state
+ if (_hovered && !_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ }
+}
+
+void LineEditWidget::setPressedColor(QColor color) {
+ // Set pressed color
+ _pressedColor = color;
+
+ // Check for current state
+ if (_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _pressedColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ }
+}
+
+void LineEditWidget::setIndicatorColor(QColor color) {
+ // Set indicator color
+ _indicatorColor = color;
+
+ _indicator->setStyleSheet("QWidget#indicator{background-color:" + _indicatorColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_indicatorWidth) + "px;}");
+}
+
+void LineEditWidget::setColorScheme(QColor primaryColor) {
+ // Generate colors
+ generateColor(primaryColor);
+
+ // Check for current state
+ if (!_hovered && !_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ }
+ else if (_hovered && !_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ }
+ else if (_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _pressedColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ }
+
+ _indicator->setStyleSheet("QWidget#indicator{background-color:" + _indicatorColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_indicatorWidth) + "px;}");
+}
+
+QString LineEditWidget::text() const {
+ // Return text
+ return _editor->text();
+}
+
+QHBoxLayout* LineEditWidget::mainLayout() const {
+ // Return main layout
+ return _mainLayout;
+}
\ No newline at end of file
diff --git a/FinalProject/lineeditwidget.h b/FinalProject/lineeditwidget.h
new file mode 100644
index 0000000..ac7a444
--- /dev/null
+++ b/FinalProject/lineeditwidget.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+class LineEditWidget : public QWidget {
+
+ Q_OBJECT
+
+public:
+ LineEditWidget(QWidget* parent = 0);
+ ~LineEditWidget();
+
+private:
+ // UI control variables
+ int _cornerRadius = 8;
+
+ // UI elements
+ QHBoxLayout* _mainLayout = nullptr; // For layout the user added components and the real editor component
+
+ QWidget* _backgroundWidget = nullptr;
+ const QColor _defaultColorScheme = QColor(58, 143, 183);
+ QColor _backgroundColor;
+ QColor _hoverColor;
+ QColor _pressedColor;
+
+ QWidget* _editorWidget = nullptr; // Container widget for the real editor component
+ QHBoxLayout* _editorWidgetLayout = nullptr;
+
+ QLineEdit* _editor = nullptr;
+ const QFont _defaultFont = QFont("DengXian", 10, QFont::Normal);
+
+ QWidget* _indicator = nullptr;
+ const int _indicatorWidth = 4;
+ const int _indicatorSpacing = 4;
+ QColor _indicatorColor;
+ QGraphicsOpacityEffect* _indicatorEffect;
+
+ // Editor state
+ bool _hovered = false;
+ bool _pressed = false;
+ bool _enabled = true;
+ bool _editing = false;
+ bool _initialized = false;
+
+private:
+ // UI Util functions
+ void initializeUI();
+ void generateColor(QColor colorScheme);
+
+ // Interaction Util functions
+ void startEdit();
+ void endEdit();
+
+private:
+ // Interactions
+ virtual void showEvent(QShowEvent* event) override;
+ virtual void enterEvent(QEnterEvent* event) override;
+ virtual void leaveEvent(QEvent* event) override;
+ virtual void mousePressEvent(QMouseEvent* event) override;
+ virtual void mouseReleaseEvent(QMouseEvent* event) override;
+ virtual void focusInEvent(QFocusEvent* event) override;
+ virtual void focusOutEvent(QFocusEvent* event) override;
+ virtual void resizeEvent(QResizeEvent* event) override;
+ virtual bool eventFilter(QObject* obj, QEvent* event) override;
+
+public:
+ // Public setter APIs for text
+ void setText(const QString& text);
+ void setPlaceholderText(const QString& text);
+ void setValidator(const QValidator* validator);
+
+ // Publics setter APIs for UI
+ void setEnabled(bool enabled = true);
+ void setMargins(QMargins margins);
+ void setMargins(int left, int top, int right, int bottom);
+ void setBackgroundColor(QColor color);
+ void setHoverColor(QColor color);
+ void setPressedColor(QColor color);
+ void setIndicatorColor(QColor color);
+ void setColorScheme(QColor primaryColor);
+
+ // Public getter APIs
+ QString text() const;
+ QHBoxLayout* mainLayout() const;
+
+signals:
+ // Public signal functions
+ void onStartEditing(const QString& text);
+ void onTextChanged(const QString& text);
+ void onEndEditing(const QString& text);
+};
\ No newline at end of file
diff --git a/FinalProject/main.cpp b/FinalProject/main.cpp
index fddc5d1..12cef37 100644
--- a/FinalProject/main.cpp
+++ b/FinalProject/main.cpp
@@ -1,13 +1,21 @@
-#include "mainwindow.h"
#include
+#include
+#include
+
+#include "mainwindow.h"
+#include "logger.h"
int main(int argc, char *argv[])
{
- QApplication a(argc, argv);
+ QApplication a(argc, argv); // Create main application instance
+
+ 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
+
MainWindow w;
- w.setWindowFlag(Qt::FramelessWindowHint);
- w.setAttribute(Qt::WA_TranslucentBackground);
w.setMouseTracking(true);
+ w.resize(900, 600);
w.show();
+
return a.exec();
}
diff --git a/FinalProject/mainwindow.cpp b/FinalProject/mainwindow.cpp
index 8c67927..dee9516 100644
--- a/FinalProject/mainwindow.cpp
+++ b/FinalProject/mainwindow.cpp
@@ -1,318 +1,97 @@
-#include
-#include
-
#include "mainwindow.h"
-#include "sceneviewer.h"
#include "logger.h"
#define MAX_MOUSE_MOVEMENT 300
MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
+ : FramelessWindow(20, parent)
{
- ui.setupUi(this);
- ui.centralWidget->setMouseTracking(true);
-
- // Create and properly set real displayed window widget
- _stretchLayout = new QVBoxLayout(this);
- _stretchLayout->setContentsMargins(30, 30, 30, 30);
- _windowWidget = new QWidget(this);
- _windowWidget->setObjectName("windowWidget");
- _windowWidget->setMouseTracking(true);
- _windowLayout = new QVBoxLayout(_windowWidget);
- _windowLayout->setContentsMargins(0, 0, 0, 0);
- _windowLayout->setSpacing(0);
- _windowLayout->setAlignment(Qt::AlignTop);
- _windowWidget->setLayout(_windowLayout);
- _stretchLayout->addWidget(_windowWidget);
- _windowWidget->show();
- ui.centralWidget->setLayout(_stretchLayout);
+ // Create main layout for page and sidebar
+ _mainLayout = new QHBoxLayout(_windowWidget);
+ _mainLayout->setContentsMargins(0, 0, 0, 0);
+ _mainLayout->setSpacing(0);
+ _windowWidget->setLayout(_mainLayout);
+
+ // Create placeholder widget for pages
+ _placeHolderWidget = new QWidget(_windowWidget);
+ _placeHolderWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _placeHolderWidget->setMouseTracking(true);
- // Set style sheet for window widget
- QString windowWidgetStyleSheet = "QWidget#windowWidget{background-color:" + _backgroundColor.name() + ";border-radius:" + QString::number(_cornerRadius) + "px;}";
- _windowWidget->setStyleSheet(windowWidgetStyleSheet);
-
- // Set shadow for window widget
- _windowShadow = new QGraphicsDropShadowEffect(_windowWidget);
- _windowShadow->setBlurRadius(30);
- _windowShadow->setColor(QColor(0, 0, 0));
- _windowShadow->setOffset(0, 0);
- _windowWidget->setGraphicsEffect(_windowShadow);
-
- // Create title bar widget
- _titleBar = new QWidget(_windowWidget);
- _titleBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
- _titleBar->setMouseTracking(true);
- _titleBarLayout = new QHBoxLayout(_titleBar);
- _titleBarLayout->setContentsMargins(18, 18, 18, 18);
- _titleBarLayout->setSpacing(0);
- _titleBarLayout->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
- _titleBar->setLayout(_titleBarLayout);
- _windowLayout->addWidget(_titleBar);
- _titleBar->show();
-
- // Create window control buttons container widget & its layout
- _windowBtnWidget = new QWidget(_titleBar);
- _windowBtnWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
- _windowBtnLayout = new QHBoxLayout(_windowBtnWidget);
- _windowBtnLayout->setContentsMargins(0, 0, 0, 0);
- _windowBtnLayout->setSpacing(8);
- _windowBtnWidget->setLayout(_windowBtnLayout);
- _titleBarLayout->addWidget(_windowBtnWidget);
- _windowBtnWidget->show();
+ // Connect the resize event of the placeholder widget to the resizePages function using event filter
+ _placeHolderWidget->installEventFilter(this);
- // Create window control buttons
- _minimizeBtn = new QPushButton(_windowBtnWidget);
- _maximizeBtn = new QPushButton(_windowBtnWidget);
- _closeBtn = new QPushButton(_windowBtnWidget);
+ // Create sidebar
+ _sideBar = new SideBar(_windowWidget);
+ connect(_sideBar, &SideBar::onPageChanged, this, [=](PageWidget* previousPage, PageWidget* currentPage) {
+ // Check for input validity
+ if (previousPage == currentPage) {
+ return;
+ }
+ if (previousPage != nullptr) {
+ previousPage->offStage();
+ }
+ if (currentPage != nullptr) {
+ currentPage->onStage();
+ }
+ });
+ _sideBar->setMouseTracking(true);
+
+ // Add sidebar and placeholder widget to main layout
+ _mainLayout->addWidget(_sideBar);
+ _mainLayout->addWidget(_placeHolderWidget);
- _minimizeBtn->setFixedSize(12, 12);
- _maximizeBtn->setFixedSize(12, 12);
- _closeBtn->setFixedSize(12, 12);
+ // Create editor page and connect to side bar
+ _editorPage = new EditorPage(_placeHolderWidget);
+ _editorPage->setMouseTracking(true);
+ _sideBar->addPage(_editorPage);
- _minimizeBtn->setStyleSheet("QPushButton{border-radius: 6px; background-color: #828282;}"
- "QPushButton:hover{background-color: #e98b2a;}");
- _maximizeBtn->setStyleSheet("QPushButton{border-radius: 6px; background-color: #828282;}"
- "QPushButton:hover{background-color: #2d6d4b;}");
- _closeBtn->setStyleSheet("QPushButton{border-radius: 6px; background-color: #828282;}"
- "QPushButton:hover{background-color: #ab3b3a;}");
-
- _windowBtnLayout->addWidget(_minimizeBtn);
- _windowBtnLayout->addWidget(_maximizeBtn);
- _windowBtnLayout->addWidget(_closeBtn);
- _minimizeBtn->show();
- _maximizeBtn->show();
- _closeBtn->show();
-
- // Connect window control buttons
- connect(_minimizeBtn, &QPushButton::clicked, this, &MainWindow::showMinimized);
- connect(_maximizeBtn, &QPushButton::clicked, this, [=]() {controlWindowScale(); });
- connect(_closeBtn, &QPushButton::clicked, qApp, &QApplication::quit);
+ // Create about page and connect to side bar
+ _aboutPage = new AboutPage(_placeHolderWidget);
+ _aboutPage->setMouseTracking(true);
+ _sideBar->addPage(_aboutPage);
}
MainWindow::~MainWindow() {
}
-void MainWindow::showEvent(QShowEvent* event) {
- // Initialize window UI after window is shown
- initializeWindowUI();
-}
-
-void MainWindow::initializeWindowUI() {
- // Create a round cornered mask for window widget
- QPainterPath path;
- path.addRoundedRect(_windowWidget->rect(), _cornerRadius - 1, _cornerRadius - 1);
- QRegion region(path.toFillPolygon().toPolygon());
- _windowWidget->setMask(region);
-
- // Create a border for window widget (in order to hide zigzagged edges)
- _windowBorder = new QWidget(this);
- _windowBorder->setObjectName("windowBorder");
- QString windowBorderStyleSheet =
- "QWidget#windowBorder{background-color:#00FFFFFF;border:1.5px solid " + _borderColor.name() + ";border-radius:" + QString::number(_cornerRadius) + "px;}";
- _windowBorder->setStyleSheet(windowBorderStyleSheet);
- _windowBorder->setAttribute(Qt::WA_TransparentForMouseEvents);
- _windowBorder->move(_windowWidget->pos() - QPoint(1, 1));
- _windowBorder->resize(_windowWidget->size() + QSize(2, 2));
- _windowBorder->show();
-}
-
-void MainWindow::resizeEvent(QResizeEvent* event) {
- // Resize window border
- if (_windowBorder != nullptr) {
- _windowBorder->move(_windowWidget->pos() - QPoint(1, 1));
- _windowBorder->resize(_windowWidget->size() + QSize(2, 2));
- }
-
- // Resize window mask
- QPainterPath path;
- path.addRoundedRect(_windowWidget->rect(), _cornerRadius - 1, _cornerRadius - 1);
- QRegion region(path.toFillPolygon().toPolygon());
- _windowWidget->setMask(region);
-}
-
-void MainWindow::controlWindowScale() {
- if (!_maximized) {
- _lastWindowGeometry = frameGeometry();
-
- Logger::debug("Maximizing window:");
- Logger::debug("[+] current window position: " + std::to_string(x()) + ", " + std::to_string(y()));
- Logger::debug("[+] current window size: " + std::to_string(width()) + ", " + std::to_string(height()));
- Logger::debug("[+] current geometry: " + std::to_string(_lastWindowGeometry.x()) + ", " + std::to_string(_lastWindowGeometry.y()) + ", " + std::to_string(_lastWindowGeometry.width()) + ", " + std::to_string(_lastWindowGeometry.height()));
- Logger::debug("[+] current window widget position: " + std::to_string(_windowWidget->x()) + ", " + std::to_string(_windowWidget->y()));
- Logger::debug("[+] current window widget size: " + std::to_string(_windowWidget->width()) + ", " + std::to_string(_windowWidget->height()));
- Logger::debug("[+] current window border position: " + std::to_string(_windowBorder->x()) + ", " + std::to_string(_windowBorder->y()));
- Logger::debug("[+] current window border size: " + std::to_string(_windowBorder->width()) + ", " + std::to_string(_windowBorder->height()));
-
- _windowShadow->setEnabled(false);
- _windowBorder->hide();
- QString windowWidgetStyleSheet = "QWidget#windowWidget{background-color:" + _backgroundColor.name() + ";}";
- _windowWidget->setStyleSheet(windowWidgetStyleSheet);
-
- _stretchLayout->setContentsMargins(0, 0, 0, 0);
-
- showMaximized();
-
- QPainterPath path;
- path.addRect(_windowWidget->rect());
- QRegion mask(path.toFillPolygon().toPolygon());
- _windowWidget->setMask(mask);
-
- _maximized = true;
- }
- else {
- _stretchLayout->setContentsMargins(30, 30, 30, 30);
-
- showNormal();
-
- resize(_lastWindowGeometry.size());
- move(_lastWindowGeometry.topLeft());
-
- _windowShadow->setEnabled(true);
- _windowBorder->show();
- QString windowWidgetStyleSheet = "QWidget#windowWidget{background-color:" + _backgroundColor.name() + ";border-radius:" + QString::number(_cornerRadius) + "px;}";
- _windowWidget->setStyleSheet(windowWidgetStyleSheet);
-
- QPainterPath path;
- path.addRoundedRect(_windowWidget->rect(), _cornerRadius - 1, _cornerRadius - 1);
- QRegion mask(path.toFillPolygon().toPolygon());
- _windowWidget->setMask(mask);
-
- Logger::debug("Restoring window:");
- Logger::debug("[+] current window position: " + std::to_string(x()) + ", " + std::to_string(y()));
- Logger::debug("[+] current window size: " + std::to_string(width()) + ", " + std::to_string(height()));
- Logger::debug("[+] current geometry: " + std::to_string(frameGeometry().x()) + ", " + std::to_string(frameGeometry().y()) + ", " + std::to_string(frameGeometry().width()) + ", " + std::to_string(frameGeometry().height()));
- Logger::debug("[+] current window widget position: " + std::to_string(_windowWidget->x()) + ", " + std::to_string(_windowWidget->y()));
- Logger::debug("[+] current window widget size: " + std::to_string(_windowWidget->width()) + ", " + std::to_string(_windowWidget->height()));
-
- _maximized = false;
- }
-}
-
-void MainWindow::updateMouseState(QMouseEvent* event) {
- _mouseState = MOUSE_STATE_NONE;
- if (_maximized) {
+void MainWindow::resizePages(QResizeEvent* event) {
+ // Check for input validity
+ if (event == nullptr) {
return;
}
- // Set mouse state according to abs distance from window border
- if (abs(event->globalPos().x() - (_windowWidget->frameGeometry().left() + frameGeometry().left())) < 5) {
- _mouseState |= MOUSE_STATE_RESIZE_LEFT;
- }
- if (abs(event->globalPos().x() - (_windowWidget->frameGeometry().right() + frameGeometry().left())) < 5) {
- _mouseState |= MOUSE_STATE_RESIZE_RIGHT;
- }
- if (abs(event->globalPos().y() - (_windowWidget->frameGeometry().top() + frameGeometry().top())) < 5) {
- _mouseState |= MOUSE_STATE_RESIZE_TOP;
- }
- if (abs(event->globalPos().y() - (_windowWidget->frameGeometry().bottom() + frameGeometry().top())) < 5) {
- _mouseState |= MOUSE_STATE_RESIZE_BOTTOM;
- }
- // Set cursor shape according to mouse state
- switch (_mouseState) {
- case MOUSE_STATE_RESIZE_LEFT:
- case MOUSE_STATE_RESIZE_RIGHT:
- setCursor(Qt::SizeHorCursor);
- break;
- case MOUSE_STATE_RESIZE_TOP:
- case MOUSE_STATE_RESIZE_BOTTOM:
- setCursor(Qt::SizeVerCursor);
- break;
- case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_TOP:
- case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_BOTTOM:
- setCursor(Qt::SizeFDiagCursor);
- break;
- case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_BOTTOM:
- case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_TOP:
- setCursor(Qt::SizeBDiagCursor);
- break;
- default:
- setCursor(Qt::ArrowCursor);
- break;
- }
+
+ // Get the size of the placeholder widget
+ QSize size = event->size();
+
+ // Resize the editor page
+ _editorPage->resize(size);
+ _aboutPage->resize(size);
}
-void MainWindow::mousePressEvent(QMouseEvent* event) {
- if (event->button() == Qt::LeftButton) {
- _mousePressed = true;
- _lastMousePosition = event->globalPos().toPointF();
- }
+void MainWindow::showEvent(QShowEvent* event) {
+ // Call parent show event
+ FramelessWindow::showEvent(event);
+
+ // Resize all the pages based on the placeholder widget
+ _editorPage->resize(_placeHolderWidget->size());
+ _aboutPage->resize(_placeHolderWidget->size());
}
-void MainWindow::mouseReleaseEvent(QMouseEvent* event) {
- _mousePressed = false;
- QScreen* screen = QGuiApplication::screenAt(event->globalPos());
- Logger::debug("Current screen geometry:");
- Logger::debug("[+] screen position: " + std::to_string(screen->geometry().x()) + ", " + std::to_string(screen->geometry().y()));
- Logger::debug("[+] screen size: " + std::to_string(screen->geometry().width()) + ", " + std::to_string(screen->geometry().height()));
- if (abs(event->globalPos().y() - screen->geometry().top()) < 5) {
- controlWindowScale();
+bool MainWindow::eventFilter(QObject* object, QEvent* event) {
+ // Check for input validity
+ if (object == nullptr || event == nullptr) {
+ return false;
}
- updateMouseState(event);
-}
-void MainWindow::mouseMoveEvent(QMouseEvent* event) {
- Logger::debug("Detected mouse move");
- Logger::debug("[+] mouse global position : " + std::to_string(event->globalPos().x()) + ", " + std::to_string(event->globalPos().y()));
- Logger::debug("[+] window geometry: " + std::to_string(frameGeometry().x()) + ", " + std::to_string(frameGeometry().y()) + ", " + std::to_string(frameGeometry().width()) + ", " + std::to_string(frameGeometry().height()));
- Logger::debug("[+] widget frame geometry: " + std::to_string(_windowWidget->frameGeometry().x()) + ", " + std::to_string(_windowWidget->frameGeometry().y()));
- Logger::debug("[+] widget frame size: " + std::to_string(_windowWidget->frameGeometry().width()) + ", " + std::to_string(_windowWidget->frameGeometry().height()));
-; if (event->buttons() == Qt::NoButton) {
- _mousePressed = false;
- }
- if (abs(event->globalPos().x() - _lastMousePosition.x()) > MAX_MOUSE_MOVEMENT) {
- // Maybe moving window across monitors, avoid window disappear
- _lastMousePosition = event->globalPos().toPointF();
- }
- if (abs(event->globalPos().y() - _lastMousePosition.y()) > MAX_MOUSE_MOVEMENT) {
- // Maybe moving window across monitors, avoid window disappear
- _lastMousePosition = event->globalPos().toPointF();
- }
- if (!_mousePressed) {
- updateMouseState(event);
- }
- else {
- // Resize window according to mouse state
- switch (_mouseState) {
- case MOUSE_STATE_RESIZE_LEFT:
- resize(frameGeometry().width() - event->globalPos().x() + frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height());
- move(event->globalPos().x() - _windowWidget->frameGeometry().left(), frameGeometry().top());
- break;
- case MOUSE_STATE_RESIZE_RIGHT:
- resize(event->globalPos().x() - frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height());
- break;
- case MOUSE_STATE_RESIZE_TOP:
- resize(frameGeometry().width(), frameGeometry().height() - event->globalPos().y() + frameGeometry().top() + _windowWidget->frameGeometry().top());
- move(frameGeometry().left(), event->globalPos().y() - _windowWidget->frameGeometry().top());
- break;
- case MOUSE_STATE_RESIZE_BOTTOM:
- resize(frameGeometry().width(), event->globalPos().y() - frameGeometry().top() + _windowWidget->frameGeometry().top());
- break;
- case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_TOP:
- resize(frameGeometry().width() - event->globalPos().x() + frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height() - event->globalPos().y() + frameGeometry().top() + _windowWidget->frameGeometry().top());
- move(event->globalPos().x() - _windowWidget->frameGeometry().left(), event->globalPos().y() - _windowWidget->frameGeometry().top());
- break;
- case MOUSE_STATE_RESIZE_LEFT | MOUSE_STATE_RESIZE_BOTTOM:
- resize(frameGeometry().width() - event->globalPos().x() + frameGeometry().left() + _windowWidget->frameGeometry().left(), event->globalPos().y() + frameGeometry().top() - _windowWidget->frameGeometry().top());
- move(event->globalPos().x() - _windowWidget->frameGeometry().left(), frameGeometry().top());
- break;
- case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_TOP:
- resize(event->globalPos().x() - frameGeometry().left() + _windowWidget->frameGeometry().left(), frameGeometry().height() - event->globalPos().y() + frameGeometry().top() + _windowWidget->frameGeometry().top());
- move(frameGeometry().left(), event->globalPos().y() - _windowWidget->frameGeometry().top());
- break;
- case MOUSE_STATE_RESIZE_RIGHT | MOUSE_STATE_RESIZE_BOTTOM:
- resize(event->globalPos().x() - frameGeometry().left() + _windowWidget->frameGeometry().left(), event->globalPos().y() - frameGeometry().top() + _windowWidget->frameGeometry().top());
- break;
- default:
- if (_maximized) {
- qreal wRatio = (qreal)event->pos().x() / (qreal)_windowWidget->width();
- controlWindowScale();
- move(event->globalPos().x() - _windowWidget->width() * wRatio, event->globalPos().y() - 52);
- }
- else {
- move(frameGeometry().left() + event->globalPos().x() - _lastMousePosition.x(), frameGeometry().top() + event->globalPos().y() - _lastMousePosition.y());
- }
- break;
+ // Check if the object is the placeholder widget
+ if (object == _placeHolderWidget) {
+ // Check if the event is a resize event
+ if (event->type() == QEvent::Resize) {
+ // Resize all the pages
+ resizePages(static_cast(event));
}
- _lastMousePosition = event->globalPos().toPointF();
}
-}
+
+ // Call parent event filter
+ return FramelessWindow::eventFilter(object, event);
+}
\ No newline at end of file
diff --git a/FinalProject/mainwindow.h b/FinalProject/mainwindow.h
index 8376819..d6f63c0 100644
--- a/FinalProject/mainwindow.h
+++ b/FinalProject/mainwindow.h
@@ -1,12 +1,13 @@
#pragma once
-#include
-#include
-#include
-#include
-#include "ui_mainwindow.h"
+#include
-class MainWindow : public QMainWindow
+#include "framelesswindow.h"
+#include "sidebar.h"
+#include "editorpage.h"
+#include "aboutpage.h"
+
+class MainWindow : public FramelessWindow
{
Q_OBJECT
@@ -15,54 +16,21 @@ public:
~MainWindow();
private:
- Ui::MainWindowClass ui;
+ // Main ui layout
+ QHBoxLayout* _mainLayout = nullptr;
+
+ // Side bar and corresponding pages
+ SideBar* _sideBar = nullptr;
+ EditorPage* _editorPage = nullptr;
+ AboutPage* _aboutPage = nullptr;
+
+ // Place holder widget for resizing pages
+ QWidget* _placeHolderWidget = nullptr;
private:
- // UI control variables
- const int _cornerRadius = 20;
- const QColor _backgroundColor = QColor(251, 251, 251);
- const QColor _borderColor = QColor(104, 104, 104);
+ void resizePages(QResizeEvent* event);
- // Window initialize
- void initializeWindowUI();
+private:
virtual void showEvent(QShowEvent* event) override;
-
- // Widget list
- QVBoxLayout* _stretchLayout = nullptr;
- QWidget* _windowWidget = nullptr;
- QWidget* _windowBorder = nullptr;
- QGraphicsDropShadowEffect* _windowShadow = nullptr;
- QVBoxLayout* _windowLayout = nullptr;
- QWidget* _titleBar = nullptr;
- QHBoxLayout* _titleBarLayout = nullptr;
- QWidget* _windowBtnWidget = nullptr;
- QHBoxLayout* _windowBtnLayout = nullptr;
- QPushButton* _minimizeBtn = nullptr;
- QPushButton* _maximizeBtn = nullptr;
- QPushButton* _closeBtn = nullptr;
- QWidget* _objectList = nullptr;
- QWidget* _settingsPannel = nullptr;
-
- // Window size control
- bool _maximized = false;
- QRect _lastWindowGeometry;
-
- virtual void resizeEvent(QResizeEvent* event) override;
- void controlWindowScale();
-
- // User interaction control
- enum MOUSE_STATE {
- MOUSE_STATE_NONE = 0,
- MOUSE_STATE_RESIZE_LEFT = 1 << 0,
- MOUSE_STATE_RESIZE_TOP = 1 << 1,
- MOUSE_STATE_RESIZE_RIGHT = 1 << 2,
- MOUSE_STATE_RESIZE_BOTTOM = 1 << 3
- };
- bool _mousePressed = false;
- int _mouseState = MOUSE_STATE_NONE;
- QPointF _lastMousePosition;
- void updateMouseState(QMouseEvent* event);
- virtual void mousePressEvent(QMouseEvent* event) override;
- virtual void mouseReleaseEvent(QMouseEvent* event) override;
- virtual void mouseMoveEvent(QMouseEvent* event) override;
+ virtual bool eventFilter(QObject* object, QEvent* event) override;
};
diff --git a/FinalProject/mainwindow.qrc b/FinalProject/mainwindow.qrc
index 89c5a99..0a9d314 100644
--- a/FinalProject/mainwindow.qrc
+++ b/FinalProject/mainwindow.qrc
@@ -3,4 +3,8 @@
fragmentshader.fs
vertexshader.vs
+
+ font_awesome_6_regular_free.otf
+ font_awesome_6_solid_free.otf
+
diff --git a/FinalProject/mainwindow.ui b/FinalProject/mainwindow.ui
deleted file mode 100644
index 444ab27..0000000
--- a/FinalProject/mainwindow.ui
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
- MainWindowClass
-
-
-
- 0
- 0
- 600
- 400
-
-
-
- MainWindow
-
-
-
-
-
-
-
-
-
diff --git a/FinalProject/model.cpp b/FinalProject/model.cpp
index e1db286..f8e6296 100644
--- a/FinalProject/model.cpp
+++ b/FinalProject/model.cpp
@@ -13,7 +13,13 @@ Model::~Model() {
// TODO: Maybe delete all meshes?
}
-// file path is ...\\...\\.obj, and processnode & processmesh have been called here
+Model::Model(std::vector&& meshes, std::vector&& textures, std::string directory) {
+ _meshes = std::move(meshes);
+ _texturesLoaded = std::move(textures);
+ _directory = directory;
+ _status = LOADED;
+}
+
void Model::loadModel(std::string path) {
Logger::info("Loading model from path: " + path);
Assimp::Importer importer;
@@ -34,7 +40,7 @@ void Model::loadModel(std::string path) {
processNode(scene->mRootNode, scene);
_status = LOADED;
Logger::info("Model loaded");
- // ½ö¼ì²éÒ»´Î¼´¿É
+ // 仅检查一次å³å¯
//check_boundary();
}
@@ -67,7 +73,7 @@ Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene) {
glm::vec3 vertexBitangent = glm::vec3(0.0f);
// Process vertex positions
- //ʹÓÃÑ»·±ÜÃâ´úÂëÖØ¸´£¬Èç¹û¿ÉÐеϰ£¬¿ÉÒÔÔÚ´ËÑ»·ÖÐÈ·¶¨·¨ÏòÁ¿µÈÐÅÏ¢
+ //使用循环é¿å…代ç é‡å¤ï¼Œå¦‚æžœå¯è¡Œçš„è¯ï¼Œå¯ä»¥åœ¨æ¤å¾ªçޝä¸ç¡®å®šæ³•å‘é‡ç‰ä¿¡æ¯
for (int j = 0; j < 3; j++) {
vertexPosition[j] = mesh->mVertices[i][j];
_left_down_back[j] = _left_down_back[j] < vertexPosition[j] ? _left_down_back[j] : vertexPosition[j];
@@ -200,5 +206,26 @@ void Model::check_boundary() {
}
}
}
+}
-}
\ No newline at end of file
+Model* Model::copyToCurrentContext() const {
+ // Reload all textures
+ std::vector newTextures;
+ for (unsigned int i = 0; i < _texturesLoaded.size(); i++) {
+ // Load texture
+ Texture newTexture = Texture(_texturesLoaded[i].type(), _directory + '/' + _texturesLoaded[i].path());
+ newTextures.push_back(newTexture);
+ }
+
+ // Copy all meshes
+ std::vector newMeshes;
+ for (unsigned int i = 0; i < _meshes.size(); i++) {
+ // Copy mesh
+ Mesh newMesh = Mesh(_meshes[i].vertices(), _meshes[i].indices(), newTextures);
+ newMeshes.push_back(newMesh);
+ }
+
+ // Create new model
+ Model* newModel = new Model(std::move(newMeshes), std::move(newTextures), _directory);
+ return newModel;
+}
diff --git a/FinalProject/model.h b/FinalProject/model.h
index 7f3df3a..57bbbff 100644
--- a/FinalProject/model.h
+++ b/FinalProject/model.h
@@ -28,8 +28,16 @@ public:
Model(std::string path);
~Model();
+private:
+ Model(std::vector&& meshes, std::vector&& textures, std::string directory);
+
public:
inline MODELSTATUS status() const { return _status; }
+ inline int meshCount() const { return _meshes.size(); }
+ inline int textureCount() const { return _texturesLoaded.size(); }
+ inline std::string directory() const { return _directory; }
+
+ Model* copyToCurrentContext() const;
// maybe we can check if boundary has not been set yet
diff --git a/FinalProject/modelselectable.cpp b/FinalProject/modelselectable.cpp
index 6f70f09..811585f 100644
--- a/FinalProject/modelselectable.cpp
+++ b/FinalProject/modelselectable.cpp
@@ -1 +1,83 @@
-#pragma once
+#include
+
+#include "modelselectable.h"
+#include "sceneviewer.h"
+
+ModelSelectable::ModelSelectable(Model* model, QWidget* parent) :
+ QWidget(parent), _model(model)
+{
+ // Set size
+ setFixedHeight(150);
+ setSizeIncrement(QSizePolicy::Expanding, QSizePolicy::Fixed);
+
+ // Create stretch layout to hold the main button
+ _stretchLayout = new QHBoxLayout(this);
+ _stretchLayout->setContentsMargins(0, 0, 0, 0);
+ _stretchLayout->setSpacing(0);
+ setLayout(_stretchLayout);
+
+ // Create main button widget
+ _mainBtn = new PushButton(nullptr, this);
+ _mainBtn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _mainBtn->setMargin(0, 0, 0, 0);
+ _mainBtn->setRadius(10);
+ _mainBtn->setBackgroundColor(QColor(58, 143, 183, 15));
+ _stretchLayout->addWidget(_mainBtn);
+ _mainBtn->show();
+
+ // Create widget to hold the button content
+ _mainBtnChild = new QWidget(_mainBtn);
+ _mainBtnChild->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ //_mainBtnChild->setObjectName("mainBtnChild");
+ //_mainBtnChild->setStyleSheet("QWidget#mainBtnChild{border: 3px solid " + _borderColor.name(QColor::HexArgb) + ";"
+ // "border-radius:" + QString::number(_cornerRadius) + "px;}");
+ _mainBtn->setChildWidget(_mainBtnChild);
+ _mainBtnChild->show();
+
+ // Create layout to hold the button content
+ _mainLayout = new QHBoxLayout(_mainBtnChild);
+ _mainLayout->setContentsMargins(16, 12, 16, 12);
+ _mainLayout->setSpacing(12);
+ _mainBtnChild->setLayout(_mainLayout);
+
+ // Create model thumbnail widget
+ _thumbnailContainer = new RoundedCornerWidget(_mainBtnChild);
+ _thumbnailContainer->setCornerRadius(_cornerRadius);
+ _thumbnailContainer->setBorderColor(QColor(255, 255, 255, 0));
+ _thumbnailContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _mainLayout->addWidget(_thumbnailContainer);
+ _thumbnailContainer->show();
+
+ _thumbnailWidget = new ModelThumbnailWidget(_model, _mainBtnChild);
+ _thumbnailWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+ _thumbnailContainerLayout = new QHBoxLayout(_thumbnailContainer->mainWidget());
+ _thumbnailContainerLayout->setContentsMargins(0, 0, 0, 0);
+ _thumbnailContainerLayout->setSpacing(0);
+ _thumbnailContainer->mainWidget()->setLayout(_thumbnailContainerLayout);
+ _thumbnailContainerLayout->addWidget(_thumbnailWidget);
+ _thumbnailWidget->show();
+
+ // Create delete button
+ _deleteButton = new PushButton(nullptr, _mainBtnChild);
+ _deleteButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ _deleteIcon = new QLabel(_deleteButton);
+ _deleteIcon->setFont(QFont("Font Awesome 6 Free Regular", 12));
+ _deleteIcon->setText("\uf2ed");
+ _deleteIcon->setAlignment(Qt::AlignCenter);
+ _deleteButton->setChildWidget(_deleteIcon);
+ _deleteButton->setColorScheme(QColor(171, 59, 58));
+ _deleteButton->setIndicatorColor(QColor(171, 59, 58, 0));
+ _deleteIcon->show();
+ _mainLayout->addWidget(_deleteButton);
+ _deleteButton->show();
+
+ // Connect
+ connect(_mainBtn, &PushButton::onClick, this, &ModelSelectable::onSelected);
+ connect(_deleteButton, &PushButton::onClick, this, &ModelSelectable::onRemoved);
+}
+
+ModelSelectable::~ModelSelectable()
+{
+ delete _model;
+}
\ No newline at end of file
diff --git a/FinalProject/modelselectable.h b/FinalProject/modelselectable.h
index 7fed472..acb94cf 100644
--- a/FinalProject/modelselectable.h
+++ b/FinalProject/modelselectable.h
@@ -1,6 +1,46 @@
#pragma once
-#include "model.h"
-
#include
+#include
+#include
+#include "model.h"
+#include "pushbutton.h"
+#include "roundedcornerwidget.h"
+#include "modelthumbnailwidget.h"
+#include "lineeditwidget.h"
+
+class ModelSelectable : public QWidget {
+
+ Q_OBJECT
+
+public:
+ ModelSelectable(Model* model, QWidget* parent = 0);
+ ~ModelSelectable();
+
+private:
+ // Data
+ Model* _model;
+
+ // UI control variables
+ const QColor _borderColor = QColor(58, 143, 183);
+ const int _cornerRadius = 10;
+
+ // UI elements
+ QHBoxLayout* _stretchLayout = nullptr;
+ PushButton* _mainBtn = nullptr;
+
+ QWidget* _mainBtnChild = nullptr;
+ QHBoxLayout* _mainLayout = nullptr;
+
+ RoundedCornerWidget* _thumbnailContainer = nullptr;
+ QHBoxLayout* _thumbnailContainerLayout = nullptr;
+ ModelThumbnailWidget* _thumbnailWidget = nullptr;
+
+ PushButton* _deleteButton = nullptr;
+ QLabel* _deleteIcon = nullptr;
+
+signals:
+ void onSelected();
+ void onRemoved();
+};
diff --git a/FinalProject/modelselector.cpp b/FinalProject/modelselector.cpp
index 6f70f09..c7c37c6 100644
--- a/FinalProject/modelselector.cpp
+++ b/FinalProject/modelselector.cpp
@@ -1 +1,83 @@
-#pragma once
+#include
+
+#include "modelselector.h"
+
+ModelSelector::ModelSelector(QWidget* parent) :
+ QWidget(parent)
+{
+ // Set maximum width
+ setMaximumWidth(240);
+
+ // Set up the main layout
+ _stretchLayout = new QVBoxLayout(this);
+ _stretchLayout->setContentsMargins(0, 0, 0, 0);
+ _stretchLayout->setSpacing(8);
+ setLayout(_stretchLayout);
+
+ // Create and add the 'add new' button
+ _addNewObjectBtn = new PushButton(nullptr, this);
+ _addNewObjectBtn->setChildWidget(new QLabel("Add New", _addNewObjectBtn));
+ _addNewObjectBtn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ _addNewObjectBtn->setMargin(32, 24, 32, 24);
+ _addNewObjectBtn->setRadius(10);
+ _addNewObjectBtn->setBackgroundColor(QColor(58, 143, 183, 20));
+ _stretchLayout->addWidget(_addNewObjectBtn);
+ _addNewObjectBtn->show();
+
+ // Create the selection list widget
+ _objectList = new ScrollListWidget(this);
+ _stretchLayout->addWidget(_objectList);
+ _objectList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _objectList->show();
+
+ // Connect
+ connect(_addNewObjectBtn, &PushButton::onClick, this, &ModelSelector::addNewObject);
+}
+
+ModelSelector::~ModelSelector() {}
+
+void ModelSelector::addNewObject() {
+ QString path = getNewObjectPath();
+ if (path.isEmpty()) {
+ return;
+ }
+
+ Model* model = loadObject(path);
+ ModelSelectable* newSelectable = new ModelSelectable(model, this);
+
+ _objectSelectables.push_back(newSelectable);
+ _objectList->addWidget(newSelectable);
+
+ connect(newSelectable, &ModelSelectable::onSelected, this, [=]() {
+ emit onObjectSelected(model);
+ });
+ connect(newSelectable, &ModelSelectable::onRemoved, this, [=]() {
+ removeObject(newSelectable);
+ });
+}
+
+QString ModelSelector::getNewObjectPath() {
+ QString path = QFileDialog::getOpenFileName(this, "Select Model", "", "Model Files (*.obj)");
+ return path;
+}
+
+Model* ModelSelector::loadObject(const QString& path) {
+ Model* model = new Model(path.toStdString());
+ return model;
+}
+
+void ModelSelector::removeObject(ModelSelectable* selectable) {
+ // Check if selectable is still in list
+ if (!_objectSelectables.contains(selectable)) {
+ return;
+ }
+
+ // Remove the selectable from the list
+ _objectSelectables.removeAll(selectable);
+
+ // Remove the selectable from the list widget
+ _objectList->removeWidget(selectable);
+
+ // Delete the selectable
+ delete selectable;
+}
diff --git a/FinalProject/modelselector.h b/FinalProject/modelselector.h
index 6f70f09..0391f8e 100644
--- a/FinalProject/modelselector.h
+++ b/FinalProject/modelselector.h
@@ -1 +1,40 @@
#pragma once
+
+#include
+#include
+#include
+
+#include "scrolllistwidget.h"
+#include "pushbutton.h"
+#include "modelselectable.h"
+#include "model.h"
+
+class ModelSelector : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ModelSelector(QWidget* parent = 0);
+ ~ModelSelector();
+
+private:
+ // UI elements
+ QVBoxLayout* _stretchLayout = nullptr;
+ PushButton* _addNewObjectBtn = nullptr;
+ ScrollListWidget* _objectList = nullptr;
+
+ // Object selection storage
+ QList _objectSelectables;
+
+private:
+ // Slot functions
+ void addNewObject();
+
+ // Util functions
+ QString getNewObjectPath();
+ Model* loadObject(const QString& path);
+ void removeObject(ModelSelectable* selectable);
+
+signals:
+ void onObjectSelected(Model* selectedModel);
+};
diff --git a/FinalProject/modelsetter.h b/FinalProject/modelsetter.h
index 6f70f09..71fd808 100644
--- a/FinalProject/modelsetter.h
+++ b/FinalProject/modelsetter.h
@@ -1 +1,3 @@
#pragma once
+
+#include
diff --git a/FinalProject/modelthumbnailwidget.cpp b/FinalProject/modelthumbnailwidget.cpp
new file mode 100644
index 0000000..b6e1fcf
--- /dev/null
+++ b/FinalProject/modelthumbnailwidget.cpp
@@ -0,0 +1,75 @@
+#include
+#include
+
+#include "modelthumbnailwidget.h"
+
+ModelThumbnailWidget::ModelThumbnailWidget(Model* model, QWidget* parent) :
+ QOpenGLWidget(parent), _model(model)
+{
+ // OpenGL initialize
+ QSurfaceFormat format;
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setVersion(4, 3);
+ setFormat(format);
+
+ // Create a folder
+ QDir dir("./temp/shaders");
+ if (!dir.exists()) {
+ dir.mkpath(".");
+ }
+
+ // Copy the shaders to the folder
+ if (QFile::exists("./temp/shaders/vertexshader.vs")) {
+ QFile::remove("./temp/shaders/vertexshader.vs");
+ }
+ QFile::copy(":/shaders/vertexshader.vs", "./temp/shaders/vertexshader.vs");
+ QFile::setPermissions("./temp/shaders/vertexshader.vs", QFileDevice::ReadOwner | QFileDevice::WriteOwner);
+ if (QFile::exists("./temp/shaders/fragmentshader.fs")) {
+ QFile::remove("./temp/shaders/fragmentshader.fs");
+ }
+ QFile::copy(":/shaders/fragmentshader.fs", "./temp/shaders/fragmentshader.fs");
+ QFile::setPermissions("./temp/shaders/fragmentshader.fs", QFile::ReadOwner | QFile::WriteOwner);
+}
+
+ModelThumbnailWidget::~ModelThumbnailWidget() {
+}
+
+void ModelThumbnailWidget::initializeGL() {
+ initializeOpenGLFunctions();
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glEnable(GL_DEPTH_TEST);
+
+ _shaderProgram.ensureInitialized();
+
+ VertexShader vertexShader("./temp/shaders/vertexshader.vs");
+ FragmentShader fragmentShader("./temp/shaders/fragmentshader.fs");
+ _shaderProgram.attachShader(vertexShader);
+ _shaderProgram.attachShader(fragmentShader);
+ vertexShader.dispose();
+ fragmentShader.dispose();
+
+ _object.setModel(_model->copyToCurrentContext());
+ _camera.setPosition(glm::vec3(0.0f, 0.0f, 7.0f));
+}
+
+void ModelThumbnailWidget::paintGL() {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ _shaderProgram.bind();
+
+ glm::mat4 view = _camera.viewMatrix();
+ glm::mat4 projection = glm::perspective(glm::radians(_camera.zoomVal()), (float)width() / (float)height(), 0.1f, 100.0f);
+ _shaderProgram.setUniform("view", view);
+ _shaderProgram.setUniform("projection", projection);
+
+ _object.render(_shaderProgram);
+
+ _shaderProgram.unbind();
+}
+
+void ModelThumbnailWidget::resizeGL(int w, int h) {
+ glViewport(0, 0, w, h);
+}
\ No newline at end of file
diff --git a/FinalProject/modelthumbnailwidget.h b/FinalProject/modelthumbnailwidget.h
new file mode 100644
index 0000000..05377ed
--- /dev/null
+++ b/FinalProject/modelthumbnailwidget.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "model.h"
+#include "camera.h"
+#include "shader.h"
+#include "renderable.h"
+
+class ModelThumbnailWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+ Q_OBJECT
+
+public:
+ ModelThumbnailWidget(Model* model, QWidget* parent = 0);
+ ~ModelThumbnailWidget();
+
+private:
+ Model* _model = nullptr;
+ Renderable _object = Renderable::empty();
+ ShaderProgram _shaderProgram = ShaderProgram::empty();
+ Camera _camera;
+
+protected:
+ virtual void initializeGL() override;
+ virtual void paintGL() override;
+ virtual void resizeGL(int w, int h) override;
+};
diff --git a/FinalProject/pagewidget.cpp b/FinalProject/pagewidget.cpp
new file mode 100644
index 0000000..ec5d3a3
--- /dev/null
+++ b/FinalProject/pagewidget.cpp
@@ -0,0 +1,72 @@
+#include
+#include
+
+#include "pagewidget.h"
+
+PageWidget::PageWidget(QWidget* parent) : QWidget(parent) {
+ // Construct and set main layout
+ _stretchLayout = new QVBoxLayout(this);
+ _stretchLayout->setContentsMargins(0, 0, 0, 0);
+ _stretchLayout->setSpacing(0);
+ setLayout(_stretchLayout);
+
+ // Construct content widget
+ _contentWidget = new QWidget(this);
+ _stretchLayout->addWidget(_contentWidget);
+ _contentWidget->show();
+
+ // Add opacity effect to real content
+ _pageOpacityEffect = new QGraphicsOpacityEffect(_contentWidget);
+ _pageOpacityEffect->setOpacity(0);
+ _contentWidget->setGraphicsEffect(_pageOpacityEffect);
+
+ // Move offstage
+ move(_originPagePosition + QPoint(0, 150));
+ hide();
+}
+
+PageWidget::~PageWidget() {}
+
+void PageWidget::onStage() {
+ // Move up and fade in
+ QParallelAnimationGroup* onStageAnimation = new QParallelAnimationGroup(this);
+ QPropertyAnimation* moveAnimation = new QPropertyAnimation(this, "pos");
+ QPropertyAnimation* fadeInAnimation = new QPropertyAnimation(_pageOpacityEffect, "opacity");
+ moveAnimation->setDuration(600);
+ moveAnimation->setEasingCurve(QEasingCurve::OutExpo);
+ moveAnimation->setStartValue(pos());
+ moveAnimation->setEndValue(_originPagePosition);
+ fadeInAnimation->setDuration(500);
+ fadeInAnimation->setEasingCurve(QEasingCurve::InQuad);
+ fadeInAnimation->setStartValue(_pageOpacityEffect->opacity());
+ fadeInAnimation->setEndValue(0.999);
+ onStageAnimation->addAnimation(moveAnimation);
+ onStageAnimation->addAnimation(fadeInAnimation);
+ onStageAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ // Show page
+ show();
+}
+
+void PageWidget::offStage() {
+ // Move down and fade out
+ QParallelAnimationGroup* offStageAnimation = new QParallelAnimationGroup(this);
+ //QPropertyAnimation* moveAnimation = new QPropertyAnimation(_contentWidget, "pos");
+ QPropertyAnimation* fadeOutAnimation = new QPropertyAnimation(_pageOpacityEffect, "opacity");
+ //moveAnimation->setDuration(300);
+ //moveAnimation->setEasingCurve(QEasingCurve::OutCubic);
+ //moveAnimation->setStartValue(_contentWidget->pos());
+ //moveAnimation->setEndValue(_originPagePosition - QPoint(0, 100));
+ fadeOutAnimation->setDuration(200);
+ fadeOutAnimation->setStartValue(_pageOpacityEffect->opacity());
+ fadeOutAnimation->setEndValue(0);
+ //offStageAnimation->addAnimation(moveAnimation);
+ offStageAnimation->addAnimation(fadeOutAnimation);
+ offStageAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ // Connect animation finished signal to hide page
+ connect(offStageAnimation, &QParallelAnimationGroup::finished, [=]() {
+ move(_originPagePosition + QPoint(0, 150));
+ hide();
+ });
+}
\ No newline at end of file
diff --git a/FinalProject/pagewidget.h b/FinalProject/pagewidget.h
new file mode 100644
index 0000000..1de30fb
--- /dev/null
+++ b/FinalProject/pagewidget.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+#include
+
+#include "pushbutton.h"
+
+class PageWidget : public QWidget
+{
+
+ Q_OBJECT
+
+public:
+ PageWidget(QWidget* parent = 0);
+ ~PageWidget();
+
+protected:
+ // Page layout and content widget (which holds the actual page elements)
+ QVBoxLayout* _stretchLayout = nullptr;
+ QWidget* _contentWidget = nullptr;
+
+ // Button widgets for side bar
+ PushButton* _iconButton = nullptr;
+ PushButton* _textButton = nullptr;
+
+ // Opacity effects
+ QGraphicsOpacityEffect* _pageOpacityEffect = nullptr;
+
+ // Page position memory
+ QPoint _originPagePosition = QPoint(0, 0);
+
+public:
+ virtual PushButton* getPageIconButton(QWidget* context) = 0; // provide a push button with only an icon
+ virtual PushButton* getPageTextButton(QWidget* context) = 0; // provide a push button with an icon and a description text
+
+public:
+ void onStage(); // provide an on stage animation when the page is selected
+ void offStage(); // provide an off stage animation when the page is deselected
+};
\ No newline at end of file
diff --git a/FinalProject/pushbutton.cpp b/FinalProject/pushbutton.cpp
new file mode 100644
index 0000000..0470b86
--- /dev/null
+++ b/FinalProject/pushbutton.cpp
@@ -0,0 +1,811 @@
+#include
+#include
+#include
+#include
+
+#include "pushbutton.h"
+
+PushButton::PushButton(QWidget* child, QWidget* parent)
+ : QWidget(parent), _childWidget(child)
+{
+ initializeUI();
+}
+
+PushButton::~PushButton()
+{
+}
+
+void PushButton::initializeUI() {
+ generateColor(_defaultColorScheme);
+
+ // Add margin for the child widget
+ _stretchLayout = new QHBoxLayout(this);
+ _stretchLayout->setContentsMargins(_contentMargin);
+ _stretchLayout->setSpacing(0);
+ _stretchLayout->setAlignment(Qt::AlignCenter);
+ setLayout(_stretchLayout);
+ if (_childWidget) {
+ // prevent adding a null widget
+ _stretchLayout->addWidget(_childWidget);
+ _childWidget->show();
+ _childWidgetOriginalGeometry = _childWidget->geometry();
+ }
+
+ // Initialize background widget
+ _backgroundWidget = new QWidget(this);
+ _backgroundWidget->resize(size());
+ _backgroundWidget->setObjectName("backgroundWidget");
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ _backgroundWidget->lower();
+ _backgroundWidget->show();
+
+ // Initialize indicator
+ _indicator = new QWidget(this);
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ _indicator->resize(_indicatorWidth, _activatedLengthRatio * height());
+ _indicator->move(_indicatorSpacing, (0.5 - _activatedLengthRatio / 2) * height());
+ break;
+ case LUI_BTN_POS_RIGHT:
+ _indicator->resize(_indicatorWidth, _activatedLengthRatio * height());
+ _indicator->move(width() - _indicatorWidth - _indicatorSpacing, (0.5 - _activatedLengthRatio / 2) * height());
+ break;
+ case LUI_BTN_POS_TOP:
+ _indicator->resize(_activatedLengthRatio * width(), _indicatorWidth);
+ _indicator->move((0.5 - _activatedLengthRatio / 2) * width(), _indicatorSpacing);
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ _indicator->resize(_activatedLengthRatio * width(), _indicatorWidth);
+ _indicator->move((0.5 - _activatedLengthRatio / 2) * width(), height() - _indicatorWidth - _indicatorSpacing);
+ break;
+ }
+ _indicator->setObjectName("indicator");
+ _indicator->setStyleSheet("QWidget#indicator{background-color:" + _indicatorColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number((float)_indicatorWidth / 2) + "px;}");
+ _indicatorEffect = new QGraphicsOpacityEffect(_indicator);
+ _indicatorEffect->setOpacity(0);
+ _indicator->setGraphicsEffect(_indicatorEffect);
+ _indicator->show();
+
+ // Enable mouse tracking
+ setMouseTracking(true);
+}
+
+void PushButton::generateColor(QColor colorScheme) {
+ _backgroundColor = colorScheme.lighter(120);
+ _backgroundColor.setAlpha(0);
+ _hoverColor = colorScheme.lighter(120);
+ _hoverColor.setAlpha(40);
+ _pressedColor = colorScheme.lighter(120);
+ _pressedColor.setAlpha(50);
+ _selectedColor = colorScheme.lighter(120);
+ _selectedColor.setAlpha(35);
+ _indicatorColor = colorScheme;
+}
+
+void PushButton::enterEvent(QEnterEvent* event) {
+ setCursor(Qt::PointingHandCursor);
+
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+
+ QParallelAnimationGroup* indicatorEnterAnimation = new QParallelAnimationGroup(this);
+ QPropertyAnimation* indicatorGrowLength = new QPropertyAnimation(_indicator, "geometry", this);
+ QPropertyAnimation* indicatorFadeIn = new QPropertyAnimation(_indicatorEffect, "opacity", this);
+ indicatorGrowLength->setDuration(150);
+ indicatorGrowLength->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorFadeIn->setDuration(100);
+ indicatorFadeIn->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorGrowLength->setStartValue(_indicator->geometry());
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ indicatorGrowLength->setEndValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_RIGHT:
+ indicatorGrowLength->setEndValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_TOP:
+ indicatorGrowLength->setEndValue(QRect(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ indicatorGrowLength->setEndValue(QRect(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ }
+ indicatorFadeIn->setStartValue(_indicatorEffect->opacity());
+ indicatorFadeIn->setEndValue(0.999);
+ indicatorEnterAnimation->addAnimation(indicatorGrowLength);
+ indicatorEnterAnimation->addAnimation(indicatorFadeIn);
+ indicatorEnterAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ _hovered = true;
+ emit onHover();
+}
+
+void PushButton::leaveEvent(QEvent* event) {
+ setCursor(Qt::ArrowCursor);
+
+ 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;}");
+ }
+
+ QParallelAnimationGroup* indicatorLeaveAnimation = new QParallelAnimationGroup(this);
+ QPropertyAnimation* indicatorShrinkLength = new QPropertyAnimation(_indicator, "geometry", this);
+ QPropertyAnimation* indicatorFadeOut = new QPropertyAnimation(_indicatorEffect, "opacity", this);
+ indicatorShrinkLength->setDuration(150);
+ indicatorShrinkLength->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorFadeOut->setDuration(100);
+ indicatorFadeOut->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorShrinkLength->setStartValue(_indicator->geometry());
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ indicatorShrinkLength->setEndValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_RIGHT:
+ indicatorShrinkLength->setEndValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_TOP:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ }
+ indicatorFadeOut->setStartValue(_indicatorEffect->opacity());
+ indicatorFadeOut->setEndValue(_selected ? 0.999 : 0);
+ indicatorLeaveAnimation->addAnimation(indicatorShrinkLength);
+ indicatorLeaveAnimation->addAnimation(indicatorFadeOut);
+ indicatorLeaveAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ _pressed = false;
+ _hovered = false;
+}
+
+void PushButton::mousePressEvent(QMouseEvent* event) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _pressedColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+
+ QPropertyAnimation* indicatorShrinkLength = new QPropertyAnimation(_indicator, "geometry", this);
+ indicatorShrinkLength->setDuration(100);
+ indicatorShrinkLength->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorShrinkLength->setStartValue(_indicator->geometry());
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ indicatorShrinkLength->setEndValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_RIGHT:
+ indicatorShrinkLength->setEndValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_TOP:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ }
+ indicatorShrinkLength->start(QAbstractAnimation::DeleteWhenStopped);
+
+ _pressed = true;
+
+ emit onPressed();
+}
+
+void PushButton::mouseReleaseEvent(QMouseEvent* event) {
+ if (!_pressed) {
+ return;
+ }
+
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+
+ QPropertyAnimation* indicatorGrowLength = new QPropertyAnimation(_indicator, "geometry", this);
+ indicatorGrowLength->setDuration(100);
+ indicatorGrowLength->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorGrowLength->setStartValue(_indicator->geometry());
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ indicatorGrowLength->setEndValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_RIGHT:
+ indicatorGrowLength->setEndValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_TOP:
+ indicatorGrowLength->setEndValue(QRect(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ indicatorGrowLength->setEndValue(QRect(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ }
+ indicatorGrowLength->start(QAbstractAnimation::DeleteWhenStopped);
+
+ if (_pressed) {
+ // prevent double trigger if mouse is first left and then released
+ emit onClick();
+ }
+
+ _pressed = false;
+}
+
+void PushButton::resizeEvent(QResizeEvent* event) {
+ _backgroundWidget->setGeometry(0, 0, width(), height());
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ if (_pressed) {
+ _indicator->setGeometry(QRect(
+ _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(QRect(
+ _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ ));
+ }
+ else {
+ _indicator->setGeometry(QRect(
+ _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ ));
+ }
+ break;
+ case LUI_BTN_POS_RIGHT:
+ if (_pressed) {
+ _indicator->setGeometry(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ ));
+ }
+ else {
+ _indicator->setGeometry(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ ));
+ }
+ break;
+ case LUI_BTN_POS_TOP:
+ if (_pressed) {
+ _indicator->setGeometry(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(QRect(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ ));
+ }
+ else {
+ _indicator->setGeometry(QRect(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ }
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ if (_pressed) {
+ _indicator->setGeometry(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(QRect(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ ));
+ }
+ else {
+ _indicator->setGeometry(QRect(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ }
+ break;
+ }
+}
+
+void PushButton::select() {
+ 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 {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _selectedColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+
+ // First shrink then length the indicator, also fade in
+ QSequentialAnimationGroup* indicatorSelectAnimation = new QSequentialAnimationGroup(this);
+ QParallelAnimationGroup* indicatorShrinkAnimation = new QParallelAnimationGroup(this);
+ QPropertyAnimation* indicatorFadeIn = new QPropertyAnimation(_indicatorEffect, "opacity", this);
+ QPropertyAnimation* indicatorShrinkLength = new QPropertyAnimation(_indicator, "geometry", this);
+ QPropertyAnimation* indicatorGrowLength = new QPropertyAnimation(_indicator, "geometry", this);
+ indicatorFadeIn->setDuration(100);
+ indicatorFadeIn->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorShrinkLength->setDuration(100);
+ indicatorShrinkLength->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorGrowLength->setDuration(100);
+ indicatorGrowLength->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorFadeIn->setStartValue(_indicatorEffect->opacity());
+ indicatorFadeIn->setEndValue(0.999);
+ indicatorShrinkLength->setStartValue(_indicator->geometry());
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ indicatorShrinkLength->setEndValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ indicatorGrowLength->setStartValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ indicatorGrowLength->setEndValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_RIGHT:
+ indicatorShrinkLength->setEndValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ indicatorGrowLength->setStartValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ indicatorGrowLength->setEndValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_TOP:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ indicatorGrowLength->setStartValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ indicatorGrowLength->setEndValue(QRect(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ indicatorGrowLength->setStartValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ indicatorGrowLength->setEndValue(QRect(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ }
+ indicatorShrinkAnimation->addAnimation(indicatorShrinkLength);
+ indicatorShrinkAnimation->addAnimation(indicatorFadeIn);
+ indicatorSelectAnimation->addAnimation(indicatorShrinkAnimation);
+ indicatorSelectAnimation->addAnimation(indicatorGrowLength);
+ indicatorSelectAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ // Set selected state
+ _selected = true;
+}
+
+void PushButton::deselect() {
+ if (!_selected) {
+ return;
+ }
+
+ 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 {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+
+ // Cursor is currently not in the button, shrink and fade out the indicator
+ QParallelAnimationGroup* indicatorDeselectAnimation = new QParallelAnimationGroup(this);
+ QPropertyAnimation* indicatorFadeOut = new QPropertyAnimation(_indicatorEffect, "opacity", this);
+ QPropertyAnimation* indicatorShrinkLength = new QPropertyAnimation(_indicator, "geometry", this);
+ indicatorFadeOut->setDuration(100);
+ indicatorFadeOut->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorShrinkLength->setDuration(100);
+ indicatorShrinkLength->setEasingCurve(QEasingCurve::OutQuad);
+ indicatorFadeOut->setStartValue(_indicatorEffect->opacity());
+ indicatorFadeOut->setEndValue(0);
+ indicatorShrinkLength->setStartValue(_indicator->geometry());
+ switch (_indicatorPosition) {
+ case LUI_BTN_POS_LEFT:
+ indicatorShrinkLength->setEndValue(QRect(
+ _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_RIGHT:
+ indicatorShrinkLength->setEndValue(QRect(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ ));
+ break;
+ case LUI_BTN_POS_TOP:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ indicatorShrinkLength->setEndValue(QRect(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ ));
+ break;
+ }
+ indicatorDeselectAnimation->addAnimation(indicatorFadeOut);
+ indicatorDeselectAnimation->addAnimation(indicatorShrinkLength);
+ indicatorDeselectAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ }
+
+ // Set selected state
+ _selected = false;
+}
+
+void PushButton::setRadius(int radius) {
+ // get current style sheet
+ QString styleSheet = _backgroundWidget->styleSheet();
+ // remove old border radius
+ styleSheet.remove(QRegularExpression("border-radius:\\d+px;"));
+ // add new border radius
+ styleSheet.append("border-radius: " + QString::number(radius) + "px;");
+ // set new style sheet
+ _backgroundWidget->setStyleSheet(styleSheet);
+}
+
+void PushButton::setBackgroundColor(QColor color) {
+ _backgroundColor = color;
+ if (!_selected && !_hovered && !_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _backgroundColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+}
+
+void PushButton::setHoverColor(QColor color) {
+ _hoverColor = color;
+ if (_hovered && !_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _hoverColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+}
+
+void PushButton::setPressedColor(QColor color) {
+ _pressedColor = color;
+ if (_pressed) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _pressedColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+}
+
+void PushButton::setSelectedColor(QColor color) {
+ _selectedColor = color;
+ if (_selected && !_pressed && !_hovered) {
+ _backgroundWidget->setStyleSheet("QWidget#backgroundWidget{background-color:" + _selectedColor.name(QColor::HexArgb) + ";border-radius:" + QString::number(_radius) + "px;}");
+ }
+}
+
+void PushButton::setIndicatorColor(QColor color) {
+ _indicatorColor = color;
+ _indicator->setStyleSheet("QWidget#indicator{background-color:" + _indicatorColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number((float)_indicatorWidth / 2) + "px;}");
+}
+
+void PushButton::setColorScheme(QColor color) {
+ generateColor(color);
+ 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;}");
+}
+
+QHBoxLayout* PushButton::mainLayout() const {
+ return _stretchLayout;
+}
+
+void PushButton::setAlignment(Qt::Alignment alignment) {
+ _stretchLayout->setAlignment(alignment);
+}
+
+void PushButton::setMargin(QMargins margin) {
+ _stretchLayout->setContentsMargins(margin);
+}
+
+void PushButton::setMargin(int left, int top, int right, int bottom) {
+ _stretchLayout->setContentsMargins(left, top, right, bottom);
+}
+
+void PushButton::setIndicatorPosition(LUI_BTN_INDICATOR_POS pos) {
+ _indicatorPosition = pos;
+ switch (pos) {
+ case LUI_BTN_POS_LEFT:
+ if (_pressed) {
+ _indicator->setGeometry(
+ _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ );
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(
+ _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ );
+ }
+ else {
+ _indicator->setGeometry(
+ _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ );
+ }
+ break;
+ case LUI_BTN_POS_RIGHT:
+ if (_pressed) {
+ _indicator->setGeometry(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _pressedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _pressedLengthRatio * height()
+ );
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _hoveredLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _hoveredLengthRatio * height()
+ );
+ }
+ else {
+ _indicator->setGeometry(
+ width() - _indicatorWidth - _indicatorSpacing,
+ (0.5 - _activatedLengthRatio / 2) * height(),
+ _indicatorWidth,
+ _activatedLengthRatio * height()
+ );
+ }
+ break;
+ case LUI_BTN_POS_TOP:
+ if (_pressed) {
+ _indicator->setGeometry(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ );
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ );
+ }
+ else {
+ _indicator->setGeometry(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ );
+ }
+ break;
+ case LUI_BTN_POS_BOTTOM:
+ if (_pressed) {
+ _indicator->setGeometry(
+ (0.5 - _pressedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _pressedLengthRatio * width(),
+ _indicatorWidth
+ );
+ }
+ else if (_hovered) {
+ _indicator->setGeometry(
+ (0.5 - _hoveredLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _hoveredLengthRatio * width(),
+ _indicatorWidth
+ );
+ }
+ else {
+ _indicator->setGeometry(
+ (0.5 - _activatedLengthRatio / 2) * width(),
+ height() - _indicatorWidth - _indicatorSpacing,
+ _activatedLengthRatio * width(),
+ _indicatorWidth
+ );
+ }
+ break;
+ }
+}
+
+QWidget* PushButton::childWidget() {
+ return _childWidget;
+}
+
+void PushButton::setChildWidget(QWidget* widget) {
+ _childWidget = widget;
+ for (int i = 0; i < _stretchLayout->count(); i++) {
+ _stretchLayout->removeItem(_stretchLayout->itemAt(i));
+ }
+ if (_childWidget != nullptr) {
+ _stretchLayout->addWidget(_childWidget);
+ _childWidget->show();
+ _childWidgetOriginalGeometry = _childWidget->geometry();
+ }
+}
+
+bool PushButton::isSelected() const {
+ return _selected;
+}
diff --git a/FinalProject/pushbutton.h b/FinalProject/pushbutton.h
new file mode 100644
index 0000000..8ac2f6d
--- /dev/null
+++ b/FinalProject/pushbutton.h
@@ -0,0 +1,102 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+class PushButton : public QWidget {
+
+ Q_OBJECT
+
+public:
+ enum LUI_BTN_INDICATOR_POS {
+ LUI_BTN_POS_LEFT,
+ LUI_BTN_POS_RIGHT,
+ LUI_BTN_POS_TOP,
+ LUI_BTN_POS_BOTTOM
+ };
+
+public:
+ PushButton(QWidget* child, QWidget* parent);
+ ~PushButton();
+
+private:
+ // Button Layout
+ QWidget* _childWidget;
+ QRect _childWidgetOriginalGeometry;
+ const float _childWidgetShrinkRatio = 0.9;
+ QHBoxLayout* _stretchLayout;
+ QMargins _contentMargin = QMargins(12, 12, 12, 12);
+
+ // Button ui
+ int _radius = 8;
+
+ QWidget* _backgroundWidget;
+ const QColor _defaultColorScheme = QColor(58, 143, 183);
+ QColor _backgroundColor;
+ QColor _hoverColor;
+ QColor _pressedColor;
+ QColor _selectedColor;
+
+ QWidget* _indicator;
+ LUI_BTN_INDICATOR_POS _indicatorPosition = LUI_BTN_POS_LEFT;
+ const int _indicatorWidth = 6;
+ const int _indicatorSpacing = 4;
+ const float _activatedLengthRatio = 0.4;
+ const float _hoveredLengthRatio = 0.5;
+ const float _pressedLengthRatio = 0.2;
+ QColor _indicatorColor;
+ QGraphicsOpacityEffect* _indicatorEffect;
+
+ // Button state
+ bool _hovered = false;
+ bool _pressed = false;
+ bool _selected = false;
+
+private:
+ // UI util functions
+ void initializeUI();
+ void generateColor(QColor colorScheme);
+
+private:
+ // Interactions
+ virtual void enterEvent(QEnterEvent* event) override;
+ virtual void leaveEvent(QEvent* event) override;
+ virtual void mousePressEvent(QMouseEvent* event) override;
+ virtual void mouseReleaseEvent(QMouseEvent* event) override;
+ virtual void resizeEvent(QResizeEvent* event) override;
+
+public:
+ // Operation APIs
+ void select();
+ void deselect();
+
+ // Attribute setter APIs
+ void setRadius(int radius);
+
+ void setBackgroundColor(QColor color);
+ void setHoverColor(QColor color);
+ void setPressedColor(QColor color);
+ void setSelectedColor(QColor color);
+ void setIndicatorColor(QColor color);
+ void setColorScheme(QColor primaryColor);
+
+ QHBoxLayout* mainLayout() const;
+ void setAlignment(Qt::Alignment alignment);
+ void setMargin(QMargins margin);
+ void setMargin(int left, int top, int right, int bottom);
+
+ void setIndicatorPosition(LUI_BTN_INDICATOR_POS position);
+
+ QWidget* childWidget();
+ void setChildWidget(QWidget* widget);
+
+ bool isSelected() const;
+
+signals:
+ void onClick();
+ void onHover();
+ void onPressed();
+};
diff --git a/FinalProject/renderable.cpp b/FinalProject/renderable.cpp
index 16eb8a2..8254edf 100644
--- a/FinalProject/renderable.cpp
+++ b/FinalProject/renderable.cpp
@@ -8,6 +8,10 @@ Renderable::Renderable(Model* model) : _model(model) {}
Renderable::Renderable(Model* model, glm::vec3 position) : _model(model), _position(position) {}
+void Renderable::setModel(Model* model) {
+ _model = model;
+}
+
void Renderable::move(glm::vec3 deltaVec) {
_position += deltaVec;
}
@@ -33,6 +37,11 @@ void Renderable::setScale(float scale) {
}
void Renderable::render(ShaderProgram shader) {
+ // Check if initialized
+ if (_model == nullptr) {
+ Logger::error("Tries to render an uninitialized renderable object");
+ return;
+ }
// Set model matrix
shader.setUniform("model", modelMatrix());
// Render
diff --git a/FinalProject/renderable.h b/FinalProject/renderable.h
index 1ae9fe3..6409f1f 100644
--- a/FinalProject/renderable.h
+++ b/FinalProject/renderable.h
@@ -7,8 +7,16 @@
#include "shader.h"
class Renderable {
+public:
+ static Renderable empty() {
+ return Renderable();
+ }
+
private:
- Model* _model;
+ Renderable() {}
+
+private:
+ Model* _model = nullptr;
glm::vec3 _position = glm::vec3(0.0f);
glm::mat4 _rotation = glm::mat4(1.0f);
glm::vec3 _scale = glm::vec3(1.0f);
@@ -21,6 +29,7 @@ public:
Renderable(Model* model, glm::vec3 position);
public:
+ void setModel(Model* model);
void move(glm::vec3 deltaVec);
void setPosition(glm::vec3 position);
void rotate(glm::vec3 axis, float deltaAngle);
diff --git a/FinalProject/roundedcornerwidget.cpp b/FinalProject/roundedcornerwidget.cpp
new file mode 100644
index 0000000..2f076fa
--- /dev/null
+++ b/FinalProject/roundedcornerwidget.cpp
@@ -0,0 +1,106 @@
+#include
+#include
+
+#include "roundedcornerwidget.h"
+
+RoundedCornerWidget::RoundedCornerWidget(QWidget* parent) : QWidget(parent) {
+ // Create main layout and main widget
+ _mainLayout = new QVBoxLayout(this);
+ _mainLayout->setContentsMargins(0, 1, 1, 1);
+ _mainLayout->setSpacing(0);
+ _mainWidget = new QWidget(this);
+ _mainWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ _mainLayout->addWidget(_mainWidget);
+ _mainWidget->show();
+ setLayout(_mainLayout);
+}
+
+RoundedCornerWidget::~RoundedCornerWidget() {
+}
+
+void RoundedCornerWidget::showEvent(QShowEvent* event) {
+ // Call parent show event
+ QWidget::showEvent(event);
+
+ if (_initialized) {
+ return;
+ }
+
+ // Initialize widget UI
+ initializeWidgetUI();
+
+ // Set initialized state
+ _initialized = true;
+}
+
+void RoundedCornerWidget::initializeWidgetUI() {
+ // Set up border widget
+ _borderWidget = new QWidget(this);
+ _borderWidget->setObjectName("borderWidget");
+ QString borderWidgetStyleSheet =
+ "QWidget#borderWidget{background-color:#00FFFFFF;border:2px solid " + _borderColor.name(QColor::HexArgb) + ";"
+ "border-radius:" + QString::number(_cornerRadius) + "px;}";
+ _borderWidget->setStyleSheet(borderWidgetStyleSheet);
+ _borderWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
+ _borderWidget->move(0, 0);
+ _borderWidget->resize(width(), height());
+ _borderWidget->show();
+
+ // Create a round cornered mask
+ QPainterPath path;
+ path.addRoundedRect(_mainWidget->rect(), _cornerRadius, _cornerRadius);
+ QRegion region(path.toFillPolygon().toPolygon());
+ _mainWidget->setMask(region);
+}
+
+void RoundedCornerWidget::resizeEvent(QResizeEvent* event) {
+ // Resize widget border
+ if (_borderWidget != nullptr) {
+ _borderWidget->move(0, 0);
+ _borderWidget->resize(width(), height());
+ }
+
+ // Resize window mask
+ if (_mainWidget != nullptr) {
+ QPainterPath path;
+ path.addRoundedRect(_mainWidget->rect(), _cornerRadius, _cornerRadius);
+ QRegion region(path.toFillPolygon().toPolygon());
+ _mainWidget->setMask(region);
+ }
+}
+
+void RoundedCornerWidget::setCornerRadius(int radius) {
+ // Set corner radius
+ _cornerRadius = radius;
+
+ // Modify the corner radius in the current style sheet of the border widget
+ if (_borderWidget != nullptr) {
+ QString styleSheet = _borderWidget->styleSheet();
+ styleSheet.replace(QRegularExpression("border-radius:\\d+px;"),
+ QString("border-radius:%1px;").arg(_cornerRadius));
+ _borderWidget->setStyleSheet(styleSheet);
+ }
+
+ // Reset the mask of the main widget
+ QPainterPath path;
+ path.addRoundedRect(_mainWidget->rect(), _cornerRadius, _cornerRadius);
+ QRegion region(path.toFillPolygon().toPolygon());
+ _mainWidget->setMask(region);
+}
+
+void RoundedCornerWidget::setBorderColor(QColor color) {
+ // Set color
+ _borderColor = color;
+
+ // Modify the border color in the current style sheet of the border widget
+ if (_borderWidget != nullptr) {
+ QString styleSheet = _borderWidget->styleSheet();
+ styleSheet.replace(QRegularExpression("border:2px solid #\\w+;"),
+ QString("border:2px solid %1;").arg(_borderColor.name(QColor::HexArgb)));
+ _borderWidget->setStyleSheet(styleSheet);
+ }
+}
+
+QWidget* RoundedCornerWidget::mainWidget() const {
+ return _mainWidget;
+}
diff --git a/FinalProject/roundedcornerwidget.h b/FinalProject/roundedcornerwidget.h
new file mode 100644
index 0000000..6c889d1
--- /dev/null
+++ b/FinalProject/roundedcornerwidget.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include
+#include
+#include
+
+class RoundedCornerWidget : public QWidget {
+
+ Q_OBJECT
+
+public:
+ RoundedCornerWidget(QWidget* parent = 0);
+ ~RoundedCornerWidget();
+
+protected:
+ // UI settings
+ int _cornerRadius = 15;
+ QColor _borderColor = QColor(255, 255, 255);
+
+ // UI elements
+ QWidget* _mainWidget = nullptr;
+ QVBoxLayout* _mainLayout = nullptr;
+ QWidget* _borderWidget = nullptr;
+
+ // Initialization mark
+ bool _initialized = false;
+ void initializeWidgetUI();
+
+ // UI events
+ virtual void showEvent(QShowEvent* event) override;
+ virtual void resizeEvent(QResizeEvent* event) override;
+
+public:
+ // Public APIs
+ void setCornerRadius(int radius);
+ void setBorderColor(QColor color);
+ QWidget* mainWidget() const;
+};
diff --git a/FinalProject/sceneviewer.cpp b/FinalProject/sceneviewer.cpp
index 6f60c35..ede88ab 100644
--- a/FinalProject/sceneviewer.cpp
+++ b/FinalProject/sceneviewer.cpp
@@ -17,6 +17,7 @@ using std::vector;
SceneViewer::SceneViewer(QWidget* parent)
: QOpenGLWidget(parent)
{
+ // OpenGL initialize
QSurfaceFormat format;
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4, 3);
@@ -64,15 +65,8 @@ void SceneViewer::initializeGL() {
_shaderProgram.attachShader(fragmentShader);
vertexShader.dispose();
fragmentShader.dispose();
-
- Model* backpackModel = new Model("D:/code/ComputerGraphic/SceneEditor/obj/nanosuit/nanosuit.obj");
- Logger::info("Model loaded");
- Renderable backpack(backpackModel);
- _objects.push_back(backpack);
-
- _camera.setPosition(glm::vec3(0.0f, 0.0f, 5.0f));
-
+ _camera.setPosition(glm::vec3(0.0f, 0.0f, 10.0f));
}
void SceneViewer::resizeGL(int w, int h) {
@@ -80,6 +74,8 @@ void SceneViewer::resizeGL(int w, int h) {
}
void SceneViewer::paintGL() {
+ Logger::debug("Repainting");
+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_shaderProgram.bind();
@@ -108,7 +104,9 @@ void SceneViewer::mousePressEvent(QMouseEvent* event) {
}
void SceneViewer::mouseMoveEvent(QMouseEvent* event) {
- Logger::debug("Mouse moved with offset: " + std::to_string(event->x() - _lastMousePosition.x()) + ", " + std::to_string(event->y() - _lastMousePosition.y()));
+ if (event->buttons() != Qt::NoButton) {
+ Logger::debug("Mouse moved with offset: " + std::to_string(event->x() - _lastMousePosition.x()) + ", " + std::to_string(event->y() - _lastMousePosition.y()));
+ }
// Check the type of button pressed
switch (event->buttons()) {
case Qt::LeftButton: {
@@ -154,7 +152,7 @@ void SceneViewer::mouseMoveEvent(QMouseEvent* event) {
// Update the last mouse position
_lastMousePosition = event->pos();
// Update the view
- update();
+ parentWidget()->update();
}
void SceneViewer::wheelEvent(QWheelEvent* event) {
@@ -167,5 +165,31 @@ void SceneViewer::wheelEvent(QWheelEvent* event) {
Logger::debug("New camera position: " + std::to_string(_camera.position().x) + ", " + std::to_string(_camera.position().y) + ", " + std::to_string(_camera.position().z));
Logger::debug("New center position: " + std::to_string(_rotateCenter.x) + ", " + std::to_string(_rotateCenter.y) + ", " + std::to_string(_rotateCenter.z));
// Update the view
- update();
+ parentWidget()->update();
+}
+
+void SceneViewer::showEvent(QShowEvent* event) {
+ // Call show event of super class
+ QOpenGLWidget::showEvent(event);
+
+ if (_initialized) {
+ return;
+ }
+
+ //// Create mask for rounded corner
+ //QPainterPath mask;
+ //mask.addRoundedRect(rect(), _cornerRadius, _cornerRadius);
+ //setMask(mask.toFillPolygon().toPolygon());
+
+ _initialized = true;
+}
+
+void SceneViewer::resizeEvent(QResizeEvent* event) {
+ // Call resize event of super class
+ QOpenGLWidget::resizeEvent(event);
+
+ //// Create mask for rounded corner
+ //QPainterPath mask;
+ //mask.addRoundedRect(rect(), _cornerRadius, _cornerRadius);
+ //setMask(mask.toFillPolygon().toPolygon());
}
diff --git a/FinalProject/sceneviewer.h b/FinalProject/sceneviewer.h
index a854b32..5d2460e 100644
--- a/FinalProject/sceneviewer.h
+++ b/FinalProject/sceneviewer.h
@@ -34,6 +34,10 @@ private:
// User Interaction flags section---------------------
QPoint _lastMousePosition;
Renderable* _selectedObject = nullptr;
+
+ // UI interface control
+ const int _cornerRadius = 10;
+ bool _initialized = false;
public:
SceneViewer(QWidget* parent = 0);
@@ -50,4 +54,7 @@ protected:
virtual void mouseMoveEvent(QMouseEvent* event) override;
virtual void wheelEvent(QWheelEvent* event) override;
+ // UI update events
+ virtual void showEvent(QShowEvent* event) override;
+ virtual void resizeEvent(QResizeEvent* event) override;
};
\ No newline at end of file
diff --git a/FinalProject/scrolllistwidget.cpp b/FinalProject/scrolllistwidget.cpp
new file mode 100644
index 0000000..6d12181
--- /dev/null
+++ b/FinalProject/scrolllistwidget.cpp
@@ -0,0 +1,422 @@
+#include "scrolllistwidget.h"
+
+ScrollListWidget::ScrollListWidget(QWidget* parent) : QWidget(parent)
+{
+ //initialize list container and timer
+ container = new ScrollListContainer(this);
+ container->move(0, 0);
+ container->resize(this->width(), 3);
+ getCord = new QTimer;
+ getCord->setSingleShot(true);
+ rfrshView = new QTimer;
+ getCord->setSingleShot(true);
+
+ indicator = new ScrollListIndicator(this);
+ indicator->resize(indicator->width(), (int)((double)this->height() * this->height() / (double)container->height()));
+ indicator->move(this->width() - indicator->width() - 3, 0);
+
+ this->setMouseTracking(true);
+ container->setMouseTracking(true);
+ indicator->setMouseTracking(true);
+
+ bounce = new QPropertyAnimation(container, "pos");
+
+ QObject::connect(getCord, SIGNAL(timeout()), this, SLOT(updateSpd()));
+ QObject::connect(rfrshView, SIGNAL(timeout()), this, SLOT(scrollContainer()));
+ QObject::connect(indicator, SIGNAL(scrollPage(int)), this, SLOT(scrollIndicator(int)));
+}
+
+void ScrollListWidget::paintEvent(QPaintEvent* event) {
+ container->resize(this->width(), container->height());
+ if (container->height() > this->height() && container->y() < this->height() - container->height() && curSpd == 0 && bounce->state() == QAbstractAnimation::Stopped)
+ container->move(container->x(), this->height() - container->height());
+ if (container->height() <= this->height()) {
+ container->move(container->x(), 0);
+ indicator->hide();
+ }
+ else {
+ indicator->show();
+ }
+ indicator->resize(indicator->width(), (int)((double)this->height() * this->height() / (double)container->height()));
+ indicator->move(this->width() - indicator->width() - 3, -container->y() * this->height() / container->height());
+}
+
+void ScrollListWidget::mousePressEvent(QMouseEvent* event) {
+ if (container->height() > this->height()) {
+ if (container->y() <= 0 && container->y() + container->height() >= this->height())
+ pressed = true;
+ lastY = event->pos().y();
+ }
+ getCord->stop();
+ rfrshView->stop();
+ curSpd = 0;
+ outOfEdge = false;
+ moveStored = 0;
+ nextMove = 1;
+}
+
+void ScrollListWidget::mouseMoveEvent(QMouseEvent* event) {
+ setCursor(Qt::ArrowCursor);
+ if (pressed) {
+ //start scroll
+ if (!getCord->isActive() && event->pos().y() - lastY != 0) {
+ //start 30ms timer
+ getCord->start(30);
+ strtY = event->pos().y();
+ }
+ if (container->y() <= 0 && container->y() + container->height() >= this->height())
+ container->move(container->x(), container->y() + event->pos().y() - lastY);
+ else {
+ if (!outOfEdge) {
+ bfEdgeY = event->pos().y();
+ container->move(container->x(), container->y() + event->pos().y() - lastY);
+ outOfEdge = true;
+ }
+ else {
+ int pos = container->y() >= 0 ? 1 : -1;
+ int dp = event->pos().y() - bfEdgeY;
+ if (dp == 0) {
+ outOfEdge = false;
+ nextMove = 1;
+ moveStored = 0;
+ if (container->y() >= 0)
+ container->move(container->x(), 0);
+ else
+ container->move(container->x(), this->height() - container->height());
+ }
+ else if (dp / abs(dp) != pos) {
+ outOfEdge = false;
+ container->move(container->x(), this->y() + event->pos().y() - bfEdgeY);
+ nextMove = 1;
+ moveStored = 0;
+ }
+ else {
+ while (abs(moveStored) + nextMove <= abs(event->pos().y() - bfEdgeY)) {
+ moveStored += nextMove * pos;
+ container->move(container->x(), container->y() + pos);
+ nextMove++;
+ }
+ while (nextMove > 1 && abs(moveStored) > abs(event->pos().y() - bfEdgeY)) {
+ nextMove--;
+ moveStored -= nextMove * pos;
+ container->move(container->x(), container->y() - pos);
+ }
+ if (moveStored == 0) {
+ outOfEdge = false;
+ if (container->y() >= 0)
+ container->move(container->x(), 0);
+ else
+ container->move(container->x(), this->height() - container->height());
+ nextMove = 1;
+ moveStored = 0;
+ }
+ }
+ }
+ }
+ lastY = event->pos().y();
+ }
+}
+
+void ScrollListWidget::mouseReleaseEvent(QMouseEvent* event) {
+ //start scrolling
+ if (container->y() > 0 || container->y() + container->height() < this->height())
+ bounceBack();
+ else
+ rfrshView->start(30);
+ pressed = false;
+}
+
+void ScrollListWidget::bounceBack() {
+ rfrshView->stop();
+ getCord->stop();
+ bounce->setDuration(500);
+ bounce->setStartValue(container->pos());
+ if (container->y() > 0)
+ bounce->setEndValue(QPoint(container->x(), 0));
+ else
+ bounce->setEndValue(QPoint(container->x(), this->height() - container->height()));
+ bounce->setEasingCurve(QEasingCurve::OutQuad);
+ bounce->start();
+}
+
+void ScrollListWidget::scrollContainer() {
+ //scroll
+ if (curSpd > 0) {
+ if (curSpd > MAXSPEED && !ignoreMaxSpeed)
+ curSpd = MAXSPEED;
+ else if (curSpd <= MAXSPEED) ignoreMaxSpeed = false;
+ int dp = scrollDown ? curSpd : -curSpd;
+ container->move(container->x(), container->y() + dp);
+ }
+ else
+ return;
+ if (container->y() <= 0 && container->y() + container->height() >= this->height()) {
+ curSpd -= damp;
+ curSpd = curSpd < 0 ? 0 : curSpd;
+ }
+ else
+ curSpd /= 2;
+ if (curSpd == 0 && (container->y() > 0 || container->y() + container->height() < this->height()))
+ bounceBack();
+ else
+ rfrshView->start(30);
+}
+
+void ScrollListWidget::updateSpd() {
+ int spd = lastY - strtY;
+ scrollDown = spd >= 0;
+ strtY = lastY;
+ curSpd = abs(spd);
+}
+
+void ScrollListWidget::addWidget(QWidget* newWidget, bool setAnimation) {
+ newWidget->setParent(container);
+ container->AddWidget(newWidget, setAnimation);
+}
+
+void ScrollListWidget::removeWidget(QWidget* w) {
+ container->RemoveWidget(w);
+}
+
+void ScrollListWidget::scrollToTop() {
+ curSpd = sqrt(8 * (-container->pos().y()) + 2) / 2;
+ scrollDown = true;
+ getCord->stop();
+ rfrshView->stop();
+ outOfEdge = false;
+ moveStored = 0;
+ nextMove = 1;
+ ignoreMaxSpeed = true;
+ rfrshView->start(30);
+}
+
+void ScrollListWidget::updateHeight() {
+ container->updateHeight();
+}
+
+void ScrollListWidget::clear() {
+ container->clear();
+}
+
+void ScrollListWidget::scrollIndicator(int dp) {
+ int newY = container->y() - dp * container->height() / this->height();
+ if (newY > 0)
+ newY = 0;
+ else if (newY < this->height() - container->height())
+ newY = this->height() - container->height();
+ container->move(container->x(), newY);
+ update();
+}
+
+void ScrollListWidget::wheelEvent(QWheelEvent* event) {
+ if (container->y() > 0 || container->y() + container->height() < this->height())
+ return;
+ curSpd += 5;
+ bool newDirection = event->angleDelta().y() > 0;
+ if (newDirection != scrollDown)
+ curSpd = 5;
+ if (curSpd > MAXSPEED)
+ curSpd = MAXSPEED;
+ scrollDown = newDirection;
+ if (!rfrshView->isActive())
+ rfrshView->start(30);
+ update();
+}
+
+ScrollListContainer::ScrollListContainer(QWidget* parent) : QWidget(parent) {}
+
+void ScrollListContainer::paintEvent(QPaintEvent* event) {
+ for (int i = 0; i < widgets.size(); i++) {
+ widgets[i]->resize(this->width(), widgets[i]->height());
+ }
+}
+
+void ScrollListContainer::AddWidget(QWidget* widget, bool setAnimation) {
+ //Add animation for all widgets current
+ this->resize(this->width(), this->height() + widget->height() + spacing);
+ widgets.push_back(widget);
+ size++;
+ ys.push_back(0);
+ widget->resize(this->width(), widget->height());
+ widget->show();
+
+ if (setAnimation) {
+ QGraphicsOpacityEffect* widgetOpac = new QGraphicsOpacityEffect(widget);
+ widgetOpac->setOpacity(0);
+ widget->setGraphicsEffect(widgetOpac);
+ QParallelAnimationGroup* dpGroup = new QParallelAnimationGroup;
+ QSequentialAnimationGroup* newWidgetFadeIn = new QSequentialAnimationGroup;
+ for (int i = 0; i < size - 1; i++) {
+ ys[i] += widget->height() + spacing;
+ QPropertyAnimation* move = new QPropertyAnimation(widgets[i], "pos");
+ move->setDuration(750);
+ move->setStartValue(widgets[i]->pos());
+ move->setEndValue(QPoint(widgets[i]->x(), ys[i]));
+ move->setEasingCurve(QEasingCurve::InOutQuart);
+ dpGroup->addAnimation(move);
+ }
+ newWidgetFadeIn->addPause(300);
+ QPropertyAnimation* fade = new QPropertyAnimation(widgetOpac, "opacity", widget);
+ fade->setDuration(300);
+ fade->setStartValue(0);
+ fade->setEndValue(0.99);
+ newWidgetFadeIn->addAnimation(fade);
+ dpGroup->addAnimation(newWidgetFadeIn);
+ dpGroup->start();
+ connect(dpGroup, &QPropertyAnimation::stateChanged, [=]() {
+ if (dpGroup->state() == QAbstractAnimation::Stopped) {
+ if (widgetOpac->opacity() != 0.99) {
+ fade->start(QAbstractAnimation::DeleteWhenStopped);
+ connect(fade, &QPropertyAnimation::finished, [=]() {widgetOpac->deleteLater(); });
+ }
+ else {
+ dpGroup->deleteLater();
+ widgetOpac->deleteLater();
+ }
+ }
+ });
+ }
+ else {
+ for (int i = 0; i < size - 1; i++) {
+ ys[i] += widget->height() + spacing;
+ widgets[i]->move(QPoint(widgets[i]->pos().x(), ys[i]));
+ }
+ }
+}
+
+void ScrollListContainer::RemoveWidget(QWidget* widget) {
+ int index;
+ if (widget == nullptr) {
+ index = size - 1;
+ if (index != -1)
+ widget = widgets[index];
+ }
+ else
+ index = widgets.indexOf(widget);
+ if (index == -1 || widget == nullptr) {
+ return;
+ }
+ this->resize(this->width(), this->height() - widget->height() - spacing);
+ this->parentWidget()->update();
+ widget->hide();
+ widget->setParent(nullptr);
+ QParallelAnimationGroup* dpGroup = new QParallelAnimationGroup;
+ for (int i = index - 1; i >= 0; i--) {
+ ys[i] -= (widget->height() + spacing);
+ QPropertyAnimation* move = new QPropertyAnimation(widgets[i], "pos");
+ move->setDuration(750);
+ move->setStartValue(widgets[i]->pos());
+ move->setEndValue(QPoint(widgets[i]->x(), ys[i]));
+ move->setEasingCurve(QEasingCurve::InOutQuart);
+ dpGroup->addAnimation(move);
+ }
+ dpGroup->start(QAbstractAnimation::DeleteWhenStopped);
+ widgets.remove(index);
+ size--;
+ ys.remove(index);
+}
+
+void ScrollListContainer::updateHeight() {
+ for (int i = size - 2; i >= 0; i--) {
+ ys[i] = ys[i + 1] + widgets[i + 1]->height() + spacing;
+ widgets[i]->move(widgets[i]->pos().x(), ys[i]);
+ }
+ this->resize(this->width(), ys[0] + widgets[0]->height() + 3);
+}
+
+void ScrollListContainer::clear() {
+ int n = size;
+ for (int i = 0; i < n; i++)
+ RemoveWidget();
+}
+
+ScrollListIndicator::ScrollListIndicator(QWidget* parent) : QWidget(parent)
+{
+ this->resize(defaultWidth, 0);
+ hovTimer = new QTimer(this);
+ hovTimer->setSingleShot(true);
+ aniPause = new QTimer(this);
+ aniPause->setSingleShot(true);
+ QObject::connect(hovTimer, SIGNAL(timeout()), this, SLOT(setHoverActive()));
+ this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ this->curColor = defaultColor;
+
+ this->setMouseTracking(true);
+}
+
+void ScrollListIndicator::paintEvent(QPaintEvent* event) {
+ QPainter painter(this);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(curColor);
+ painter.drawRect(this->rect());
+}
+
+void ScrollListIndicator::enterEvent(QEnterEvent* event) {
+ if (!pressed) {
+ hovTimer->start(100);
+ curColor = hoverColor;
+ update();
+ }
+}
+
+void ScrollListIndicator::leaveEvent(QEvent* event) {
+ hovTimer->stop();
+ curColor = defaultColor;
+ QPropertyAnimation* narrow = new QPropertyAnimation(this, "geometry");
+ narrow->setDuration(300);
+ narrow->setStartValue(QRect(this->x(), this->y(), this->width(), this->height()));
+ narrow->setEndValue(QRect(this->parentWidget()->width() - margin - defaultWidth, this->y(), defaultWidth, this->height()));
+ narrow->setEasingCurve(QEasingCurve::InOutQuad);
+ narrow->start(QAbstractAnimation::DeleteWhenStopped);
+ aniPause->start(300);
+ update();
+}
+
+void ScrollListIndicator::mousePressEvent(QMouseEvent* event) {
+ curColor = pressColor;
+ pressed = true;
+ //>note: globalPos -> globalPosition here due to deprecation
+ //> may cause issues
+#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
+ lastY = event->globalPosition().y();
+#else
+ lastY = event->globalPos().y();
+#endif
+ update();
+}
+
+void ScrollListIndicator::mouseMoveEvent(QMouseEvent* event) {
+ if (pressed && !aniPause->isActive()) {
+ //>note: globalPos -> globalPosition here due to deprecation
+ //> may cause issues
+#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
+ int dp = event->globalPosition().y() - lastY;
+#else
+ int dp = event->globalPos().y() - lastY;
+#endif
+ emit scrollPage(dp);
+ //>note: globalPos -> globalPosition here due to deprecation
+ //> may cause issues
+#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
+ lastY = event->globalPosition().y();
+#else
+ lastY = event->globalPos().y();
+#endif
+ }
+}
+
+void ScrollListIndicator::mouseReleaseEvent(QMouseEvent* event) {
+ pressed = false;
+ curColor = hoverColor;
+ update();
+}
+
+void ScrollListIndicator::setHoverActive() {
+ QPropertyAnimation* widen = new QPropertyAnimation(this, "geometry");
+ widen->setDuration(300);
+ widen->setStartValue(QRect(this->x(), this->y(), this->width(), this->height()));
+ widen->setEndValue(QRect(this->parentWidget()->width() - margin - defaultWidthAtFocus, this->y(), defaultWidthAtFocus, this->height()));
+ widen->setEasingCurve(QEasingCurve::InOutQuad);
+ widen->start(QAbstractAnimation::DeleteWhenStopped);
+ aniPause->start(300);
+}
diff --git a/FinalProject/scrolllistwidget.h b/FinalProject/scrolllistwidget.h
new file mode 100644
index 0000000..4ace7a1
--- /dev/null
+++ b/FinalProject/scrolllistwidget.h
@@ -0,0 +1,142 @@
+/* This Module has not yet been rewritten */
+/* Behavior and performance is not guaranteed*/
+/* @Linloir */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAXSPEED 70
+
+class ScrollListWidget;
+class ScrollListContainer;
+class ScrollListIndicator;
+
+class ScrollListWidget : public QWidget
+{
+ Q_OBJECT
+
+private:
+ QTimer* getCord;
+ QTimer* rfrshView;
+
+ ScrollListContainer* container;
+ ScrollListIndicator* indicator;
+
+ QPropertyAnimation* bounce;
+
+ bool pressed = false;
+ bool scrollDown = true;
+ bool outOfEdge = false;
+ bool ignoreMaxSpeed = false;
+
+ int strtY;
+ int lastY;
+ int bfEdgeY; //last y value before out of edge
+
+ int curSpd = 0;
+ int damp = 1;
+ int moveStored = 0;
+ int nextMove = 1;
+
+ void paintEvent(QPaintEvent* event);
+ void mousePressEvent(QMouseEvent* event);
+ void mouseMoveEvent(QMouseEvent* event);
+ void mouseReleaseEvent(QMouseEvent* event);
+ void wheelEvent(QWheelEvent* event);
+ void bounceBack();
+
+
+public:
+ explicit ScrollListWidget(QWidget* parent = nullptr);
+ void addWidget(QWidget* newWidget, bool setAnimation = true);
+ void addWidgets(QVector widgets) { for (int i = 0; i < widgets.size(); i++)addWidget(widgets[i], false); }
+ void removeWidget(QWidget* w = nullptr);
+ void scrollToTop();
+ void updateHeight();
+ void clear();
+
+signals:
+
+private slots:
+ void scrollContainer();
+ void updateSpd();
+ void scrollIndicator(int dp);
+
+};
+
+class ScrollListContainer : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit ScrollListContainer(QWidget* parent = nullptr);
+ void AddWidget(QWidget* widget, bool setAnimation = true);
+ void RemoveWidget(QWidget* widget = nullptr);
+ void updateHeight();
+ void clear();
+ //void RemoveWidget(QWidget* widget);
+
+private:
+ //QTimer* newWidgetFade;
+ int spacing = 3;
+ QVector widgets;
+ int size = 0;
+ QVector ys;
+
+ void paintEvent(QPaintEvent* event);
+
+signals:
+
+private slots:
+
+};
+
+class ScrollListIndicator : public QWidget
+{
+ Q_OBJECT
+
+private:
+ QColor curColor;
+ QColor defaultColor = QColor(100, 100, 100, 130);
+ QColor hoverColor = QColor(70, 70, 70, 150);
+ QColor pressColor = QColor(50, 50, 50, 170);
+
+ QTimer* hovTimer;
+ QTimer* aniPause;
+
+ int lastY;
+
+ int defaultWidth = 2;
+ int defaultWidthAtFocus = 9;
+ int margin = 3;
+
+ bool pressed = false;
+
+public:
+ explicit ScrollListIndicator(QWidget* parent = nullptr);
+
+private:
+ void paintEvent(QPaintEvent* event);
+ void enterEvent(QEnterEvent* event);
+ void leaveEvent(QEvent* event);
+ void mousePressEvent(QMouseEvent* event);
+ void mouseMoveEvent(QMouseEvent* event);
+ void mouseReleaseEvent(QMouseEvent* event);
+
+signals:
+ void scrollPage(int);
+
+private slots:
+ void setHoverActive();
+
+};
diff --git a/FinalProject/sidebar.cpp b/FinalProject/sidebar.cpp
new file mode 100644
index 0000000..4ee8dd1
--- /dev/null
+++ b/FinalProject/sidebar.cpp
@@ -0,0 +1,286 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "sidebar.h"
+
+SideBar::SideBar(QWidget* parent)
+ : QWidget(parent)
+{
+ setAttribute(Qt::WA_StyledBackground, true);
+ setMaximumWidth(_collapsedWidth);
+
+ // Create stylesheet
+ setObjectName("sideBar");
+ QString sideBarStyleSheet = "QWidget#sideBar{background-color:" + _backgroundColor.name(QColor::HexArgb) + "; }";
+ setStyleSheet(sideBarStyleSheet);
+
+ // Create main layout
+ _mainLayout = new QVBoxLayout(this);
+ _mainLayout->setSpacing(8);
+ _mainLayout->setContentsMargins(16, 16, 16, 16);
+ _mainLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ _mainLayout->addSpacing(8); // Add the top spacing between the expand button and the window edge
+
+ // Create expand button
+ _expandButton = new PushButton(nullptr, this);
+ _expandButton->setMargin(18, 16, 18, 16);
+ _expandButton->setIndicatorColor(QColor(255, 255, 255, 0)); // Set indicator to transparent
+ _expandButton->setHoverColor(QColor(0, 0, 0, 10));
+ _expandButton->setPressedColor(QColor(0, 0, 0, 20));
+ int buttonSize = _collapsedWidth - _mainLayout->contentsMargins().left() - _mainLayout->contentsMargins().right();
+ int buttonSizeMax = _expandedWidth - _mainLayout->contentsMargins().left() - _mainLayout->contentsMargins().right();
+ _expandButton->setMinimumSize(buttonSize, buttonSize);
+ _expandButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ _expandButtonIcon = new QLabel(_expandButton);
+ _expandButtonIcon->setFont(QFont("Font Awesome 6 Free Solid", 14));
+ _expandButtonIcon->setText("\uf0c9");
+ _expandButton->setChildWidget(_expandButtonIcon);
+
+ // Connect the expand button event to expand / collapse event
+ connect(_expandButton, &PushButton::onClick, this, [=]() {
+ if (_expanded) {
+ collapse();
+ }
+ else {
+ expand();
+ }
+ });
+
+ // Add expand button to main layout
+ _mainLayout->addWidget(_expandButton);
+ _expandButton->show();
+
+ // Create the page icon button container widget
+ _pageIconButtonWidget = new QWidget(this);
+ _pageIconButtonWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ _pageIconButtonLayout = new QVBoxLayout(_pageIconButtonWidget);
+ _pageIconButtonLayout->setSpacing(8);
+ _pageIconButtonLayout->setContentsMargins(0, 0, 0, 0);
+ _pageIconButtonLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ _pageIconButtonWidget->setLayout(_pageIconButtonLayout);
+
+ // Create the page text button container widget
+ _pageTextButtonWidget = new QWidget(this);
+ _pageTextButtonWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ _pageTextButtonLayout = new QVBoxLayout(_pageTextButtonWidget);
+ _pageTextButtonLayout->setSpacing(8);
+ _pageTextButtonLayout->setContentsMargins(0, 0, 0, 0);
+ _pageTextButtonLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ _pageTextButtonWidget->setLayout(_pageTextButtonLayout);
+
+ // Add page icon and text button container widgets to main layout
+ _mainLayout->addWidget(_pageIconButtonWidget);
+ _mainLayout->addWidget(_pageTextButtonWidget);
+
+ // Display the icon buttons
+ _pageIconButtonWidget->show();
+ _pageTextButtonWidget->hide();
+}
+
+SideBar::~SideBar() {}
+
+void SideBar::expand() {
+ // Add animation to expand the side bar
+ QPropertyAnimation* expandAnimation = new QPropertyAnimation(this, "maximumWidth");
+ expandAnimation->setDuration(650);
+ expandAnimation->setEasingCurve(QEasingCurve::OutExpo);
+ expandAnimation->setStartValue(width());
+ expandAnimation->setEndValue(_expandedWidth);
+ expandAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ // Show the page text buttons
+ _pageIconButtonWidget->hide();
+ _pageTextButtonWidget->show();
+
+ // Set the expand state
+ _expanded = true;
+}
+
+void SideBar::collapse() {
+ // Add animation to collapse the side bar
+ QPropertyAnimation* collapseAnimation = new QPropertyAnimation(this, "maximumWidth");
+ collapseAnimation->setDuration(650);
+ collapseAnimation->setEasingCurve(QEasingCurve::OutExpo);
+ collapseAnimation->setStartValue(width());
+ collapseAnimation->setEndValue(_collapsedWidth);
+ collapseAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+
+ // Show the page icon buttons
+ _pageIconButtonWidget->show();
+ _pageTextButtonWidget->hide();
+
+ // Set the expand state
+ _expanded = false;
+}
+
+void SideBar::setBackgroundColor(QColor color) {
+ _backgroundColor = color;
+ QString sideBarStyleSheet = "QWidget#sideBar{background-color:" + _backgroundColor.name(QColor::HexArgb) + "; }";
+ setStyleSheet(sideBarStyleSheet);
+}
+
+void SideBar::setExpandedWidth(int width) {
+ if (width <= 0) {
+ return;
+ }
+ _expandedWidth = width;
+}
+
+void SideBar::setCollapsedWidth(int width) {
+ if (width <= 0) {
+ return;
+ }
+ _collapsedWidth = width;
+}
+
+void SideBar::selectPage(PageWidget* page) {
+ // Check for input validity
+ if (page == nullptr) {
+ return;
+ }
+
+ // Deselect current page if there is one
+ if (_currentPage != nullptr) {
+ // Find the buttons of the current page
+ int index = _pageList.indexOf(_currentPage);
+ PushButton* currentPageIconButton = _pageButtonList.at(index).first;
+ PushButton* currentPageTextButton = _pageButtonList.at(index).second;
+
+ // Deselect both the buttons
+ currentPageIconButton->deselect();
+ currentPageTextButton->deselect();
+ }
+
+ // Temporarily store the current page
+ PageWidget* previousPage = _currentPage;
+
+ // Select the new page
+ int index = _pageList.indexOf(page);
+ PushButton* newPageIconButton = _pageButtonList.at(index).first;
+ PushButton* newPageTextButton = _pageButtonList.at(index).second;
+ newPageIconButton->select();
+ newPageTextButton->select();
+ _currentPage = page;
+
+ // Emit the page selected signal
+ emit onPageChanged(previousPage, page);
+}
+
+void SideBar::addPage(PageWidget* page) {
+ // Call on the page's get button method to get icon button & text button
+ PushButton* iconButton = page->getPageIconButton(_pageIconButtonWidget);
+ PushButton* textButton = page->getPageTextButton(_pageTextButtonWidget);
+
+ // Store the page in the page list
+ _pageList.append(page);
+
+ // Store the corresponding buttons
+ _pageButtonList.append(QPair(iconButton, textButton));
+
+ // Add the icon button to the icon button layout
+ _pageIconButtonLayout->addWidget(iconButton);
+ iconButton->show();
+
+ // Add the text button to the text button layout
+ _pageTextButtonLayout->addWidget(textButton);
+ textButton->show();
+
+ // Resize the buttons and set size policy to fixed
+ int buttonSize = _collapsedWidth - _mainLayout->contentsMargins().left() - _mainLayout->contentsMargins().right();
+ int buttonSizeMax = _expandedWidth - _mainLayout->contentsMargins().left() - _mainLayout->contentsMargins().right();
+ iconButton->setMinimumSize(buttonSize, buttonSize);
+ textButton->setMinimumSize(buttonSize, buttonSize);
+ iconButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ textButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+
+ // Connect the button click event with page selection
+ connect(iconButton, &PushButton::onClick, this, [=]() {
+ selectPage(page);
+ });
+ connect(textButton, &PushButton::onClick, this, [=]() {
+ selectPage(page);
+ });
+
+ // Select the page if there are no pages
+ if (_currentPage == nullptr) {
+ selectPage(page);
+ }
+}
+
+void SideBar::insertPage(PageWidget* page, int index) {
+ // Call on the page's get button method to get icon button & text button
+ PushButton* iconButton = page->getPageIconButton(_pageIconButtonWidget);
+ PushButton* textButton = page->getPageTextButton(_pageTextButtonWidget);
+
+ // Store the page
+ _pageList.insert(index, page);
+
+ // Store the corresponding buttons
+ _pageButtonList.insert(index, QPair(iconButton, textButton));
+
+ // Add the icon button to the icon button layout
+ _pageIconButtonLayout->insertWidget(index, iconButton);
+ iconButton->show();
+
+ // Add the text button to the text button layout
+ _pageTextButtonLayout->insertWidget(index, textButton);
+ textButton->show();
+
+ // Resize the buttons and set size policy to fixed
+ int buttonSize = _collapsedWidth - _mainLayout->contentsMargins().left() - _mainLayout->contentsMargins().right();
+ int buttonSizeMax = _expandedWidth - _mainLayout->contentsMargins().left() - _mainLayout->contentsMargins().right();
+ iconButton->setMinimumSize(buttonSize, buttonSize);
+ textButton->setMinimumSize(buttonSize, buttonSize);
+ iconButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ textButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+
+ // Connect the button click event with page selection
+ connect(iconButton, &PushButton::onClick, this, [=]() {
+ selectPage(page);
+ });
+ connect(textButton, &PushButton::onClick, this, [=]() {
+ selectPage(page);
+ });
+
+ // Select the page if there are no pages
+ if (_currentPage == nullptr) {
+ selectPage(page);
+ }
+}
+
+void SideBar::removePage(PageWidget* page) {
+ // Get the index of the page from the page list
+ int index = _pageList.indexOf(page);
+
+ // Get the corresponding buttons
+ PushButton* iconButton = _pageButtonList.at(index).first;
+ PushButton* textButton = _pageButtonList.at(index).second;
+
+ // Remove the page from the list
+ _pageList.removeAt(index);
+
+ // Remove the icon and text buttons from the list
+ _pageButtonList.removeAt(index);
+
+ // Remove the icon button from the icon button layout
+ _pageIconButtonLayout->removeWidget(iconButton);
+ iconButton->hide();
+
+ // Remove the text button from the text button layout
+ _pageTextButtonLayout->removeWidget(textButton);
+ textButton->hide();
+
+ // Select the first page if the current page is the page being removed
+ if (_currentPage == page) {
+ if (_pageList.size() > 0) {
+ selectPage(_pageList.first());
+ }
+ else {
+ _currentPage = nullptr;
+ }
+ }
+}
diff --git a/FinalProject/sidebar.h b/FinalProject/sidebar.h
new file mode 100644
index 0000000..7ebfe07
--- /dev/null
+++ b/FinalProject/sidebar.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "pushbutton.h"
+#include "pagewidget.h"
+
+class SideBar : public QWidget
+{
+ Q_OBJECT
+
+public:
+ SideBar(QWidget* parent = 0);
+ ~SideBar();
+
+private:
+ // Initialized flag
+ bool _initialized = false;
+
+ // UI control variables
+ QColor _backgroundColor = QColor(230, 230, 230);
+ int _collapsedWidth = 86;
+ int _expandedWidth = 200;
+
+ // UI widgets
+ PushButton* _expandButton;
+ QLabel* _expandButtonIcon;
+ QVBoxLayout* _mainLayout = nullptr; // Layout expand icon and page selection buttons
+ QWidget* _pageIconButtonWidget = nullptr; // Container for page icon buttons
+ QVBoxLayout* _pageIconButtonLayout = nullptr; // Layout page icon buttons
+ QWidget* _pageTextButtonWidget = nullptr; // Container for page text buttons
+ QVBoxLayout* _pageTextButtonLayout = nullptr; // Layout page text buttons
+
+ // Interaction controls
+ bool _expanded = false;
+ PageWidget* _currentPage = nullptr;
+
+ // State storage
+ // QMap> _pageButtonMap;
+ QList _pageList;
+ QList> _pageButtonList;
+
+private:
+ void expand();
+ void collapse();
+ void selectPage(PageWidget* page);
+
+public:
+ void setBackgroundColor(QColor color);
+ void setCollapsedWidth(int width);
+ void setExpandedWidth(int width);
+ void addPage(PageWidget* page);
+ void insertPage(PageWidget* page, int index);
+ void removePage(PageWidget* page);
+
+signals:
+ void onPageChanged(PageWidget* previousPage, PageWidget* currentPage);
+};