Merge UI development

UI development
This commit is contained in:
Linloir 2022-12-17 13:04:13 +08:00 committed by GitHub
commit ef9580af3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 4106 additions and 425 deletions

View File

@ -103,23 +103,32 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="aboutpage.cpp" />
<ClCompile Include="camera.cpp" /> <ClCompile Include="camera.cpp" />
<ClCompile Include="ebo.cpp" /> <ClCompile Include="ebo.cpp" />
<ClCompile Include="editorpage.cpp" />
<ClCompile Include="framelesswindow.cpp" />
<ClCompile Include="lineeditwidget.cpp" />
<ClCompile Include="logger.cpp" /> <ClCompile Include="logger.cpp" />
<ClCompile Include="mesh.cpp" /> <ClCompile Include="mesh.cpp" />
<ClCompile Include="model.cpp" /> <ClCompile Include="model.cpp" />
<ClCompile Include="modelselectable.cpp" /> <ClCompile Include="modelselectable.cpp" />
<ClCompile Include="modelselector.cpp" /> <ClCompile Include="modelselector.cpp" />
<ClCompile Include="modelsetter.cpp" /> <ClCompile Include="modelsetter.cpp" />
<ClCompile Include="modelthumbnailwidget.cpp" />
<ClCompile Include="pagewidget.cpp" />
<ClCompile Include="pushbutton.cpp" />
<ClCompile Include="renderable.cpp" /> <ClCompile Include="renderable.cpp" />
<ClCompile Include="roundedcornerwidget.cpp" />
<ClCompile Include="sceneviewer.cpp" /> <ClCompile Include="sceneviewer.cpp" />
<ClCompile Include="scrolllistwidget.cpp" />
<ClCompile Include="shader.cpp" /> <ClCompile Include="shader.cpp" />
<ClCompile Include="sidebar.cpp" />
<ClCompile Include="texture.cpp" /> <ClCompile Include="texture.cpp" />
<ClCompile Include="vao.cpp" /> <ClCompile Include="vao.cpp" />
<ClCompile Include="vbo.cpp" /> <ClCompile Include="vbo.cpp" />
<ClCompile Include="vertex.cpp" /> <ClCompile Include="vertex.cpp" />
<QtRcc Include="mainwindow.qrc" /> <QtRcc Include="mainwindow.qrc" />
<QtUic Include="mainwindow.ui" />
<QtMoc Include="mainwindow.h" /> <QtMoc Include="mainwindow.h" />
<ClCompile Include="mainwindow.cpp" /> <ClCompile Include="mainwindow.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
@ -128,18 +137,28 @@
<QtMoc Include="sceneviewer.h" /> <QtMoc Include="sceneviewer.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="aboutpage.h" />
<ClInclude Include="camera.h" /> <ClInclude Include="camera.h" />
<QtMoc Include="editorpage.h" />
<QtMoc Include="framelesswindow.h" />
<QtMoc Include="lineeditwidget.h" />
<ClInclude Include="logger.h" /> <ClInclude Include="logger.h" />
<ClInclude Include="mesh.h" /> <ClInclude Include="mesh.h" />
<ClInclude Include="model.h" /> <ClInclude Include="model.h" />
<QtMoc Include="sidebar.h" />
<QtMoc Include="pagewidget.h" />
<QtMoc Include="pushbutton.h" />
<QtMoc Include="roundedcornerwidget.h" />
<QtMoc Include="scrolllistwidget.h" />
<QtMoc Include="modelthumbnailwidget.h" />
<ClInclude Include="texture.h" /> <ClInclude Include="texture.h" />
<ClInclude Include="utils.h" /> <ClInclude Include="utils.h" />
<ClInclude Include="vbo.h" /> <ClInclude Include="vbo.h" />
<ClInclude Include="ebo.h" /> <ClInclude Include="ebo.h" />
<ClInclude Include="vertex.h" /> <ClInclude Include="vertex.h" />
<ClInclude Include="vao.h" /> <ClInclude Include="vao.h" />
<ClInclude Include="modelselectable.h" /> <QtMoc Include="modelselectable.h" />
<ClInclude Include="modelselector.h" /> <QtMoc Include="modelselector.h" />
<ClInclude Include="modelsetter.h" /> <ClInclude Include="modelsetter.h" />
<ClInclude Include="renderable.h" /> <ClInclude Include="renderable.h" />
<ClInclude Include="shader.h" /> <ClInclude Include="shader.h" />

View File

@ -13,10 +13,6 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter> </Filter>
<Filter Include="Form Files">
<UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
<Extensions>ui</Extensions>
</Filter>
<Filter Include="Translation Files"> <Filter Include="Translation Files">
<UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier>
<Extensions>ts</Extensions> <Extensions>ts</Extensions>
@ -39,14 +35,53 @@
<Filter Include="Source Files\Utils"> <Filter Include="Source Files\Utils">
<UniqueIdentifier>{602fc1ed-84a6-4d4a-ad71-5e7415d8d429}</UniqueIdentifier> <UniqueIdentifier>{602fc1ed-84a6-4d4a-ad71-5e7415d8d429}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Header Files\Qt Widgets\Pages">
<UniqueIdentifier>{dd0c6d73-36ab-44ac-9780-4c378faa5774}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Qt Widgets\Pages\Scene Editor">
<UniqueIdentifier>{d0eeae28-809f-4e8e-b1e0-d5d7c5cca3e7}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Qt Widgets\Pages">
<UniqueIdentifier>{0ee9dd70-07d0-44ec-9cb5-37478f6d9ede}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Qt Widgets\Pages\Scene Editor">
<UniqueIdentifier>{c6368565-9e0a-487b-953c-6c4bc9e3f655}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Qt Widgets\Pages\About Page">
<UniqueIdentifier>{2ce6e2ec-f0bf-4d49-8b11-cdf0f31693c5}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Qt Widgets\Pages\About Page">
<UniqueIdentifier>{f8b17ddd-9689-45cc-84a1-5c321b0290b0}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Qt Widgets\GUI Components">
<UniqueIdentifier>{469e46b8-328a-474b-bf82-19c86b980e82}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Qt Widgets\GUI Components">
<UniqueIdentifier>{1351d841-7665-48d3-9850-da0738823396}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Qt Widgets\Pages\Scene Editor\Object Selector">
<UniqueIdentifier>{5e6e1e89-1c77-4a89-92cc-d64ea2546961}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Qt Widgets\Pages\Scene Editor\Object Setter">
<UniqueIdentifier>{d2b9abbc-ef10-4990-96a1-a88f9c217dcf}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Qt Widgets\Pages\Scene Editor\Scene Viewer">
<UniqueIdentifier>{d6a38868-baed-45d5-9cbf-3faa3b563fa9}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Qt Widgets\Pages\Scene Editor\Scene Viewer">
<UniqueIdentifier>{4b5e61f5-3dfe-44d7-bfcc-cf5b70528b16}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Qt Widgets\Pages\Scene Editor\Object Selector">
<UniqueIdentifier>{995296ff-ac0c-4996-9692-c0e017e5fc03}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Qt Widgets\Pages\Scene Editor\Object Setter">
<UniqueIdentifier>{6c5ac0df-26ad-40ad-860c-3d0372b81094}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtRcc Include="mainwindow.qrc"> <QtRcc Include="mainwindow.qrc">
<Filter>Resource Files</Filter> <Filter>Resource Files</Filter>
</QtRcc> </QtRcc>
<QtUic Include="mainwindow.ui">
<Filter>Form Files</Filter>
</QtUic>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp"> <ClCompile Include="main.cpp">
@ -85,21 +120,51 @@
<ClCompile Include="mainwindow.cpp"> <ClCompile Include="mainwindow.cpp">
<Filter>Source Files\Qt Widgets</Filter> <Filter>Source Files\Qt Widgets</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="sceneviewer.cpp">
<Filter>Source Files\Qt Widgets</Filter>
</ClCompile>
<ClCompile Include="modelselector.cpp">
<Filter>Source Files\Qt Widgets</Filter>
</ClCompile>
<ClCompile Include="modelselectable.cpp">
<Filter>Source Files\Qt Widgets</Filter>
</ClCompile>
<ClCompile Include="modelsetter.cpp">
<Filter>Source Files\Qt Widgets</Filter>
</ClCompile>
<ClCompile Include="logger.cpp"> <ClCompile Include="logger.cpp">
<Filter>Source Files\Utils</Filter> <Filter>Source Files\Utils</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="editorpage.cpp">
<Filter>Source Files\Qt Widgets\Pages\Scene Editor</Filter>
</ClCompile>
<ClCompile Include="framelesswindow.cpp">
<Filter>Source Files\Qt Widgets\GUI Components</Filter>
</ClCompile>
<ClCompile Include="pushbutton.cpp">
<Filter>Source Files\Qt Widgets\GUI Components</Filter>
</ClCompile>
<ClCompile Include="scrolllistwidget.cpp">
<Filter>Source Files\Qt Widgets\GUI Components</Filter>
</ClCompile>
<ClCompile Include="sidebar.cpp">
<Filter>Source Files\Qt Widgets\GUI Components</Filter>
</ClCompile>
<ClCompile Include="pagewidget.cpp">
<Filter>Source Files\Qt Widgets\GUI Components</Filter>
</ClCompile>
<ClCompile Include="roundedcornerwidget.cpp">
<Filter>Source Files\Qt Widgets\GUI Components</Filter>
</ClCompile>
<ClCompile Include="aboutpage.cpp">
<Filter>Source Files\Qt Widgets\Pages\About Page</Filter>
</ClCompile>
<ClCompile Include="lineeditwidget.cpp">
<Filter>Source Files\Qt Widgets\GUI Components</Filter>
</ClCompile>
<ClCompile Include="modelselector.cpp">
<Filter>Source Files\Qt Widgets\Pages\Scene Editor\Object Selector</Filter>
</ClCompile>
<ClCompile Include="modelselectable.cpp">
<Filter>Source Files\Qt Widgets\Pages\Scene Editor\Object Selector</Filter>
</ClCompile>
<ClCompile Include="modelsetter.cpp">
<Filter>Source Files\Qt Widgets\Pages\Scene Editor\Object Setter</Filter>
</ClCompile>
<ClCompile Include="sceneviewer.cpp">
<Filter>Source Files\Qt Widgets\Pages\Scene Editor\Scene Viewer</Filter>
</ClCompile>
<ClCompile Include="modelthumbnailwidget.cpp">
<Filter>Source Files\Qt Widgets\Pages\Scene Editor\Object Selector</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="camera.h"> <ClInclude Include="camera.h">
@ -117,12 +182,6 @@
<ClInclude Include="mesh.h"> <ClInclude Include="mesh.h">
<Filter>Header Files\OpenGL Abstractions</Filter> <Filter>Header Files\OpenGL Abstractions</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="modelselector.h">
<Filter>Header Files\Qt Widgets</Filter>
</ClInclude>
<ClInclude Include="modelsetter.h">
<Filter>Header Files\Qt Widgets</Filter>
</ClInclude>
<ClInclude Include="vertex.h"> <ClInclude Include="vertex.h">
<Filter>Header Files\OpenGL Abstractions</Filter> <Filter>Header Files\OpenGL Abstractions</Filter>
</ClInclude> </ClInclude>
@ -141,20 +200,56 @@
<ClInclude Include="utils.h"> <ClInclude Include="utils.h">
<Filter>Header Files\Utils</Filter> <Filter>Header Files\Utils</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="modelselectable.h">
<Filter>Header Files\Qt Widgets</Filter>
</ClInclude>
<ClInclude Include="logger.h"> <ClInclude Include="logger.h">
<Filter>Header Files\Utils</Filter> <Filter>Header Files\Utils</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="modelsetter.h">
<Filter>Header Files\Qt Widgets\Pages\Scene Editor\Object Setter</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="sceneviewer.h">
<Filter>Header Files\Qt Widgets</Filter>
</QtMoc>
<QtMoc Include="mainwindow.h"> <QtMoc Include="mainwindow.h">
<Filter>Header Files\Qt Widgets</Filter> <Filter>Header Files\Qt Widgets</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="framelesswindow.h">
<Filter>Header Files\Qt Widgets\GUI Components</Filter>
</QtMoc>
<QtMoc Include="pushbutton.h">
<Filter>Header Files\Qt Widgets\GUI Components</Filter>
</QtMoc>
<QtMoc Include="pagewidget.h">
<Filter>Header Files\Qt Widgets\GUI Components</Filter>
</QtMoc>
<QtMoc Include="sidebar.h">
<Filter>Header Files\Qt Widgets\GUI Components</Filter>
</QtMoc>
<QtMoc Include="editorpage.h">
<Filter>Header Files\Qt Widgets\Pages\Scene Editor</Filter>
</QtMoc>
<QtMoc Include="roundedcornerwidget.h">
<Filter>Header Files\Qt Widgets\GUI Components</Filter>
</QtMoc>
<QtMoc Include="aboutpage.h">
<Filter>Header Files\Qt Widgets\Pages\About Page</Filter>
</QtMoc>
<QtMoc Include="lineeditwidget.h">
<Filter>Header Files\Qt Widgets\GUI Components</Filter>
</QtMoc>
<QtMoc Include="scrolllistwidget.h">
<Filter>Header Files\Qt Widgets\GUI Components</Filter>
</QtMoc>
<QtMoc Include="sceneviewer.h">
<Filter>Header Files\Qt Widgets\Pages\Scene Editor\Scene Viewer</Filter>
</QtMoc>
<QtMoc Include="modelselector.h">
<Filter>Header Files\Qt Widgets\Pages\Scene Editor\Object Selector</Filter>
</QtMoc>
<QtMoc Include="modelselectable.h">
<Filter>Header Files\Qt Widgets\Pages\Scene Editor\Object Selector</Filter>
</QtMoc>
<QtMoc Include="modelthumbnailwidget.h">
<Filter>Header Files\Qt Widgets\Pages\Scene Editor\Object Selector</Filter>
</QtMoc>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="fragmentshader.fs"> <None Include="fragmentshader.fs">

135
FinalProject/aboutpage.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <qlayoutitem.h>
#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;
}

41
FinalProject/aboutpage.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <qwidget.h>
#include <qlabel.h>
#include <qboxlayout.h>
#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;
};

118
FinalProject/editorpage.cpp Normal file
View File

@ -0,0 +1,118 @@
#pragma once
#include <qfontdatabase.h>
#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;
}

45
FinalProject/editorpage.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include <qwidget.h>
#include <qboxlayout.h>
#include <qlabel.h>
#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;
};

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,373 @@
#include <qapplication.h>
#include <qpainterpath.h>
#include <qscreen.h>
#include <qlabel.h>
#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();
}
}

View File

@ -0,0 +1,82 @@
#pragma once
#include <QtWidgets/QMainWindow>
#include <qevent.h>
#include <qboxlayout.h>
#include <qgraphicseffect.h>
#include <qpushbutton.h>
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);
};

View File

@ -0,0 +1,427 @@
#include <qpropertyanimation.h>
#include <qparallelanimationgroup.h>
#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;
}

View File

@ -0,0 +1,95 @@
#pragma once
#include <qwidget.h>
#include <qlineedit.h>
#include <qboxlayout.h>
#include <qgraphicseffect.h>
#include <qevent.h>
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);
};

View File

@ -1,13 +1,21 @@
#include "mainwindow.h"
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <qfontdatabase.h>
#include <string>
#include "mainwindow.h"
#include "logger.h"
int main(int argc, char *argv[]) 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; MainWindow w;
w.setWindowFlag(Qt::FramelessWindowHint);
w.setAttribute(Qt::WA_TranslucentBackground);
w.setMouseTracking(true); w.setMouseTracking(true);
w.resize(900, 600);
w.show(); w.show();
return a.exec(); return a.exec();
} }

View File

@ -1,318 +1,97 @@
#include <qpainterpath.h>
#include <qscreen.h>
#include "mainwindow.h" #include "mainwindow.h"
#include "sceneviewer.h"
#include "logger.h" #include "logger.h"
#define MAX_MOUSE_MOVEMENT 300 #define MAX_MOUSE_MOVEMENT 300
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : FramelessWindow(20, parent)
{ {
ui.setupUi(this); // Create main layout for page and sidebar
ui.centralWidget->setMouseTracking(true); _mainLayout = new QHBoxLayout(_windowWidget);
_mainLayout->setContentsMargins(0, 0, 0, 0);
_mainLayout->setSpacing(0);
_windowWidget->setLayout(_mainLayout);
// Create and properly set real displayed window widget // Create placeholder widget for pages
_stretchLayout = new QVBoxLayout(this); _placeHolderWidget = new QWidget(_windowWidget);
_stretchLayout->setContentsMargins(30, 30, 30, 30); _placeHolderWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_windowWidget = new QWidget(this); _placeHolderWidget->setMouseTracking(true);
_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);
// Set style sheet for window widget // Connect the resize event of the placeholder widget to the resizePages function using event filter
QString windowWidgetStyleSheet = "QWidget#windowWidget{background-color:" + _backgroundColor.name() + ";border-radius:" + QString::number(_cornerRadius) + "px;}"; _placeHolderWidget->installEventFilter(this);
_windowWidget->setStyleSheet(windowWidgetStyleSheet);
// Set shadow for window widget // Create sidebar
_windowShadow = new QGraphicsDropShadowEffect(_windowWidget); _sideBar = new SideBar(_windowWidget);
_windowShadow->setBlurRadius(30); connect(_sideBar, &SideBar::onPageChanged, this, [=](PageWidget* previousPage, PageWidget* currentPage) {
_windowShadow->setColor(QColor(0, 0, 0)); // Check for input validity
_windowShadow->setOffset(0, 0); if (previousPage == currentPage) {
_windowWidget->setGraphicsEffect(_windowShadow); return;
}
if (previousPage != nullptr) {
previousPage->offStage();
}
if (currentPage != nullptr) {
currentPage->onStage();
}
});
_sideBar->setMouseTracking(true);
// Create title bar widget // Add sidebar and placeholder widget to main layout
_titleBar = new QWidget(_windowWidget); _mainLayout->addWidget(_sideBar);
_titleBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); _mainLayout->addWidget(_placeHolderWidget);
_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 // Create editor page and connect to side bar
_windowBtnWidget = new QWidget(_titleBar); _editorPage = new EditorPage(_placeHolderWidget);
_windowBtnWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); _editorPage->setMouseTracking(true);
_windowBtnLayout = new QHBoxLayout(_windowBtnWidget); _sideBar->addPage(_editorPage);
_windowBtnLayout->setContentsMargins(0, 0, 0, 0);
_windowBtnLayout->setSpacing(8);
_windowBtnWidget->setLayout(_windowBtnLayout);
_titleBarLayout->addWidget(_windowBtnWidget);
_windowBtnWidget->show();
// Create window control buttons // Create about page and connect to side bar
_minimizeBtn = new QPushButton(_windowBtnWidget); _aboutPage = new AboutPage(_placeHolderWidget);
_maximizeBtn = new QPushButton(_windowBtnWidget); _aboutPage->setMouseTracking(true);
_closeBtn = new QPushButton(_windowBtnWidget); _sideBar->addPage(_aboutPage);
_minimizeBtn->setFixedSize(12, 12);
_maximizeBtn->setFixedSize(12, 12);
_closeBtn->setFixedSize(12, 12);
_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);
} }
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
} }
void MainWindow::showEvent(QShowEvent* event) { void MainWindow::resizePages(QResizeEvent* event) {
// Initialize window UI after window is shown // Check for input validity
initializeWindowUI(); if (event == nullptr) {
}
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) {
return; return;
} }
// Set mouse state according to abs distance from window border
if (abs(event->globalPos().x() - (_windowWidget->frameGeometry().left() + frameGeometry().left())) < 5) { // Get the size of the placeholder widget
_mouseState |= MOUSE_STATE_RESIZE_LEFT; QSize size = event->size();
}
if (abs(event->globalPos().x() - (_windowWidget->frameGeometry().right() + frameGeometry().left())) < 5) { // Resize the editor page
_mouseState |= MOUSE_STATE_RESIZE_RIGHT; _editorPage->resize(size);
} _aboutPage->resize(size);
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 MainWindow::mousePressEvent(QMouseEvent* event) { void MainWindow::showEvent(QShowEvent* event) {
if (event->button() == Qt::LeftButton) { // Call parent show event
_mousePressed = true; FramelessWindow::showEvent(event);
_lastMousePosition = event->globalPos().toPointF();
} // Resize all the pages based on the placeholder widget
_editorPage->resize(_placeHolderWidget->size());
_aboutPage->resize(_placeHolderWidget->size());
} }
void MainWindow::mouseReleaseEvent(QMouseEvent* event) { bool MainWindow::eventFilter(QObject* object, QEvent* event) {
_mousePressed = false; // Check for input validity
QScreen* screen = QGuiApplication::screenAt(event->globalPos()); if (object == nullptr || event == nullptr) {
Logger::debug("Current screen geometry:"); return false;
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 MainWindow::mouseMoveEvent(QMouseEvent* event) { // Check if the object is the placeholder widget
Logger::debug("Detected mouse move"); if (object == _placeHolderWidget) {
Logger::debug("[+] mouse global position : " + std::to_string(event->globalPos().x()) + ", " + std::to_string(event->globalPos().y())); // Check if the event is a resize event
Logger::debug("[+] window geometry: " + std::to_string(frameGeometry().x()) + ", " + std::to_string(frameGeometry().y()) + ", " + std::to_string(frameGeometry().width()) + ", " + std::to_string(frameGeometry().height())); if (event->type() == QEvent::Resize) {
Logger::debug("[+] widget frame geometry: " + std::to_string(_windowWidget->frameGeometry().x()) + ", " + std::to_string(_windowWidget->frameGeometry().y())); // Resize all the pages
Logger::debug("[+] widget frame size: " + std::to_string(_windowWidget->frameGeometry().width()) + ", " + std::to_string(_windowWidget->frameGeometry().height())); resizePages(static_cast<QResizeEvent*>(event));
; 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();
} }
// Call parent event filter
return FramelessWindow::eventFilter(object, event);
} }

View File

@ -1,12 +1,13 @@
#pragma once #pragma once
#include <QtWidgets/QMainWindow> #include <qevent.h>
#include <qboxlayout.h>
#include <qgraphicseffect.h>
#include <qpushbutton.h>
#include "ui_mainwindow.h"
class MainWindow : public QMainWindow #include "framelesswindow.h"
#include "sidebar.h"
#include "editorpage.h"
#include "aboutpage.h"
class MainWindow : public FramelessWindow
{ {
Q_OBJECT Q_OBJECT
@ -15,54 +16,21 @@ public:
~MainWindow(); ~MainWindow();
private: 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: private:
// UI control variables void resizePages(QResizeEvent* event);
const int _cornerRadius = 20;
const QColor _backgroundColor = QColor(251, 251, 251);
const QColor _borderColor = QColor(104, 104, 104);
// Window initialize private:
void initializeWindowUI();
virtual void showEvent(QShowEvent* event) override; virtual void showEvent(QShowEvent* event) override;
virtual bool eventFilter(QObject* object, QEvent* 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;
}; };

View File

@ -3,4 +3,8 @@
<file>fragmentshader.fs</file> <file>fragmentshader.fs</file>
<file>vertexshader.vs</file> <file>vertexshader.vs</file>
</qresource> </qresource>
<qresource prefix="/fonts">
<file>font_awesome_6_regular_free.otf</file>
<file>font_awesome_6_solid_free.otf</file>
</qresource>
</RCC> </RCC>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindowClass</class>
<widget class="QMainWindow" name="MainWindowClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="mainwindow.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -13,7 +13,13 @@ Model::~Model() {
// TODO: Maybe delete all meshes? // TODO: Maybe delete all meshes?
} }
// file path is ...\\...\\.obj, and processnode & processmesh have been called here Model::Model(std::vector<Mesh>&& meshes, std::vector<Texture>&& textures, std::string directory) {
_meshes = std::move(meshes);
_texturesLoaded = std::move(textures);
_directory = directory;
_status = LOADED;
}
void Model::loadModel(std::string path) { void Model::loadModel(std::string path) {
Logger::info("Loading model from path: " + path); Logger::info("Loading model from path: " + path);
Assimp::Importer importer; Assimp::Importer importer;
@ -34,7 +40,7 @@ void Model::loadModel(std::string path) {
processNode(scene->mRootNode, scene); processNode(scene->mRootNode, scene);
_status = LOADED; _status = LOADED;
Logger::info("Model loaded"); Logger::info("Model loaded");
// 仅检查一次即可 // 仅检查一次即可
//check_boundary(); //check_boundary();
} }
@ -67,7 +73,7 @@ Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene) {
glm::vec3 vertexBitangent = glm::vec3(0.0f); glm::vec3 vertexBitangent = glm::vec3(0.0f);
// Process vertex positions // Process vertex positions
//使用循环避免代码重复,如果可行的话,可以在此循环中确定法向量等信息 //使用循环避免代码重复,如果可行的话,可以在此循环中确定法向量等信息
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
vertexPosition[j] = mesh->mVertices[i][j]; vertexPosition[j] = mesh->mVertices[i][j];
_left_down_back[j] = _left_down_back[j] < vertexPosition[j] ? _left_down_back[j] : vertexPosition[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() {
} }
} }
} }
}
Model* Model::copyToCurrentContext() const {
// Reload all textures
std::vector<Texture> 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<Mesh> 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;
} }

View File

@ -28,8 +28,16 @@ public:
Model(std::string path); Model(std::string path);
~Model(); ~Model();
private:
Model(std::vector<Mesh>&& meshes, std::vector<Texture>&& textures, std::string directory);
public: public:
inline MODELSTATUS status() const { return _status; } 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 // maybe we can check if boundary has not been set yet

View File

@ -1 +1,83 @@
#pragma once #include <qlabel.h>
#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;
}

View File

@ -1,6 +1,46 @@
#pragma once #pragma once
#include "model.h"
#include <qwidget.h> #include <qwidget.h>
#include <qlayout.h>
#include <qlabel.h>
#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();
};

View File

@ -1 +1,83 @@
#pragma once #include <qfiledialog.h>
#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;
}

View File

@ -1 +1,40 @@
#pragma once #pragma once
#include <qwidget.h>
#include <qboxlayout.h>
#include <qlist.h>
#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<ModelSelectable*> _objectSelectables;
private:
// Slot functions
void addNewObject();
// Util functions
QString getNewObjectPath();
Model* loadObject(const QString& path);
void removeObject(ModelSelectable* selectable);
signals:
void onObjectSelected(Model* selectedModel);
};

View File

@ -1 +1,3 @@
#pragma once #pragma once
#include <qwidget.h>

View File

@ -0,0 +1,75 @@
#include <qfile.h>
#include <qdir.h>
#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);
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <QtOpenGLWidgets/qopenglwidget.h>
#include <qopenglfunctions.h>
#include <qevent.h>
#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;
};

View File

@ -0,0 +1,72 @@
#include <qpropertyanimation.h>
#include <qparallelanimationgroup.h>
#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();
});
}

39
FinalProject/pagewidget.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <qwidget.h>
#include <qgraphicseffect.h>
#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
};

811
FinalProject/pushbutton.cpp Normal file
View File

@ -0,0 +1,811 @@
#include <qanimationgroup.h>
#include <qparallelanimationgroup.h>
#include <qsequentialanimationgroup.h>
#include <qregularexpression.h>
#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;
}

102
FinalProject/pushbutton.h Normal file
View File

@ -0,0 +1,102 @@
#pragma once
#include <qwidget.h>
#include <qevent.h>
#include <qboxlayout.h>
#include <qgraphicseffect.h>
#include <qpropertyanimation.h>
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();
};

View File

@ -8,6 +8,10 @@ Renderable::Renderable(Model* model) : _model(model) {}
Renderable::Renderable(Model* model, glm::vec3 position) : _model(model), _position(position) {} Renderable::Renderable(Model* model, glm::vec3 position) : _model(model), _position(position) {}
void Renderable::setModel(Model* model) {
_model = model;
}
void Renderable::move(glm::vec3 deltaVec) { void Renderable::move(glm::vec3 deltaVec) {
_position += deltaVec; _position += deltaVec;
} }
@ -33,6 +37,11 @@ void Renderable::setScale(float scale) {
} }
void Renderable::render(ShaderProgram shader) { void Renderable::render(ShaderProgram shader) {
// Check if initialized
if (_model == nullptr) {
Logger::error("Tries to render an uninitialized renderable object");
return;
}
// Set model matrix // Set model matrix
shader.setUniform("model", modelMatrix()); shader.setUniform("model", modelMatrix());
// Render // Render

View File

@ -7,8 +7,16 @@
#include "shader.h" #include "shader.h"
class Renderable { class Renderable {
public:
static Renderable empty() {
return Renderable();
}
private: private:
Model* _model; Renderable() {}
private:
Model* _model = nullptr;
glm::vec3 _position = glm::vec3(0.0f); glm::vec3 _position = glm::vec3(0.0f);
glm::mat4 _rotation = glm::mat4(1.0f); glm::mat4 _rotation = glm::mat4(1.0f);
glm::vec3 _scale = glm::vec3(1.0f); glm::vec3 _scale = glm::vec3(1.0f);
@ -21,6 +29,7 @@ public:
Renderable(Model* model, glm::vec3 position); Renderable(Model* model, glm::vec3 position);
public: public:
void setModel(Model* model);
void move(glm::vec3 deltaVec); void move(glm::vec3 deltaVec);
void setPosition(glm::vec3 position); void setPosition(glm::vec3 position);
void rotate(glm::vec3 axis, float deltaAngle); void rotate(glm::vec3 axis, float deltaAngle);

View File

@ -0,0 +1,106 @@
#include <qpainterpath.h>
#include <qregularexpression.h>
#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;
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <qwidget.h>
#include <qevent.h>
#include <qboxlayout.h>
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;
};

View File

@ -17,6 +17,7 @@ using std::vector;
SceneViewer::SceneViewer(QWidget* parent) SceneViewer::SceneViewer(QWidget* parent)
: QOpenGLWidget(parent) : QOpenGLWidget(parent)
{ {
// OpenGL initialize
QSurfaceFormat format; QSurfaceFormat format;
format.setProfile(QSurfaceFormat::CoreProfile); format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(4, 3); format.setVersion(4, 3);
@ -65,14 +66,7 @@ void SceneViewer::initializeGL() {
vertexShader.dispose(); vertexShader.dispose();
fragmentShader.dispose(); fragmentShader.dispose();
Model* backpackModel = new Model("D:/code/ComputerGraphic/SceneEditor/obj/nanosuit/nanosuit.obj"); _camera.setPosition(glm::vec3(0.0f, 0.0f, 10.0f));
Logger::info("Model loaded");
Renderable backpack(backpackModel);
_objects.push_back(backpack);
_camera.setPosition(glm::vec3(0.0f, 0.0f, 5.0f));
} }
void SceneViewer::resizeGL(int w, int h) { void SceneViewer::resizeGL(int w, int h) {
@ -80,6 +74,8 @@ void SceneViewer::resizeGL(int w, int h) {
} }
void SceneViewer::paintGL() { void SceneViewer::paintGL() {
Logger::debug("Repainting");
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_shaderProgram.bind(); _shaderProgram.bind();
@ -108,7 +104,9 @@ void SceneViewer::mousePressEvent(QMouseEvent* event) {
} }
void SceneViewer::mouseMoveEvent(QMouseEvent* event) { void SceneViewer::mouseMoveEvent(QMouseEvent* event) {
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())); 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 // Check the type of button pressed
switch (event->buttons()) { switch (event->buttons()) {
case Qt::LeftButton: { case Qt::LeftButton: {
@ -154,7 +152,7 @@ void SceneViewer::mouseMoveEvent(QMouseEvent* event) {
// Update the last mouse position // Update the last mouse position
_lastMousePosition = event->pos(); _lastMousePosition = event->pos();
// Update the view // Update the view
update(); parentWidget()->update();
} }
void SceneViewer::wheelEvent(QWheelEvent* event) { 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 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)); Logger::debug("New center position: " + std::to_string(_rotateCenter.x) + ", " + std::to_string(_rotateCenter.y) + ", " + std::to_string(_rotateCenter.z));
// Update the view // 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());
} }

View File

@ -35,6 +35,10 @@ private:
QPoint _lastMousePosition; QPoint _lastMousePosition;
Renderable* _selectedObject = nullptr; Renderable* _selectedObject = nullptr;
// UI interface control
const int _cornerRadius = 10;
bool _initialized = false;
public: public:
SceneViewer(QWidget* parent = 0); SceneViewer(QWidget* parent = 0);
~SceneViewer(); ~SceneViewer();
@ -50,4 +54,7 @@ protected:
virtual void mouseMoveEvent(QMouseEvent* event) override; virtual void mouseMoveEvent(QMouseEvent* event) override;
virtual void wheelEvent(QWheelEvent* event) override; virtual void wheelEvent(QWheelEvent* event) override;
// UI update events
virtual void showEvent(QShowEvent* event) override;
virtual void resizeEvent(QResizeEvent* event) override;
}; };

View File

@ -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);
}

View File

@ -0,0 +1,142 @@
/* This Module has not yet been rewritten */
/* Behavior and performance is not guaranteed*/
/* @Linloir */
#pragma once
#include <qwidget.h>
#include <qboxlayout.h>
#include <qpainter.h>
#include <qpropertyanimation.h>
#include <qparallelanimationgroup.h>
#include <qsequentialanimationgroup.h>
#include <qgraphicseffect.h>
#include <qvector.h>
#include <qevent.h>
#include <qtimer.h>
#include <qmath.h>
#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<QWidget*> 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<QWidget*> widgets;
int size = 0;
QVector<int> 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();
};

286
FinalProject/sidebar.cpp Normal file
View File

@ -0,0 +1,286 @@
#pragma once
#include <qfontdatabase.h>
#include <qpropertyanimation.h>
#include <qparallelanimationgroup.h>
#include <qlayoutitem.h>
#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<PushButton*, PushButton*>(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<PushButton*, PushButton*>(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;
}
}
}

63
FinalProject/sidebar.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <qwidget.h>
#include <qlabel.h>
#include <qboxlayout.h>
#include <qcolor.h>
#include <qmap.h>
#include <qlist.h>
#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<PageWidget*, QPair<PushButton*, PushButton*>> _pageButtonMap;
QList<PageWidget*> _pageList;
QList<QPair<PushButton*, PushButton*>> _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);
};