mirror of
https://github.com/Linloir/SceneEditor.git
synced 2025-12-17 07:28:12 +08:00
374 lines
17 KiB
C++
374 lines
17 KiB
C++
#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();
|
|
}
|
|
}
|