From 135337f1aa303212ed423e4d9f48fe6b277fff3e Mon Sep 17 00:00:00 2001 From: Mauro Mascarenhas Date: Mon, 24 Aug 2020 21:49:35 -0300 Subject: [PATCH 1/4] Improved performance and expanded compatibility - NMainWindow has been renamed to QCustomWindow; - TitleBar has been renamed to QTitleBar; - QCustomWindow and QTitleBar no longer use designer files; - It is no longer necessary to call "setCustomWidgets", "setNewCentralWidget" or "setCustomStatusBar". Widgets additions are handled on "QCustomWindow::event(QEvent*)"; - QTitleBar can be accessed directly through "QCustomWindow::titleBar()"; - QToolBar and QDockWidget objects can be normally added/inserted/removed; - Faster API calls (every single event has been redesiged so as to provide smoother transformations); - QTitleBar properties must be set directly to QCustomWindow's titlebar object; - Minor fixes; ### LIMITATIONS Unfortunately, there are some limitations which cannot be overcome. Here are some of them: - It is not recommended to change QToobar's stylesheet after it has been inserted into a QCustomWindow's layout; - Under no circumstances "QMainWindow::setMenuBar" and "QMainWindow::setMenuWidgets" should be called, since those methods have been reimplemented in QCustomWindow. It means that they cannot be generated by Qt Designer. --- .../CustomFrame-Static/CustomFrameTestIn.pro | 13 +- .../customtitlebar/nmainwindow.cpp | 219 --------------- .../customtitlebar/nmainwindow.h | 78 ------ .../customtitlebar/nmainwindow.ui | 144 ---------- .../customtitlebar/qcustomwindow.cpp | 260 ++++++++++++++++++ .../customtitlebar/qcustomwindow.h | 68 +++++ .../customtitlebar/qtitlebar.cpp | 111 ++++++++ .../customtitlebar/qtitlebar.h | 73 +++++ .../customtitlebar/titlebar.cpp | 116 -------- .../customtitlebar/titlebar.h | 65 ----- .../customtitlebar/titlebar.ui | 148 ---------- sample/CustomFrame-Static/testwindow.cpp | 11 +- sample/CustomFrame-Static/testwindow.h | 4 +- sample/CustomFrame-Static/testwindow.ui | 5 +- src/CustomTitlebar-Dynamic/nmainwindow.cpp | 2 +- src/CustomTitlebar-Static/CustomTitlebar.pro | 12 +- src/CustomTitlebar-Static/main.cpp | 4 +- src/CustomTitlebar-Static/nmainwindow.cpp | 219 --------------- src/CustomTitlebar-Static/nmainwindow.h | 78 ------ src/CustomTitlebar-Static/nmainwindow.ui | 144 ---------- src/CustomTitlebar-Static/qcustomwindow.cpp | 260 ++++++++++++++++++ src/CustomTitlebar-Static/qcustomwindow.h | 68 +++++ src/CustomTitlebar-Static/qtitlebar.cpp | 111 ++++++++ src/CustomTitlebar-Static/qtitlebar.h | 73 +++++ src/CustomTitlebar-Static/titlebar.cpp | 116 -------- src/CustomTitlebar-Static/titlebar.h | 65 ----- src/CustomTitlebar-Static/titlebar.ui | 148 ---------- 27 files changed, 1049 insertions(+), 1566 deletions(-) delete mode 100644 sample/CustomFrame-Static/customtitlebar/nmainwindow.cpp delete mode 100644 sample/CustomFrame-Static/customtitlebar/nmainwindow.h delete mode 100644 sample/CustomFrame-Static/customtitlebar/nmainwindow.ui create mode 100644 sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp create mode 100644 sample/CustomFrame-Static/customtitlebar/qcustomwindow.h create mode 100644 sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp create mode 100644 sample/CustomFrame-Static/customtitlebar/qtitlebar.h delete mode 100644 sample/CustomFrame-Static/customtitlebar/titlebar.cpp delete mode 100644 sample/CustomFrame-Static/customtitlebar/titlebar.h delete mode 100644 sample/CustomFrame-Static/customtitlebar/titlebar.ui delete mode 100644 src/CustomTitlebar-Static/nmainwindow.cpp delete mode 100644 src/CustomTitlebar-Static/nmainwindow.h delete mode 100644 src/CustomTitlebar-Static/nmainwindow.ui create mode 100644 src/CustomTitlebar-Static/qcustomwindow.cpp create mode 100644 src/CustomTitlebar-Static/qcustomwindow.h create mode 100644 src/CustomTitlebar-Static/qtitlebar.cpp create mode 100644 src/CustomTitlebar-Static/qtitlebar.h delete mode 100644 src/CustomTitlebar-Static/titlebar.cpp delete mode 100644 src/CustomTitlebar-Static/titlebar.h delete mode 100644 src/CustomTitlebar-Static/titlebar.ui diff --git a/sample/CustomFrame-Static/CustomFrameTestIn.pro b/sample/CustomFrame-Static/CustomFrameTestIn.pro index fc4fd77..76a6640 100644 --- a/sample/CustomFrame-Static/CustomFrameTestIn.pro +++ b/sample/CustomFrame-Static/CustomFrameTestIn.pro @@ -36,17 +36,14 @@ INCLUDEPATH += $$PWD/customtitlebar DEPENDPATH += $$PWD/customtitlebar SOURCES += \ - customtitlebar/nmainwindow.cpp \ - customtitlebar/titlebar.cpp \ + customtitlebar/qcustomwindow.cpp \ + customtitlebar/qtitlebar.cpp \ testwindow.cpp \ main.cpp HEADERS += \ - customtitlebar/nmainwindow.h \ - customtitlebar/titlebar.h \ + customtitlebar/qcustomwindow.h \ + customtitlebar/qtitlebar.h \ testwindow.h -FORMS += \ - customtitlebar/nmainwindow.ui \ - customtitlebar/titlebar.ui \ - testwindow.ui +FORMS += testwindow.ui diff --git a/sample/CustomFrame-Static/customtitlebar/nmainwindow.cpp b/sample/CustomFrame-Static/customtitlebar/nmainwindow.cpp deleted file mode 100644 index 68ac9dd..0000000 --- a/sample/CustomFrame-Static/customtitlebar/nmainwindow.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#include "nmainwindow.h" -#include "ui_nmainwindow.h" - -NMainWindow::NMainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::NMainWindow), - RESIZE_LIMIT(2) -{ - ui->setupUi(this); - setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); - centralWidget()->installEventFilter(this); - ui->titleBar->installEventFilter(this); - ui->statusBar->installEventFilter(this); - - centralWidget()->setMouseTracking(true); - ui->titleBar->setMouseTracking(true); - ui->statusBar->setMouseTracking(true); - - setWindowTitle("Custom Window Border"); - locked = LockMoveType::None; - - ui->headerWidget->setTitleBarWidget(ui->titleBar); - if (ui->titleBar->mainWindow() != this) - ui->titleBar->setMainWindow(this); - - if (this->maximumSize() == this->minimumSize()) - setMaximizeButtonEnabled(false); - - connect(ui->titleBar, &TitleBar::closeRequest, this, &NMainWindow::close); -} - -NMainWindow::~NMainWindow() -{ - delete ui; -} - -void NMainWindow::setCustomWidgets(QWidget *newCentralWidget, QStatusBar *newStatusBar){ - setCustomStatusBar(newStatusBar); - setNewCentralWidget(newCentralWidget); -} - -void NMainWindow::setCustomStatusBar(QStatusBar *newStatusBar){ - if (!newStatusBar) return; - newStatusBar->installEventFilter(this); - newStatusBar->setMouseTracking(true); -} - -void NMainWindow::setNewCentralWidget(QWidget *newCentralWidget){ - if (!newCentralWidget) return; - newCentralWidget->installEventFilter(this); - newCentralWidget->setMouseTracking(true); - - if (newCentralWidget->layout()) - newCentralWidget->layout()->setContentsMargins(10, 0, 10, 0); -} - -void NMainWindow::setCloseButtonEnabled(bool enable){ - ui->titleBar->setCloseButtonEnabled(enable); -} - -void NMainWindow::setMaximizeButtonEnabled(bool enable){ - ui->titleBar->setMaximizeButtonEnabled(enable); -} - -void NMainWindow::setMinimizeButtonEnabled(bool enable){ - ui->titleBar->setMinimizeButtonEnabled(enable); -} - -void NMainWindow::setTitlebarStylesheet(const QString &styleSheet){ - ui->titleBar->setStyleSheet(styleSheet); -} - -QString NMainWindow::titlebarStylesheet() const { - return ui->titleBar->styleSheet(); -} - -void NMainWindow::closeEvent(QCloseEvent *event){ - if (event->isAccepted()) QMainWindow::closeEvent(event); - else event->ignore(); -} - -/* - * GUI Protected Methods (do not change them, unless it's really necessary) - */ - -void NMainWindow::mousePressEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - int x = event->x(), y = event->y(), bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; - if (x < RESIZE_LIMIT && y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topLeft(); - locked = LockMoveType::TopLeft; - } - else if (x < RESIZE_LIMIT && y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomLeft(); - locked = LockMoveType::BottomLeft; - } - else if (x > right && y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topRight(); - locked = LockMoveType::TopRight; - } - else if (x > right && y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomRight(); - locked = LockMoveType::BottomRight; - } - else if (x < RESIZE_LIMIT || y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topLeft(); - locked = x < RESIZE_LIMIT ? LockMoveType::Left : LockMoveType::Top; - } - else if (x > right || y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomRight(); - locked = x > right ? LockMoveType::Right : LockMoveType::Bottom; - } - event->accept(); - } -} - -void NMainWindow::undefMouseMoveEvent(QObject* object, QMouseEvent* event){ - if (locked != LockMoveType::None){ - switch (locked) { - case LockMoveType::TopLeft: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), event->globalPos().y() - posCursor.y()), - this->geometry().bottomRight())); - break; - case LockMoveType::TopRight: - this->setGeometry(QRect(QPoint(this->geometry().left(), event->globalPos().y() - posCursor.y()), - QPoint(event->globalPos().x() - posCursor.x(), this->geometry().bottom()))); - break; - case LockMoveType::BottomLeft: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), this->geometry().top()), - QPoint(this->geometry().right(), event->globalPos().y() - posCursor.y()))); - break; - case LockMoveType::BottomRight: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(event->globalPos().x() - posCursor.x(), event->globalPos().y() - posCursor.y()))); - break; - case LockMoveType::Left: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), this->geometry().top()), - this->geometry().bottomRight())); - break; - case LockMoveType::Right: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(event->globalPos().x() - posCursor.x(), this->geometry().bottom()))); - break; - case LockMoveType::Top: - this->setGeometry(QRect(QPoint(this->geometry().left(), event->globalPos().y() - posCursor.y()), - this->geometry().bottomRight())); - break; - default: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(this->geometry().right(), event->globalPos().y() - posCursor.y()))); - break; - } - return; - } - - int x = event->x(), y = event->y(), right = this->width() - RESIZE_LIMIT; - if (object->objectName() == "statusBar"){ - if (x < RESIZE_LIMIT && y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeBDiagCursor)); - return; - } - else if (x > right && y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeFDiagCursor)); - return; - } - else if (y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeVerCursor)); - return; - } - } - else if (object->objectName() == "titleBar"){ - if (x < RESIZE_LIMIT && y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeFDiagCursor)); - return; - } - if (x > right && y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeBDiagCursor)); - return; - } - else if (y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeVerCursor)); - return; - } - } - - this->setCursor(QCursor(x < RESIZE_LIMIT || x > right ? Qt::SizeHorCursor : Qt::ArrowCursor)); -} - -void NMainWindow::mouseReleaseEvent(QMouseEvent *event){ - locked = LockMoveType::None; - event->accept(); -} - -bool NMainWindow::eventFilter(QObject* object, QEvent* event) -{ - if(event->type() == QEvent::MouseMove) - undefMouseMoveEvent(object, static_cast(event)); - else if (event->type() == QEvent::MouseButtonPress && object->objectName() == "titleBar") - mousePressEvent(static_cast(event)); - return false; -} diff --git a/sample/CustomFrame-Static/customtitlebar/nmainwindow.h b/sample/CustomFrame-Static/customtitlebar/nmainwindow.h deleted file mode 100644 index 6e06456..0000000 --- a/sample/CustomFrame-Static/customtitlebar/nmainwindow.h +++ /dev/null @@ -1,78 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#ifndef NMAINWINDOW_H -#define NMAINWINDOW_H - -#include -#include -#include -#include - -#include "titlebar.h" - -namespace Ui { -class NMainWindow; -} - -class NMainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit NMainWindow(QWidget *parent = nullptr); - ~NMainWindow(); - - void setCustomWidgets(QWidget *newCentralWidget, QStatusBar *newStatusBar); - void setNewCentralWidget(QWidget *newCentralWidget); - void setCustomStatusBar(QStatusBar *newStatusBar); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - - void setTitlebarStylesheet(const QString &styleSheet); - QString titlebarStylesheet() const; - -protected: - void closeEvent(QCloseEvent *event); - - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void undefMouseMoveEvent(QObject *object, QMouseEvent* event); - bool eventFilter(QObject *watched, QEvent *event); - - enum LockMoveType{ - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight, - None - }; - -private: - Ui::NMainWindow *ui; - const int RESIZE_LIMIT; - - QPoint posCursor; - LockMoveType locked; -}; - -#endif // NMAINWINDOW_H diff --git a/sample/CustomFrame-Static/customtitlebar/nmainwindow.ui b/sample/CustomFrame-Static/customtitlebar/nmainwindow.ui deleted file mode 100644 index bdc7d03..0000000 --- a/sample/CustomFrame-Static/customtitlebar/nmainwindow.ui +++ /dev/null @@ -1,144 +0,0 @@ - - - NMainWindow - - - - 0 - 0 - 400 - 300 - - - - ArrowCursor - - - NMainWindow - - - QWidget#titleBar{ - background-color: #FFF; - border-top: 1px solid orange; - border-left: 1px solid orange; - border-right: 1px solid orange; - color: #000; -} - -QDockWidget#headerWidget, QDockWidget#headerWidget::title{ - background-color: #FFF; - border-top: 1px solid orange; - border-left: 1px solid orange; - border-right: 1px solid orange; - color: #000; -} - -QWidget#centralWidget, QWidget#headerWidgetContents, QStatusBar{ - background-color: #FFF; - border-left: 1px solid orange; - border-right: 1px solid orange; -} - -QMainWindow::separator { - background-color: #FFF; - background: #FFF; - border-left: 1px solid orange; - border-right: 1px solid orange; - height: 0px; -} - -QStatusBar{ - border-bottom: 1px solid orange; -} - - - - - 10 - - - 0 - - - 10 - - - 0 - - - 0 - - - - - - - - 36 - 35 - - - - - 524287 - 35 - - - - QDockWidget::NoDockWidgetFeatures - - - Qt::TopDockWidgetArea - - - 4 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 35 - - - - - 16777215 - 35 - - - - - - - - - - - - TitleBar - QWidget -
titlebar.h
- 1 -
-
- - -
diff --git a/sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp new file mode 100644 index 0000000..5447b1a --- /dev/null +++ b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp @@ -0,0 +1,260 @@ +#include "qcustomwindow.h" + +QCustomWindow::QCustomWindow(QWidget *parent) : + QMainWindow(parent), RESIZE_LIMIT(4) +{ + setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); + + this->setMinimumSize(QSize(256, 64)); + this->setStyleSheet(QStringLiteral( + "\nQCustomWindow {\n" + " background : white;\n" + " border : 1px solid orange;\n" + "}\n" + "QStatusBar {\n" + " background : white;\n" + " border-bottom: 1px solid orange;\n" + " border-left: 1px solid orange;\n" + " border-right: 1px solid orange;\n" + "}\n" + "QToolBar {\n" + " margin : 1px;\n" + "}" + )); + + this->m_titleBar = new QTitleBar(this); + connect(this->m_titleBar, &QTitleBar::requestClose, + this, &QCustomWindow::close); + connect(this->m_titleBar, &QTitleBar::requestMaximize, [this]{ + if (this->isMaximized()) this->showNormal(); + else this->showMaximized(); + }); + connect(this->m_titleBar, &QTitleBar::requestMinimize, + this, &QCustomWindow::showMinimized); + connect(this, &QMainWindow::windowTitleChanged, this->m_titleBar, + &QWidget::setWindowTitle); + + this->m_titleBarW = new QWidget(); + this->m_titleBarW->setMouseTracking(true); + this->m_titleBarW->installEventFilter(this); + + QVBoxLayout *titleLayout = new QVBoxLayout(); + titleLayout->setContentsMargins(6, 6, 6, 0); + titleLayout->setSpacing(0); + titleLayout->addWidget(this->m_titleBar); + + this->m_titleBarW->setLayout(titleLayout); + QMainWindow::setMenuWidget(this->m_titleBarW); + + this->m_leftBorder = generateBorder(Qt::LeftToolBarArea, Qt::Vertical); + this->m_rightBorder = generateBorder(Qt::RightToolBarArea, Qt::Vertical); + this->m_bottomBorder = generateBorder(Qt::BottomToolBarArea, Qt::Horizontal); + + this->m_menuBar = nullptr; + this->m_menuWidget = nullptr; +} + +QCustomWindow::~QCustomWindow(){ + delete this->m_titleBar; + delete this->m_leftBorder; + delete this->m_rightBorder; + delete this->m_bottomBorder; + + if (this->m_menuBar) delete this->m_menuBar; + else if (this->m_menuWidget) delete this->m_menuWidget; +} + +QToolBar *QCustomWindow::generateBorder(Qt::ToolBarArea area, + Qt::Orientation orientation){ + QToolBar *border = new QToolBar("___border___"); + border->setStyleSheet( + "\nQToolBar {\n" + " margin : 1px;\n" + " border : 0px;\n" + " background : transparent;\n" + "}" + ); + + if (orientation & Qt::Horizontal){ + border->setMinimumHeight(6); + border->setMaximumHeight(6); + } + else { + border->setMinimumWidth(6); + border->setMaximumWidth(6); + } + border->setMovable(false); + border->setFloatable(false); + border->setAllowedAreas(area); + border->setMouseTracking(true); + border->installEventFilter(this); + + this->addToolBar(area, border); + return border; +} + +QMenu * QCustomWindow::createPopupMenu(){ + QMenu *menu = QMainWindow::createPopupMenu(); + QList removal; + foreach (QAction *a, menu->actions()) + if (a->text() == "___border___") removal.append(a); + foreach (QAction *a, removal) menu->removeAction(a); + return menu; +} + +void QCustomWindow::setMenuBar(QMenuBar *menuBar){ + if (this->m_menuBar == menuBar) return; + + if (this->m_menuBar) { + if (this->m_menuBar != this->m_menuWidget && this->m_menuWidget){ + this->m_menuWidget->setParent(nullptr); + this->m_menuWidget->deleteLater(); + } + + this->m_menuBar->hide(); + this->m_menuBar->setParent(nullptr); + this->m_menuBar->deleteLater(); + } + + this->m_menuBar = menuBar; + this->m_menuWidget = qobject_cast(menuBar); + + if (menuBar){ + menuBar->setParent(this); + this->m_titleBarW->layout()->addWidget(menuBar); + } +} + +QMenuBar* QCustomWindow::menuBar() const{ + return this->m_menuBar; +} + +void QCustomWindow::setMenuWidget(QWidget *widget){ + if (this->m_menuWidget == widget) return; + + widget->setParent(this); + + if (this->m_menuWidget){ + this->m_menuWidget->hide(); + this->m_menuWidget->setParent(nullptr); + this->m_menuWidget->deleteLater(); + } + + this->m_menuBar = nullptr; + this->m_menuWidget = widget; + + if (this->m_menuWidget){ + this->m_menuWidget->setParent(this); + this->m_titleBarW->layout()->addWidget(widget); + } +} + +QWidget * QCustomWindow::menuWidget() const{ + return this->m_menuWidget; +} + +bool QCustomWindow::eventFilter(QObject*, QEvent *event){ + if (event->type() == QEvent::MouseMove) + customMouseMoveEvent(static_cast(event)); + else if (event->type() == QEvent::MouseButtonPress) + mousePressEvent(static_cast(event)); + return false; +} + +bool QCustomWindow::event(QEvent *event){ + if (event->type() == QEvent::ChildRemoved){ + QChildEvent *evt = static_cast(event); + if (evt->child()->isWidgetType()) evt->child()->removeEventFilter(this); + } + else if (event->type() == QEvent::ChildAdded){ + QChildEvent *evt = static_cast(event); + if (evt->child()->isWidgetType()){ + QWidget *child = qobject_cast(evt->child()); + + child->setMouseTracking(true); + child->installEventFilter(this); + + if (child->metaObject()->indexOfClassInfo("custom_obj_type") == -1){ + child->setStyleSheet(child->styleSheet() + + "\nQToolBar {\n" + " margin : 1px;\n" + " padding: 0px 6px 0px 6px;\n" + " border: 1px transparent solid;" + "}\n" + "QToolBar:top:first, QToolBar:bottom:first, QToolBar:left:first {\n" + " margin : 0px 0px 0px 6px;\n" + "}\n" + "QToolBar:top:only-one, QToolBar:bottom:only-one {\n" + " margin : 0px 6px 0px 6px;\n" + "}\n" + "QToolBar:top:last, QToolBar:bottom:last, QToolBar:right:last {\n" + " margin : 0px 6px 0px 0px;\n" + "}\n"); + } + } + } + return QMainWindow::event(event); +} + +void QCustomWindow::mousePressEvent(QMouseEvent *event){ + if (event->button() & Qt::LeftButton){ + int x = event->x(), y = event->y(); + int bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; + + QPoint posCursor = event->globalPos(); + Qt::Edges nFlags = {}; + if (x < RESIZE_LIMIT) { + nFlags |= Qt::LeftEdge; + posCursor.rx() -= this->x(); + } + if (y < RESIZE_LIMIT) { + nFlags |= Qt::TopEdge; + posCursor.ry() -= this->y(); + } + if (x > right) { + nFlags |= Qt::RightEdge; + posCursor.rx() -= (this->x() + this->width()); + } + if (y > bottom) { + nFlags |= Qt::BottomEdge; + posCursor.ry() -= (this->y() + this->height()); + } + this->m_lock = nFlags; + this->m_posCursor = posCursor; + } + QMainWindow::mousePressEvent(event); +} + +void QCustomWindow::mouseReleaseEvent(QMouseEvent *event){ + this->m_lock = {}; + this->unsetCursor(); + QMainWindow::mouseMoveEvent(event); +} + +void QCustomWindow::customMouseMoveEvent(QMouseEvent *event){ + if (this->m_lock){ + QPoint tL = this->geometry().topLeft(), bR = this->geometry().bottomRight(); + if (this->m_lock & Qt::TopEdge) tL.ry() = event->globalY() - this->m_posCursor.y(); + if (this->m_lock & Qt::BottomEdge) bR.ry() = event->globalY() - this->m_posCursor.y(); + if (this->m_lock & Qt::LeftEdge) tL.rx() = event->globalX() - this->m_posCursor.x(); + if (this->m_lock & Qt::RightEdge) bR.rx() = event->globalX() - this->m_posCursor.x(); + this->setGeometry(QRect(tL, bR)); + return; + } + + int x = event->globalX() - this->x(), y = event->globalY() - this->y(); + int bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; + + if (x < RESIZE_LIMIT){ + if (y < RESIZE_LIMIT) this->setCursor(QCursor(Qt::SizeFDiagCursor)); + else if (y > bottom) this->setCursor(QCursor(Qt::SizeBDiagCursor)); + else this->setCursor(QCursor(Qt::SizeHorCursor)); + } + else if (x > right){ + if (y < RESIZE_LIMIT) this->setCursor(QCursor(Qt::SizeBDiagCursor)); + else if (y > bottom) this->setCursor(QCursor(Qt::SizeFDiagCursor)); + else this->setCursor(QCursor(Qt::SizeHorCursor)); + } + else if (y < RESIZE_LIMIT || y > bottom) this->setCursor(Qt::SizeVerCursor); + else this->unsetCursor(); +} diff --git a/sample/CustomFrame-Static/customtitlebar/qcustomwindow.h b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.h new file mode 100644 index 0000000..20015d2 --- /dev/null +++ b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.h @@ -0,0 +1,68 @@ +#ifndef QCUSTOMWINDOW_H +#define QCUSTOMWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qtitlebar.h" + +class QCustomWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit QCustomWindow(QWidget *parent = nullptr); + ~QCustomWindow() override; + + QMenu * createPopupMenu() override; + + void setMenuBar(QMenuBar *menuBar); + QMenuBar * menuBar() const; + + void setMenuWidget(QWidget *widget); + QWidget * menuWidget() const; + + inline QTitleBar& titleBar() const { return *this->m_titleBar; } + +protected: + const int RESIZE_LIMIT; + + bool event(QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + + void customMouseMoveEvent(QMouseEvent* event); + +private: + bool init; + + QWidget *m_titleBarW; + QWidget *m_menuWidget; + QMenuBar *m_menuBar; + QTitleBar *m_titleBar; + + QToolBar *m_leftBorder; + QToolBar *m_rightBorder; + QToolBar *m_bottomBorder; + + Qt::Edges m_lock; + QPoint m_posCursor; + + QToolBar * generateBorder(Qt::ToolBarArea area, Qt::Orientation orientation); +}; + +#endif // QCUSTOMWINDOW_H diff --git a/sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp b/sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp new file mode 100644 index 0000000..6b364c0 --- /dev/null +++ b/sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp @@ -0,0 +1,111 @@ +#include "qtitlebar.h" + +QTitleBar::QTitleBar(QMainWindow *parent) : + QWidget(parent), FRAME_BUTTON_SIZE(24, 24) +{ + this->canMove = false; + this->maximizing = false; + this->m_frameButtons = QCustomAttrs::All; + + this->setStyleSheet(QStringLiteral( + "QPushButton {\n" + " border : 0.5px solid gray;\n" + " border-radius: 3px;\n" + " min-height: 20px;\n" + " margin: 1px;\n" + " background: white;\n" + " color: gray;\n" + "}\n" + "QPushButton::hover {\n" + " background: darkorange;\n" + " border : 0.5px solid darkorange;\n" + " color: white;\n" + "}\n" + "QPushButton::pressed, QPushButton::!enabled {\n" + " background: orange;\n" + " border : 0.5px solid orange;\n" + " color: white;\n" + "}\n" + "QTitleBar { background: white; }\n" + )); + + if (!parent) throw std::exception("Parent must be a QCustomWindow object (cannot be null)."); + this->m_parentWindow = parent; + + this->lbl_windowTitle.setText("QCustomWindow"); + this->lbl_windowTitle.setAlignment(Qt::AlignCenter); + + this->btn_close.setText("X"); + this->btn_maximize.setText("+"); + this->btn_minimize.setText("-"); + + this->btn_close.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_close.setMinimumSize(FRAME_BUTTON_SIZE); + this->btn_maximize.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_maximize.setMinimumSize(FRAME_BUTTON_SIZE); + this->btn_minimize.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_minimize.setMinimumSize(FRAME_BUTTON_SIZE); + + this->m_layout.addWidget(&this->lbl_windowTitle, 1); + this->m_layout.addWidget(&this->btn_minimize); + this->m_layout.addWidget(&this->btn_maximize); + this->m_layout.addWidget(&this->btn_close); + this->m_layout.setContentsMargins(0, 0, 0, 4); + this->m_layout.setSpacing(0); + + this->setLayout(&this->m_layout); + + connect(&this->btn_close, &QPushButton::clicked, [this]{ emit this->requestClose(); }); + connect(&this->btn_minimize, &QPushButton::clicked, [this]{ emit this->requestMinimize(); }); + connect(&this->btn_maximize, &QPushButton::clicked, [this]{ emit this->requestMaximize(); }); + + connect(this, &QWidget::windowTitleChanged, &this->lbl_windowTitle, &QLabel::setText); + + this->setMaximumHeight(35); + this->setMinimumHeight(35); +} + +void QTitleBar::setWindowButtons(QCustomAttrs::WindowButtons btns){ + this->m_frameButtons = btns; + this->btn_close.setVisible(btns & QCustomAttrs::Close); + this->btn_maximize.setVisible(btns & QCustomAttrs::Maximize); + this->btn_minimize.setVisible(btns & QCustomAttrs::Minimize); +} + +void QTitleBar::setWindowButtonEnabled(QCustomAttrs::WindowButton btn, bool enabled){ + if (btn & QCustomAttrs::Close) this->btn_close.setEnabled(enabled); + if (btn & QCustomAttrs::Maximize) this->btn_maximize.setEnabled(enabled); + if (btn & QCustomAttrs::Minimize) this->btn_minimize.setEnabled(enabled); +} + +void QTitleBar::paintEvent(QPaintEvent *event){ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} + +void QTitleBar::mousePressEvent(QMouseEvent *event){ + if (event->button() & Qt::LeftButton){ + this->canMove = (event->x() > 5 && event->y() > 5 && event->x() < (this->m_parentWindow->width() - 5)); + this->m_pCursor = event->globalPos() - this->m_parentWindow->geometry().topLeft(); + } + QWidget::mousePressEvent(event); +} + +void QTitleBar::mouseMoveEvent(QMouseEvent *event){ + if (!this->maximizing && canMove && event->buttons() & Qt::LeftButton + && !this->m_parentWindow->isMaximized()) this->m_parentWindow->move(event->globalPos() - m_pCursor); + this->maximizing = false; + QWidget::mouseMoveEvent(event); +} + +void QTitleBar::mouseDoubleClickEvent(QMouseEvent *event){ + if (m_frameButtons & QCustomAttrs::Maximize && btn_maximize.isEnabled() + && event->buttons() & Qt::LeftButton) { + this->maximizing = true; + emit requestMaximize(); + } + QWidget::mouseDoubleClickEvent(event); +} diff --git a/sample/CustomFrame-Static/customtitlebar/qtitlebar.h b/sample/CustomFrame-Static/customtitlebar/qtitlebar.h new file mode 100644 index 0000000..e0b83c9 --- /dev/null +++ b/sample/CustomFrame-Static/customtitlebar/qtitlebar.h @@ -0,0 +1,73 @@ +#ifndef QTITLEBAR_H +#define QTITLEBAR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QCustomAttrs { + enum WindowButton { + Minimize = 0x01, + Maximize = 0x02, + Close = 0x04, + All = Minimize | Maximize | Close + }; + + Q_DECLARE_FLAGS(WindowButtons, WindowButton) + Q_DECLARE_OPERATORS_FOR_FLAGS(WindowButtons) +} + +class QTitleBar : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QCustomAttrs::WindowButtons windowButtons READ windowButtons WRITE setWindowButtons) + Q_CLASSINFO("custom_obj_type", "QTitleBar") +public: + explicit QTitleBar(QMainWindow *parent = nullptr); + + void setWindowButtons(QCustomAttrs::WindowButtons btns); + inline QCustomAttrs::WindowButtons windowButtons() const { return this->m_frameButtons; } + + void setWindowButtonEnabled(QCustomAttrs::WindowButton btn, bool enabled = true); + +protected: + void paintEvent(QPaintEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + +private: + bool canMove; + bool maximizing; + + QPoint m_pCursor; + const QSize FRAME_BUTTON_SIZE; + + QWidget *m_parentWindow; + + QCustomAttrs::WindowButtons m_frameButtons; + + QLabel lbl_windowTitle; + QHBoxLayout m_layout; + QPushButton btn_minimize; + QPushButton btn_maximize; + QPushButton btn_close; + +signals: + void requestClose(); + void requestMaximize(); + void requestMinimize(); +}; + +#endif // QTITLEBAR_H diff --git a/sample/CustomFrame-Static/customtitlebar/titlebar.cpp b/sample/CustomFrame-Static/customtitlebar/titlebar.cpp deleted file mode 100644 index 831009a..0000000 --- a/sample/CustomFrame-Static/customtitlebar/titlebar.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#include "titlebar.h" -#include "ui_titlebar.h" - -TitleBar::TitleBar(QWidget *parent) : - QWidget(parent), - ui(new Ui::TitleBar) -{ - ui->setupUi(this); - setMainWindow(parent->parentWidget() ? parent->parentWidget() : parent); - - canMove = false; -} - -TitleBar::~TitleBar() -{ - delete ui; -} - -void TitleBar::setMainWindow(QWidget *mainWindow){ - this->parent = mainWindow; - - disconnect(ui->btClose, &QPushButton::clicked, this, &TitleBar::onCloseRequest); - disconnect(ui->btMinimize, &QPushButton::clicked, this->parent, &QWidget::showMinimized); - disconnect(ui->btMaximize, &QPushButton::clicked, this, &TitleBar::maximizeParent); - - disconnect(this->parent, &QWidget::windowTitleChanged, ui->lblFormTitle, &QLabel::setText); - - connect(ui->btClose, &QPushButton::clicked, this, &TitleBar::onCloseRequest); - connect(ui->btMinimize, &QPushButton::clicked, this->parent, &QWidget::showMinimized); - connect(ui->btMaximize, &QPushButton::clicked, this, &TitleBar::maximizeParent); - - connect(this->parent, &QWidget::windowTitleChanged, ui->lblFormTitle, &QLabel::setText); -} - -void TitleBar::maximizeParent(){ - if (parent->isMaximized()) parent->showNormal(); - else parent->showMaximized(); -} - -void TitleBar::onCloseRequest(){ - emit closeRequest(); -} - -QWidget* TitleBar::mainWindow(){ - return this->parent; -} - -void TitleBar::setCloseButtonEnabled(bool enable){ - ui->btClose->setEnabled(enable); -} - -void TitleBar::setMaximizeButtonEnabled(bool enable){ - ui->btMaximize->setEnabled(enable); -} - -void TitleBar::setMinimizeButtonEnabled(bool enable){ - ui->btMinimize->setEnabled(enable); -} - -void TitleBar::mousePressEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - canMove = !(event->x() < 5 || event->x() > (parent->width() - 5) || event->y() < 5); - m_pCursor = event->globalPos() - parent->geometry().topLeft(); - event->accept(); - } -} - -void TitleBar::mouseMoveEvent(QMouseEvent *event) -{ - if (!canMove){ - event->accept(); - return; - } - if (event->buttons() & Qt::LeftButton){ - if (parent->isMaximized()){ - event->accept(); - return; - } - parent->move(event->globalPos() - m_pCursor); - event->accept(); - } -} - -void TitleBar::mouseDoubleClickEvent(QMouseEvent *event){ - if (!ui->btMaximize->isEnabled()){ - event->accept(); - return; - } - if (event->button() == Qt::LeftButton) maximizeParent(); -} - -void TitleBar::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} diff --git a/sample/CustomFrame-Static/customtitlebar/titlebar.h b/sample/CustomFrame-Static/customtitlebar/titlebar.h deleted file mode 100644 index 2c170d4..0000000 --- a/sample/CustomFrame-Static/customtitlebar/titlebar.h +++ /dev/null @@ -1,65 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#ifndef TITLEBAR_H -#define TITLEBAR_H - -#include -#include -#include -#include - -namespace Ui { -class TitleBar; -} - -class TitleBar : public QWidget -{ - Q_OBJECT - -public: - explicit TitleBar(QWidget *parent = nullptr); - ~TitleBar(); - - void setMainWindow(QWidget *mainWindow); - QWidget* mainWindow(); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - -private: - bool canMove; - - Ui::TitleBar *ui; - QWidget *parent; - QPoint m_pCursor; - -protected: - void paintEvent(QPaintEvent *); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseDoubleClickEvent(QMouseEvent *event); - -protected slots: - void maximizeParent(); - void onCloseRequest(); - -signals: - void closeRequest(); -}; - -#endif // TITLEBAR_H diff --git a/sample/CustomFrame-Static/customtitlebar/titlebar.ui b/sample/CustomFrame-Static/customtitlebar/titlebar.ui deleted file mode 100644 index c397b65..0000000 --- a/sample/CustomFrame-Static/customtitlebar/titlebar.ui +++ /dev/null @@ -1,148 +0,0 @@ - - - TitleBar - - - - 0 - 0 - 721 - 35 - - - - - 0 - 35 - - - - - 16777215 - 35 - - - - Form - - - QPushButton{ - border : 0.5px solid gray; - border-radius: 3px; - min-height: 20px; - margin: 1px; - background: white; - color: gray; -} - -QPushButton::hover{ - background: darkorange; - border : 0.5px solid darkorange; - color: white; -} - -QPushButton::pressed, QPushButton::!enabled{ - background: orange; - border : 0.5px solid orange; - color: white; -} - -QWidget::lblFormTitle{ - background-color: white; -} - -QPushButton#lblFormTitle{ - background: transparent; - border-radius: 0px; - border: transparent; - color: black; -} - - - - 0 - - - 6 - - - 6 - - - 6 - - - 0 - - - - - frmTitle - - - Qt::AlignCenter - - - - - - - - 24 - 24 - - - - - 25 - 16777215 - - - - _ - - - - - - - - 24 - 24 - - - - - 25 - 16777215 - - - - + - - - - - - - - 24 - 24 - - - - - 30 - 16777215 - - - - x - - - - - - - - diff --git a/sample/CustomFrame-Static/testwindow.cpp b/sample/CustomFrame-Static/testwindow.cpp index 9493aa2..d25cd0e 100644 --- a/sample/CustomFrame-Static/testwindow.cpp +++ b/sample/CustomFrame-Static/testwindow.cpp @@ -18,19 +18,22 @@ #include "ui_testwindow.h" TestWindow::TestWindow(QWidget *parent) : - NMainWindow(parent), + QCustomWindow(parent), ui(new Ui::TestWindow) { ui->setupUi(this); - // Sets the custom Widgets on the parent Class - // Otherwise, the window resizing feature will not work - NMainWindow::setCustomWidgets(ui->centralWidget, ui->statusBar); + /* + * QMenuBar should never be inserted using Qt Designer. + * Insert them manually instead setMenuBar(QMenuBar*) + * and setMenuWidget(QWidget*) have been reimplemented. + */ clicks = 0; connect(ui->pushButton, &QPushButton::clicked, [this]{ setWindowTitle("Number of clicks : " + QString::number(++clicks)); }); + this->setMinimumSize(500, 400); } TestWindow::~TestWindow() diff --git a/sample/CustomFrame-Static/testwindow.h b/sample/CustomFrame-Static/testwindow.h index 40c7d03..afe47f3 100644 --- a/sample/CustomFrame-Static/testwindow.h +++ b/sample/CustomFrame-Static/testwindow.h @@ -19,13 +19,13 @@ #include -#include "nmainwindow.h" +#include "qcustomwindow.h" namespace Ui { class TestWindow; } -class TestWindow : public NMainWindow +class TestWindow : public QCustomWindow { Q_OBJECT diff --git a/sample/CustomFrame-Static/testwindow.ui b/sample/CustomFrame-Static/testwindow.ui index 430ef59..cad0629 100644 --- a/sample/CustomFrame-Static/testwindow.ui +++ b/sample/CustomFrame-Static/testwindow.ui @@ -6,7 +6,7 @@ 0 0 - 358 + 368 200 @@ -24,6 +24,9 @@ + + + Click me diff --git a/src/CustomTitlebar-Dynamic/nmainwindow.cpp b/src/CustomTitlebar-Dynamic/nmainwindow.cpp index 68ac9dd..1eb3714 100644 --- a/src/CustomTitlebar-Dynamic/nmainwindow.cpp +++ b/src/CustomTitlebar-Dynamic/nmainwindow.cpp @@ -102,7 +102,7 @@ void NMainWindow::closeEvent(QCloseEvent *event){ void NMainWindow::mousePressEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) - { + { int x = event->x(), y = event->y(), bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; if (x < RESIZE_LIMIT && y < RESIZE_LIMIT){ posCursor = event->globalPos() - this->geometry().topLeft(); diff --git a/src/CustomTitlebar-Static/CustomTitlebar.pro b/src/CustomTitlebar-Static/CustomTitlebar.pro index 64c07eb..5e40d43 100644 --- a/src/CustomTitlebar-Static/CustomTitlebar.pro +++ b/src/CustomTitlebar-Static/CustomTitlebar.pro @@ -35,13 +35,9 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ - titlebar.cpp \ - nmainwindow.cpp + qcustomwindow.cpp \ + qtitlebar.cpp HEADERS += \ - titlebar.h \ - nmainwindow.h - -FORMS += \ - titlebar.ui \ - nmainwindow.ui + qcustomwindow.h \ + qtitlebar.h diff --git a/src/CustomTitlebar-Static/main.cpp b/src/CustomTitlebar-Static/main.cpp index 8b6848e..7cb0ebe 100644 --- a/src/CustomTitlebar-Static/main.cpp +++ b/src/CustomTitlebar-Static/main.cpp @@ -14,13 +14,13 @@ # ------------------------------------------------- */ -#include "nmainwindow.h" +#include "qcustomwindow.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); - NMainWindow w; + QCustomWindow w; w.show(); return a.exec(); diff --git a/src/CustomTitlebar-Static/nmainwindow.cpp b/src/CustomTitlebar-Static/nmainwindow.cpp deleted file mode 100644 index 68ac9dd..0000000 --- a/src/CustomTitlebar-Static/nmainwindow.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#include "nmainwindow.h" -#include "ui_nmainwindow.h" - -NMainWindow::NMainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::NMainWindow), - RESIZE_LIMIT(2) -{ - ui->setupUi(this); - setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); - centralWidget()->installEventFilter(this); - ui->titleBar->installEventFilter(this); - ui->statusBar->installEventFilter(this); - - centralWidget()->setMouseTracking(true); - ui->titleBar->setMouseTracking(true); - ui->statusBar->setMouseTracking(true); - - setWindowTitle("Custom Window Border"); - locked = LockMoveType::None; - - ui->headerWidget->setTitleBarWidget(ui->titleBar); - if (ui->titleBar->mainWindow() != this) - ui->titleBar->setMainWindow(this); - - if (this->maximumSize() == this->minimumSize()) - setMaximizeButtonEnabled(false); - - connect(ui->titleBar, &TitleBar::closeRequest, this, &NMainWindow::close); -} - -NMainWindow::~NMainWindow() -{ - delete ui; -} - -void NMainWindow::setCustomWidgets(QWidget *newCentralWidget, QStatusBar *newStatusBar){ - setCustomStatusBar(newStatusBar); - setNewCentralWidget(newCentralWidget); -} - -void NMainWindow::setCustomStatusBar(QStatusBar *newStatusBar){ - if (!newStatusBar) return; - newStatusBar->installEventFilter(this); - newStatusBar->setMouseTracking(true); -} - -void NMainWindow::setNewCentralWidget(QWidget *newCentralWidget){ - if (!newCentralWidget) return; - newCentralWidget->installEventFilter(this); - newCentralWidget->setMouseTracking(true); - - if (newCentralWidget->layout()) - newCentralWidget->layout()->setContentsMargins(10, 0, 10, 0); -} - -void NMainWindow::setCloseButtonEnabled(bool enable){ - ui->titleBar->setCloseButtonEnabled(enable); -} - -void NMainWindow::setMaximizeButtonEnabled(bool enable){ - ui->titleBar->setMaximizeButtonEnabled(enable); -} - -void NMainWindow::setMinimizeButtonEnabled(bool enable){ - ui->titleBar->setMinimizeButtonEnabled(enable); -} - -void NMainWindow::setTitlebarStylesheet(const QString &styleSheet){ - ui->titleBar->setStyleSheet(styleSheet); -} - -QString NMainWindow::titlebarStylesheet() const { - return ui->titleBar->styleSheet(); -} - -void NMainWindow::closeEvent(QCloseEvent *event){ - if (event->isAccepted()) QMainWindow::closeEvent(event); - else event->ignore(); -} - -/* - * GUI Protected Methods (do not change them, unless it's really necessary) - */ - -void NMainWindow::mousePressEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - int x = event->x(), y = event->y(), bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; - if (x < RESIZE_LIMIT && y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topLeft(); - locked = LockMoveType::TopLeft; - } - else if (x < RESIZE_LIMIT && y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomLeft(); - locked = LockMoveType::BottomLeft; - } - else if (x > right && y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topRight(); - locked = LockMoveType::TopRight; - } - else if (x > right && y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomRight(); - locked = LockMoveType::BottomRight; - } - else if (x < RESIZE_LIMIT || y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topLeft(); - locked = x < RESIZE_LIMIT ? LockMoveType::Left : LockMoveType::Top; - } - else if (x > right || y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomRight(); - locked = x > right ? LockMoveType::Right : LockMoveType::Bottom; - } - event->accept(); - } -} - -void NMainWindow::undefMouseMoveEvent(QObject* object, QMouseEvent* event){ - if (locked != LockMoveType::None){ - switch (locked) { - case LockMoveType::TopLeft: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), event->globalPos().y() - posCursor.y()), - this->geometry().bottomRight())); - break; - case LockMoveType::TopRight: - this->setGeometry(QRect(QPoint(this->geometry().left(), event->globalPos().y() - posCursor.y()), - QPoint(event->globalPos().x() - posCursor.x(), this->geometry().bottom()))); - break; - case LockMoveType::BottomLeft: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), this->geometry().top()), - QPoint(this->geometry().right(), event->globalPos().y() - posCursor.y()))); - break; - case LockMoveType::BottomRight: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(event->globalPos().x() - posCursor.x(), event->globalPos().y() - posCursor.y()))); - break; - case LockMoveType::Left: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), this->geometry().top()), - this->geometry().bottomRight())); - break; - case LockMoveType::Right: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(event->globalPos().x() - posCursor.x(), this->geometry().bottom()))); - break; - case LockMoveType::Top: - this->setGeometry(QRect(QPoint(this->geometry().left(), event->globalPos().y() - posCursor.y()), - this->geometry().bottomRight())); - break; - default: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(this->geometry().right(), event->globalPos().y() - posCursor.y()))); - break; - } - return; - } - - int x = event->x(), y = event->y(), right = this->width() - RESIZE_LIMIT; - if (object->objectName() == "statusBar"){ - if (x < RESIZE_LIMIT && y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeBDiagCursor)); - return; - } - else if (x > right && y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeFDiagCursor)); - return; - } - else if (y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeVerCursor)); - return; - } - } - else if (object->objectName() == "titleBar"){ - if (x < RESIZE_LIMIT && y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeFDiagCursor)); - return; - } - if (x > right && y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeBDiagCursor)); - return; - } - else if (y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeVerCursor)); - return; - } - } - - this->setCursor(QCursor(x < RESIZE_LIMIT || x > right ? Qt::SizeHorCursor : Qt::ArrowCursor)); -} - -void NMainWindow::mouseReleaseEvent(QMouseEvent *event){ - locked = LockMoveType::None; - event->accept(); -} - -bool NMainWindow::eventFilter(QObject* object, QEvent* event) -{ - if(event->type() == QEvent::MouseMove) - undefMouseMoveEvent(object, static_cast(event)); - else if (event->type() == QEvent::MouseButtonPress && object->objectName() == "titleBar") - mousePressEvent(static_cast(event)); - return false; -} diff --git a/src/CustomTitlebar-Static/nmainwindow.h b/src/CustomTitlebar-Static/nmainwindow.h deleted file mode 100644 index 6e06456..0000000 --- a/src/CustomTitlebar-Static/nmainwindow.h +++ /dev/null @@ -1,78 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#ifndef NMAINWINDOW_H -#define NMAINWINDOW_H - -#include -#include -#include -#include - -#include "titlebar.h" - -namespace Ui { -class NMainWindow; -} - -class NMainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit NMainWindow(QWidget *parent = nullptr); - ~NMainWindow(); - - void setCustomWidgets(QWidget *newCentralWidget, QStatusBar *newStatusBar); - void setNewCentralWidget(QWidget *newCentralWidget); - void setCustomStatusBar(QStatusBar *newStatusBar); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - - void setTitlebarStylesheet(const QString &styleSheet); - QString titlebarStylesheet() const; - -protected: - void closeEvent(QCloseEvent *event); - - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void undefMouseMoveEvent(QObject *object, QMouseEvent* event); - bool eventFilter(QObject *watched, QEvent *event); - - enum LockMoveType{ - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight, - None - }; - -private: - Ui::NMainWindow *ui; - const int RESIZE_LIMIT; - - QPoint posCursor; - LockMoveType locked; -}; - -#endif // NMAINWINDOW_H diff --git a/src/CustomTitlebar-Static/nmainwindow.ui b/src/CustomTitlebar-Static/nmainwindow.ui deleted file mode 100644 index bdc7d03..0000000 --- a/src/CustomTitlebar-Static/nmainwindow.ui +++ /dev/null @@ -1,144 +0,0 @@ - - - NMainWindow - - - - 0 - 0 - 400 - 300 - - - - ArrowCursor - - - NMainWindow - - - QWidget#titleBar{ - background-color: #FFF; - border-top: 1px solid orange; - border-left: 1px solid orange; - border-right: 1px solid orange; - color: #000; -} - -QDockWidget#headerWidget, QDockWidget#headerWidget::title{ - background-color: #FFF; - border-top: 1px solid orange; - border-left: 1px solid orange; - border-right: 1px solid orange; - color: #000; -} - -QWidget#centralWidget, QWidget#headerWidgetContents, QStatusBar{ - background-color: #FFF; - border-left: 1px solid orange; - border-right: 1px solid orange; -} - -QMainWindow::separator { - background-color: #FFF; - background: #FFF; - border-left: 1px solid orange; - border-right: 1px solid orange; - height: 0px; -} - -QStatusBar{ - border-bottom: 1px solid orange; -} - - - - - 10 - - - 0 - - - 10 - - - 0 - - - 0 - - - - - - - - 36 - 35 - - - - - 524287 - 35 - - - - QDockWidget::NoDockWidgetFeatures - - - Qt::TopDockWidgetArea - - - 4 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 35 - - - - - 16777215 - 35 - - - - - - - - - - - - TitleBar - QWidget -
titlebar.h
- 1 -
-
- - -
diff --git a/src/CustomTitlebar-Static/qcustomwindow.cpp b/src/CustomTitlebar-Static/qcustomwindow.cpp new file mode 100644 index 0000000..5447b1a --- /dev/null +++ b/src/CustomTitlebar-Static/qcustomwindow.cpp @@ -0,0 +1,260 @@ +#include "qcustomwindow.h" + +QCustomWindow::QCustomWindow(QWidget *parent) : + QMainWindow(parent), RESIZE_LIMIT(4) +{ + setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); + + this->setMinimumSize(QSize(256, 64)); + this->setStyleSheet(QStringLiteral( + "\nQCustomWindow {\n" + " background : white;\n" + " border : 1px solid orange;\n" + "}\n" + "QStatusBar {\n" + " background : white;\n" + " border-bottom: 1px solid orange;\n" + " border-left: 1px solid orange;\n" + " border-right: 1px solid orange;\n" + "}\n" + "QToolBar {\n" + " margin : 1px;\n" + "}" + )); + + this->m_titleBar = new QTitleBar(this); + connect(this->m_titleBar, &QTitleBar::requestClose, + this, &QCustomWindow::close); + connect(this->m_titleBar, &QTitleBar::requestMaximize, [this]{ + if (this->isMaximized()) this->showNormal(); + else this->showMaximized(); + }); + connect(this->m_titleBar, &QTitleBar::requestMinimize, + this, &QCustomWindow::showMinimized); + connect(this, &QMainWindow::windowTitleChanged, this->m_titleBar, + &QWidget::setWindowTitle); + + this->m_titleBarW = new QWidget(); + this->m_titleBarW->setMouseTracking(true); + this->m_titleBarW->installEventFilter(this); + + QVBoxLayout *titleLayout = new QVBoxLayout(); + titleLayout->setContentsMargins(6, 6, 6, 0); + titleLayout->setSpacing(0); + titleLayout->addWidget(this->m_titleBar); + + this->m_titleBarW->setLayout(titleLayout); + QMainWindow::setMenuWidget(this->m_titleBarW); + + this->m_leftBorder = generateBorder(Qt::LeftToolBarArea, Qt::Vertical); + this->m_rightBorder = generateBorder(Qt::RightToolBarArea, Qt::Vertical); + this->m_bottomBorder = generateBorder(Qt::BottomToolBarArea, Qt::Horizontal); + + this->m_menuBar = nullptr; + this->m_menuWidget = nullptr; +} + +QCustomWindow::~QCustomWindow(){ + delete this->m_titleBar; + delete this->m_leftBorder; + delete this->m_rightBorder; + delete this->m_bottomBorder; + + if (this->m_menuBar) delete this->m_menuBar; + else if (this->m_menuWidget) delete this->m_menuWidget; +} + +QToolBar *QCustomWindow::generateBorder(Qt::ToolBarArea area, + Qt::Orientation orientation){ + QToolBar *border = new QToolBar("___border___"); + border->setStyleSheet( + "\nQToolBar {\n" + " margin : 1px;\n" + " border : 0px;\n" + " background : transparent;\n" + "}" + ); + + if (orientation & Qt::Horizontal){ + border->setMinimumHeight(6); + border->setMaximumHeight(6); + } + else { + border->setMinimumWidth(6); + border->setMaximumWidth(6); + } + border->setMovable(false); + border->setFloatable(false); + border->setAllowedAreas(area); + border->setMouseTracking(true); + border->installEventFilter(this); + + this->addToolBar(area, border); + return border; +} + +QMenu * QCustomWindow::createPopupMenu(){ + QMenu *menu = QMainWindow::createPopupMenu(); + QList removal; + foreach (QAction *a, menu->actions()) + if (a->text() == "___border___") removal.append(a); + foreach (QAction *a, removal) menu->removeAction(a); + return menu; +} + +void QCustomWindow::setMenuBar(QMenuBar *menuBar){ + if (this->m_menuBar == menuBar) return; + + if (this->m_menuBar) { + if (this->m_menuBar != this->m_menuWidget && this->m_menuWidget){ + this->m_menuWidget->setParent(nullptr); + this->m_menuWidget->deleteLater(); + } + + this->m_menuBar->hide(); + this->m_menuBar->setParent(nullptr); + this->m_menuBar->deleteLater(); + } + + this->m_menuBar = menuBar; + this->m_menuWidget = qobject_cast(menuBar); + + if (menuBar){ + menuBar->setParent(this); + this->m_titleBarW->layout()->addWidget(menuBar); + } +} + +QMenuBar* QCustomWindow::menuBar() const{ + return this->m_menuBar; +} + +void QCustomWindow::setMenuWidget(QWidget *widget){ + if (this->m_menuWidget == widget) return; + + widget->setParent(this); + + if (this->m_menuWidget){ + this->m_menuWidget->hide(); + this->m_menuWidget->setParent(nullptr); + this->m_menuWidget->deleteLater(); + } + + this->m_menuBar = nullptr; + this->m_menuWidget = widget; + + if (this->m_menuWidget){ + this->m_menuWidget->setParent(this); + this->m_titleBarW->layout()->addWidget(widget); + } +} + +QWidget * QCustomWindow::menuWidget() const{ + return this->m_menuWidget; +} + +bool QCustomWindow::eventFilter(QObject*, QEvent *event){ + if (event->type() == QEvent::MouseMove) + customMouseMoveEvent(static_cast(event)); + else if (event->type() == QEvent::MouseButtonPress) + mousePressEvent(static_cast(event)); + return false; +} + +bool QCustomWindow::event(QEvent *event){ + if (event->type() == QEvent::ChildRemoved){ + QChildEvent *evt = static_cast(event); + if (evt->child()->isWidgetType()) evt->child()->removeEventFilter(this); + } + else if (event->type() == QEvent::ChildAdded){ + QChildEvent *evt = static_cast(event); + if (evt->child()->isWidgetType()){ + QWidget *child = qobject_cast(evt->child()); + + child->setMouseTracking(true); + child->installEventFilter(this); + + if (child->metaObject()->indexOfClassInfo("custom_obj_type") == -1){ + child->setStyleSheet(child->styleSheet() + + "\nQToolBar {\n" + " margin : 1px;\n" + " padding: 0px 6px 0px 6px;\n" + " border: 1px transparent solid;" + "}\n" + "QToolBar:top:first, QToolBar:bottom:first, QToolBar:left:first {\n" + " margin : 0px 0px 0px 6px;\n" + "}\n" + "QToolBar:top:only-one, QToolBar:bottom:only-one {\n" + " margin : 0px 6px 0px 6px;\n" + "}\n" + "QToolBar:top:last, QToolBar:bottom:last, QToolBar:right:last {\n" + " margin : 0px 6px 0px 0px;\n" + "}\n"); + } + } + } + return QMainWindow::event(event); +} + +void QCustomWindow::mousePressEvent(QMouseEvent *event){ + if (event->button() & Qt::LeftButton){ + int x = event->x(), y = event->y(); + int bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; + + QPoint posCursor = event->globalPos(); + Qt::Edges nFlags = {}; + if (x < RESIZE_LIMIT) { + nFlags |= Qt::LeftEdge; + posCursor.rx() -= this->x(); + } + if (y < RESIZE_LIMIT) { + nFlags |= Qt::TopEdge; + posCursor.ry() -= this->y(); + } + if (x > right) { + nFlags |= Qt::RightEdge; + posCursor.rx() -= (this->x() + this->width()); + } + if (y > bottom) { + nFlags |= Qt::BottomEdge; + posCursor.ry() -= (this->y() + this->height()); + } + this->m_lock = nFlags; + this->m_posCursor = posCursor; + } + QMainWindow::mousePressEvent(event); +} + +void QCustomWindow::mouseReleaseEvent(QMouseEvent *event){ + this->m_lock = {}; + this->unsetCursor(); + QMainWindow::mouseMoveEvent(event); +} + +void QCustomWindow::customMouseMoveEvent(QMouseEvent *event){ + if (this->m_lock){ + QPoint tL = this->geometry().topLeft(), bR = this->geometry().bottomRight(); + if (this->m_lock & Qt::TopEdge) tL.ry() = event->globalY() - this->m_posCursor.y(); + if (this->m_lock & Qt::BottomEdge) bR.ry() = event->globalY() - this->m_posCursor.y(); + if (this->m_lock & Qt::LeftEdge) tL.rx() = event->globalX() - this->m_posCursor.x(); + if (this->m_lock & Qt::RightEdge) bR.rx() = event->globalX() - this->m_posCursor.x(); + this->setGeometry(QRect(tL, bR)); + return; + } + + int x = event->globalX() - this->x(), y = event->globalY() - this->y(); + int bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; + + if (x < RESIZE_LIMIT){ + if (y < RESIZE_LIMIT) this->setCursor(QCursor(Qt::SizeFDiagCursor)); + else if (y > bottom) this->setCursor(QCursor(Qt::SizeBDiagCursor)); + else this->setCursor(QCursor(Qt::SizeHorCursor)); + } + else if (x > right){ + if (y < RESIZE_LIMIT) this->setCursor(QCursor(Qt::SizeBDiagCursor)); + else if (y > bottom) this->setCursor(QCursor(Qt::SizeFDiagCursor)); + else this->setCursor(QCursor(Qt::SizeHorCursor)); + } + else if (y < RESIZE_LIMIT || y > bottom) this->setCursor(Qt::SizeVerCursor); + else this->unsetCursor(); +} diff --git a/src/CustomTitlebar-Static/qcustomwindow.h b/src/CustomTitlebar-Static/qcustomwindow.h new file mode 100644 index 0000000..20015d2 --- /dev/null +++ b/src/CustomTitlebar-Static/qcustomwindow.h @@ -0,0 +1,68 @@ +#ifndef QCUSTOMWINDOW_H +#define QCUSTOMWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qtitlebar.h" + +class QCustomWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit QCustomWindow(QWidget *parent = nullptr); + ~QCustomWindow() override; + + QMenu * createPopupMenu() override; + + void setMenuBar(QMenuBar *menuBar); + QMenuBar * menuBar() const; + + void setMenuWidget(QWidget *widget); + QWidget * menuWidget() const; + + inline QTitleBar& titleBar() const { return *this->m_titleBar; } + +protected: + const int RESIZE_LIMIT; + + bool event(QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + + void customMouseMoveEvent(QMouseEvent* event); + +private: + bool init; + + QWidget *m_titleBarW; + QWidget *m_menuWidget; + QMenuBar *m_menuBar; + QTitleBar *m_titleBar; + + QToolBar *m_leftBorder; + QToolBar *m_rightBorder; + QToolBar *m_bottomBorder; + + Qt::Edges m_lock; + QPoint m_posCursor; + + QToolBar * generateBorder(Qt::ToolBarArea area, Qt::Orientation orientation); +}; + +#endif // QCUSTOMWINDOW_H diff --git a/src/CustomTitlebar-Static/qtitlebar.cpp b/src/CustomTitlebar-Static/qtitlebar.cpp new file mode 100644 index 0000000..6b364c0 --- /dev/null +++ b/src/CustomTitlebar-Static/qtitlebar.cpp @@ -0,0 +1,111 @@ +#include "qtitlebar.h" + +QTitleBar::QTitleBar(QMainWindow *parent) : + QWidget(parent), FRAME_BUTTON_SIZE(24, 24) +{ + this->canMove = false; + this->maximizing = false; + this->m_frameButtons = QCustomAttrs::All; + + this->setStyleSheet(QStringLiteral( + "QPushButton {\n" + " border : 0.5px solid gray;\n" + " border-radius: 3px;\n" + " min-height: 20px;\n" + " margin: 1px;\n" + " background: white;\n" + " color: gray;\n" + "}\n" + "QPushButton::hover {\n" + " background: darkorange;\n" + " border : 0.5px solid darkorange;\n" + " color: white;\n" + "}\n" + "QPushButton::pressed, QPushButton::!enabled {\n" + " background: orange;\n" + " border : 0.5px solid orange;\n" + " color: white;\n" + "}\n" + "QTitleBar { background: white; }\n" + )); + + if (!parent) throw std::exception("Parent must be a QCustomWindow object (cannot be null)."); + this->m_parentWindow = parent; + + this->lbl_windowTitle.setText("QCustomWindow"); + this->lbl_windowTitle.setAlignment(Qt::AlignCenter); + + this->btn_close.setText("X"); + this->btn_maximize.setText("+"); + this->btn_minimize.setText("-"); + + this->btn_close.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_close.setMinimumSize(FRAME_BUTTON_SIZE); + this->btn_maximize.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_maximize.setMinimumSize(FRAME_BUTTON_SIZE); + this->btn_minimize.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_minimize.setMinimumSize(FRAME_BUTTON_SIZE); + + this->m_layout.addWidget(&this->lbl_windowTitle, 1); + this->m_layout.addWidget(&this->btn_minimize); + this->m_layout.addWidget(&this->btn_maximize); + this->m_layout.addWidget(&this->btn_close); + this->m_layout.setContentsMargins(0, 0, 0, 4); + this->m_layout.setSpacing(0); + + this->setLayout(&this->m_layout); + + connect(&this->btn_close, &QPushButton::clicked, [this]{ emit this->requestClose(); }); + connect(&this->btn_minimize, &QPushButton::clicked, [this]{ emit this->requestMinimize(); }); + connect(&this->btn_maximize, &QPushButton::clicked, [this]{ emit this->requestMaximize(); }); + + connect(this, &QWidget::windowTitleChanged, &this->lbl_windowTitle, &QLabel::setText); + + this->setMaximumHeight(35); + this->setMinimumHeight(35); +} + +void QTitleBar::setWindowButtons(QCustomAttrs::WindowButtons btns){ + this->m_frameButtons = btns; + this->btn_close.setVisible(btns & QCustomAttrs::Close); + this->btn_maximize.setVisible(btns & QCustomAttrs::Maximize); + this->btn_minimize.setVisible(btns & QCustomAttrs::Minimize); +} + +void QTitleBar::setWindowButtonEnabled(QCustomAttrs::WindowButton btn, bool enabled){ + if (btn & QCustomAttrs::Close) this->btn_close.setEnabled(enabled); + if (btn & QCustomAttrs::Maximize) this->btn_maximize.setEnabled(enabled); + if (btn & QCustomAttrs::Minimize) this->btn_minimize.setEnabled(enabled); +} + +void QTitleBar::paintEvent(QPaintEvent *event){ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} + +void QTitleBar::mousePressEvent(QMouseEvent *event){ + if (event->button() & Qt::LeftButton){ + this->canMove = (event->x() > 5 && event->y() > 5 && event->x() < (this->m_parentWindow->width() - 5)); + this->m_pCursor = event->globalPos() - this->m_parentWindow->geometry().topLeft(); + } + QWidget::mousePressEvent(event); +} + +void QTitleBar::mouseMoveEvent(QMouseEvent *event){ + if (!this->maximizing && canMove && event->buttons() & Qt::LeftButton + && !this->m_parentWindow->isMaximized()) this->m_parentWindow->move(event->globalPos() - m_pCursor); + this->maximizing = false; + QWidget::mouseMoveEvent(event); +} + +void QTitleBar::mouseDoubleClickEvent(QMouseEvent *event){ + if (m_frameButtons & QCustomAttrs::Maximize && btn_maximize.isEnabled() + && event->buttons() & Qt::LeftButton) { + this->maximizing = true; + emit requestMaximize(); + } + QWidget::mouseDoubleClickEvent(event); +} diff --git a/src/CustomTitlebar-Static/qtitlebar.h b/src/CustomTitlebar-Static/qtitlebar.h new file mode 100644 index 0000000..e0b83c9 --- /dev/null +++ b/src/CustomTitlebar-Static/qtitlebar.h @@ -0,0 +1,73 @@ +#ifndef QTITLEBAR_H +#define QTITLEBAR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QCustomAttrs { + enum WindowButton { + Minimize = 0x01, + Maximize = 0x02, + Close = 0x04, + All = Minimize | Maximize | Close + }; + + Q_DECLARE_FLAGS(WindowButtons, WindowButton) + Q_DECLARE_OPERATORS_FOR_FLAGS(WindowButtons) +} + +class QTitleBar : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QCustomAttrs::WindowButtons windowButtons READ windowButtons WRITE setWindowButtons) + Q_CLASSINFO("custom_obj_type", "QTitleBar") +public: + explicit QTitleBar(QMainWindow *parent = nullptr); + + void setWindowButtons(QCustomAttrs::WindowButtons btns); + inline QCustomAttrs::WindowButtons windowButtons() const { return this->m_frameButtons; } + + void setWindowButtonEnabled(QCustomAttrs::WindowButton btn, bool enabled = true); + +protected: + void paintEvent(QPaintEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + +private: + bool canMove; + bool maximizing; + + QPoint m_pCursor; + const QSize FRAME_BUTTON_SIZE; + + QWidget *m_parentWindow; + + QCustomAttrs::WindowButtons m_frameButtons; + + QLabel lbl_windowTitle; + QHBoxLayout m_layout; + QPushButton btn_minimize; + QPushButton btn_maximize; + QPushButton btn_close; + +signals: + void requestClose(); + void requestMaximize(); + void requestMinimize(); +}; + +#endif // QTITLEBAR_H diff --git a/src/CustomTitlebar-Static/titlebar.cpp b/src/CustomTitlebar-Static/titlebar.cpp deleted file mode 100644 index 831009a..0000000 --- a/src/CustomTitlebar-Static/titlebar.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#include "titlebar.h" -#include "ui_titlebar.h" - -TitleBar::TitleBar(QWidget *parent) : - QWidget(parent), - ui(new Ui::TitleBar) -{ - ui->setupUi(this); - setMainWindow(parent->parentWidget() ? parent->parentWidget() : parent); - - canMove = false; -} - -TitleBar::~TitleBar() -{ - delete ui; -} - -void TitleBar::setMainWindow(QWidget *mainWindow){ - this->parent = mainWindow; - - disconnect(ui->btClose, &QPushButton::clicked, this, &TitleBar::onCloseRequest); - disconnect(ui->btMinimize, &QPushButton::clicked, this->parent, &QWidget::showMinimized); - disconnect(ui->btMaximize, &QPushButton::clicked, this, &TitleBar::maximizeParent); - - disconnect(this->parent, &QWidget::windowTitleChanged, ui->lblFormTitle, &QLabel::setText); - - connect(ui->btClose, &QPushButton::clicked, this, &TitleBar::onCloseRequest); - connect(ui->btMinimize, &QPushButton::clicked, this->parent, &QWidget::showMinimized); - connect(ui->btMaximize, &QPushButton::clicked, this, &TitleBar::maximizeParent); - - connect(this->parent, &QWidget::windowTitleChanged, ui->lblFormTitle, &QLabel::setText); -} - -void TitleBar::maximizeParent(){ - if (parent->isMaximized()) parent->showNormal(); - else parent->showMaximized(); -} - -void TitleBar::onCloseRequest(){ - emit closeRequest(); -} - -QWidget* TitleBar::mainWindow(){ - return this->parent; -} - -void TitleBar::setCloseButtonEnabled(bool enable){ - ui->btClose->setEnabled(enable); -} - -void TitleBar::setMaximizeButtonEnabled(bool enable){ - ui->btMaximize->setEnabled(enable); -} - -void TitleBar::setMinimizeButtonEnabled(bool enable){ - ui->btMinimize->setEnabled(enable); -} - -void TitleBar::mousePressEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - canMove = !(event->x() < 5 || event->x() > (parent->width() - 5) || event->y() < 5); - m_pCursor = event->globalPos() - parent->geometry().topLeft(); - event->accept(); - } -} - -void TitleBar::mouseMoveEvent(QMouseEvent *event) -{ - if (!canMove){ - event->accept(); - return; - } - if (event->buttons() & Qt::LeftButton){ - if (parent->isMaximized()){ - event->accept(); - return; - } - parent->move(event->globalPos() - m_pCursor); - event->accept(); - } -} - -void TitleBar::mouseDoubleClickEvent(QMouseEvent *event){ - if (!ui->btMaximize->isEnabled()){ - event->accept(); - return; - } - if (event->button() == Qt::LeftButton) maximizeParent(); -} - -void TitleBar::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} diff --git a/src/CustomTitlebar-Static/titlebar.h b/src/CustomTitlebar-Static/titlebar.h deleted file mode 100644 index 2c170d4..0000000 --- a/src/CustomTitlebar-Static/titlebar.h +++ /dev/null @@ -1,65 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#ifndef TITLEBAR_H -#define TITLEBAR_H - -#include -#include -#include -#include - -namespace Ui { -class TitleBar; -} - -class TitleBar : public QWidget -{ - Q_OBJECT - -public: - explicit TitleBar(QWidget *parent = nullptr); - ~TitleBar(); - - void setMainWindow(QWidget *mainWindow); - QWidget* mainWindow(); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - -private: - bool canMove; - - Ui::TitleBar *ui; - QWidget *parent; - QPoint m_pCursor; - -protected: - void paintEvent(QPaintEvent *); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseDoubleClickEvent(QMouseEvent *event); - -protected slots: - void maximizeParent(); - void onCloseRequest(); - -signals: - void closeRequest(); -}; - -#endif // TITLEBAR_H diff --git a/src/CustomTitlebar-Static/titlebar.ui b/src/CustomTitlebar-Static/titlebar.ui deleted file mode 100644 index c397b65..0000000 --- a/src/CustomTitlebar-Static/titlebar.ui +++ /dev/null @@ -1,148 +0,0 @@ - - - TitleBar - - - - 0 - 0 - 721 - 35 - - - - - 0 - 35 - - - - - 16777215 - 35 - - - - Form - - - QPushButton{ - border : 0.5px solid gray; - border-radius: 3px; - min-height: 20px; - margin: 1px; - background: white; - color: gray; -} - -QPushButton::hover{ - background: darkorange; - border : 0.5px solid darkorange; - color: white; -} - -QPushButton::pressed, QPushButton::!enabled{ - background: orange; - border : 0.5px solid orange; - color: white; -} - -QWidget::lblFormTitle{ - background-color: white; -} - -QPushButton#lblFormTitle{ - background: transparent; - border-radius: 0px; - border: transparent; - color: black; -} - - - - 0 - - - 6 - - - 6 - - - 6 - - - 0 - - - - - frmTitle - - - Qt::AlignCenter - - - - - - - - 24 - 24 - - - - - 25 - 16777215 - - - - _ - - - - - - - - 24 - 24 - - - - - 25 - 16777215 - - - - + - - - - - - - - 24 - 24 - - - - - 30 - 16777215 - - - - x - - - - - - - - From cb801d65a7392c6bfa2fb805a3500e882251aa63 Mon Sep 17 00:00:00 2001 From: Mauro Mascarenhas Date: Tue, 25 Aug 2020 18:24:57 -0300 Subject: [PATCH 2/4] Update dynamic linked version - Updated the dynamically linked version of the library so as to reflect changes made on the static version; - Updated dynamically linked sample. - Updated licence notice date. --- .../CustomFrame-Dynamic/CustomFrameTestIn.pro | 6 +- .../customtitlebar/CustomTitlebar.dll | Bin 132608 -> 0 bytes .../customtitlebar/CustomTitlebar.lib | Bin 10850 -> 0 bytes .../customtitlebar/nmainwindow.h | 86 ------ .../customtitlebar/qcustomwindow.h | 92 ++++++ .../customtitlebar/qtitlebar.h | 97 ++++++ .../customtitlebar/titlebar.h | 65 ----- sample/CustomFrame-Dynamic/main.cpp | 2 +- sample/CustomFrame-Dynamic/testwindow.cpp | 15 +- sample/CustomFrame-Dynamic/testwindow.h | 7 +- .../CustomFrame-Static/CustomFrameTestIn.pro | 2 +- .../customtitlebar/qcustomwindow.cpp | 16 + .../customtitlebar/qcustomwindow.h | 16 + .../customtitlebar/qtitlebar.cpp | 16 + .../customtitlebar/qtitlebar.h | 16 + sample/CustomFrame-Static/main.cpp | 2 +- sample/CustomFrame-Static/testwindow.cpp | 2 +- sample/CustomFrame-Static/testwindow.h | 2 +- src/CustomTitlebar-Dynamic/CustomTitlebar.pro | 22 +- src/CustomTitlebar-Dynamic/nmainwindow.cpp | 219 -------------- src/CustomTitlebar-Dynamic/nmainwindow.h | 86 ------ src/CustomTitlebar-Dynamic/nmainwindow.ui | 144 --------- src/CustomTitlebar-Dynamic/qcustomwindow.cpp | 276 ++++++++++++++++++ src/CustomTitlebar-Dynamic/qcustomwindow.h | 92 ++++++ src/CustomTitlebar-Dynamic/qtitlebar.cpp | 127 ++++++++ src/CustomTitlebar-Dynamic/qtitlebar.h | 97 ++++++ src/CustomTitlebar-Dynamic/titlebar.cpp | 116 -------- src/CustomTitlebar-Dynamic/titlebar.h | 65 ----- src/CustomTitlebar-Dynamic/titlebar.ui | 148 ---------- src/CustomTitlebar-Static/CustomTitlebar.pro | 2 +- src/CustomTitlebar-Static/main.cpp | 2 +- src/CustomTitlebar-Static/qcustomwindow.cpp | 16 + src/CustomTitlebar-Static/qcustomwindow.h | 16 + src/CustomTitlebar-Static/qtitlebar.cpp | 16 + src/CustomTitlebar-Static/qtitlebar.h | 16 + 35 files changed, 942 insertions(+), 960 deletions(-) delete mode 100644 sample/CustomFrame-Dynamic/customtitlebar/CustomTitlebar.dll delete mode 100644 sample/CustomFrame-Dynamic/customtitlebar/CustomTitlebar.lib delete mode 100644 sample/CustomFrame-Dynamic/customtitlebar/nmainwindow.h create mode 100644 sample/CustomFrame-Dynamic/customtitlebar/qcustomwindow.h create mode 100644 sample/CustomFrame-Dynamic/customtitlebar/qtitlebar.h delete mode 100644 sample/CustomFrame-Dynamic/customtitlebar/titlebar.h delete mode 100644 src/CustomTitlebar-Dynamic/nmainwindow.cpp delete mode 100644 src/CustomTitlebar-Dynamic/nmainwindow.h delete mode 100644 src/CustomTitlebar-Dynamic/nmainwindow.ui create mode 100644 src/CustomTitlebar-Dynamic/qcustomwindow.cpp create mode 100644 src/CustomTitlebar-Dynamic/qcustomwindow.h create mode 100644 src/CustomTitlebar-Dynamic/qtitlebar.cpp create mode 100644 src/CustomTitlebar-Dynamic/qtitlebar.h delete mode 100644 src/CustomTitlebar-Dynamic/titlebar.cpp delete mode 100644 src/CustomTitlebar-Dynamic/titlebar.h delete mode 100644 src/CustomTitlebar-Dynamic/titlebar.ui diff --git a/sample/CustomFrame-Dynamic/CustomFrameTestIn.pro b/sample/CustomFrame-Dynamic/CustomFrameTestIn.pro index d23231d..539c2f7 100644 --- a/sample/CustomFrame-Dynamic/CustomFrameTestIn.pro +++ b/sample/CustomFrame-Dynamic/CustomFrameTestIn.pro @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # @@ -43,5 +43,5 @@ HEADERS += testwindow.h FORMS += testwindow.ui -win32:CONFIG(release, debug|release): LIBS += -L$$PWD/customtitlebar/ -lCustomTitlebar -else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/customtitlebar/ -lCustomTitlebar +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/customtitlebar/ -lQCustomTitlebar +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/customtitlebar/ -lQCustomTitlebar diff --git a/sample/CustomFrame-Dynamic/customtitlebar/CustomTitlebar.dll b/sample/CustomFrame-Dynamic/customtitlebar/CustomTitlebar.dll deleted file mode 100644 index 393d2a298409b9b5d9467744b960f6d0dc77a333..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132608 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 U{zhv?3Ce$E36v%9|62n81Fd6#CjbBd diff --git a/sample/CustomFrame-Dynamic/customtitlebar/CustomTitlebar.lib b/sample/CustomFrame-Dynamic/customtitlebar/CustomTitlebar.lib deleted file mode 100644 index 842282b4380a17201a4a867affd13b5608eb250c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10850 zcmcgyOLx;o6uxnInny!`Ktccq0wlBrJ5PslD%p-{Qex*#a@vKX*aEA^v8k;<+f8>_ z_3#5&v+SA`3w{6#4u>U67OeOIZ0McQj3wEUt&x@LoEyFF=j zj@XayWA=M~7iAY?@z}I+xfKS0F@V000R1-s28sY9&kY*A4nXL_TZ0BS0SFBv525RE zgRZ^-pmYb%NY{E8y}&cl^>+r1gitTiCDcJEa)r?=JR^<1H0aW2e2;YTgF%Du@%#Yb z@-u^mz5q}{9YaW0_6)jF&gJGaON%ROx!Lq;YGrs*5)YPF^{Q5WB+E{&vRqBh9O=|lpNBJ2&V@JQ8V*#zu`Rgz*hed|QuX8oI8 z-byu9HwM?RTC;yG0kvrZMQ>JhMc=I@m8unF0GbGztok&imi4MqV&h=@s%AjuDESjM zN5K3l7n3G~Wp3)9ZNBv+PVJT|H8r`b>y>i4tZbLmg2V9VvbIl5d6pp-GRhy?j`o+R z5g{>ZM=SfXBO*rZ@K~{`|5;LNMHT&|d9)Mhr0M*&!Sf|9=`29G8%^5cjp=nhB&9r= z;Peyp0sE(A>b^*qfWcu|h{AHxPGz^IE>+c9t);mPVQVtyPuQ@zqLx&J>$J_E`H?NG z`Vg0P%LVlz)tRY0aT?zF`tUAp|EA_K{?btA3?m?Il1ireo>tP;h7F92LoQ1Z*x)ox zLkAipDR<8{_%yhhi?{9TX`A#^a+yPw`A#e4ns}CQo7QL5Ct6;`-2IG5O}(X(-Cv2`xs8dGf-h_z zF^ATOK?h@gbPxnJ+>o_A`@lveDYk|4+3}2NAG8`OEmLZn^D+BuC&5SdJU-5|=Rr)L zKlTCK><1_g06ZVTTz3@U?FE2Md|4a@h+_`@<|@D)q@HU4FOc5hvk>xbUc%fp0`Lmy zCCYuq`wtfZ-lOaTJU_b(@MQ@3koGWNF5jT(Ivj$-a29&t44j8k&n+*Ir2M>v~iYCFismGVrZr=0r*Cp>jB##-3wz!0 z6_0@~z`WQvZWYw+-ABW#>=x$m;vas)&NB2QZ)pWZSB9}SZX9RugpFwU#O}NCEJO4) zn3t2IxA{YF!0DdD_zHXN^dRYbso0y*^3_VM@=%YA ztc*q$=Ck+Nd)w(a-Vr>60JDB_g-v+O{>fFdF+`NF>^w&3rD9jt*RNJd6aj)Y1bhT! z0E)iIpU`hL>@l7R!h=Fk=b1F0kmp#o)DNN^5zxQJpAc-}#eR10MOV?-B>RCRLRL|T zuA!~mhMmbOB6|00b$FR;EH&MK8WHxv@hEp%(+5AI zg;t~2!UreWKv5qiK4?8;khu>|@aM$W@y8?ELHPD-l)a2FHUirI#w5B*r>D%=3Ajc9 z3VS5p#p#zH@K(V)ga=U^Q>-5nL2t3*_!e<=VLWnc5;&L^UTag9{ljWIhPFc(0kIL$ z)E_i6`XCTYPdLv436tF}2pT$+4lso0zy-y3h~fa91D9eX0NZhwIY^`JgT%IQrQ-lf zcxBAEpjt#!hX|?;>Zl;_#pBNFW)|@sCiq0FGzU%;n{@Wo1Y$lxFp~=1b5$P%qL~){aFQeP({ptICp^PZGkdn~ z$s0IYdQ>$H}mtpeJs0jjD z3okg$kooEY1%NHiouDD+rqBz{ki1avhe`)X!Yh7;Z&VGKenL!V$$0r_U - -#if defined(CUSTOMTITLEBAR_LIBRARY) -# define CUSTOMTITLEBARSHARED_EXPORT Q_DECL_EXPORT -#else -# define CUSTOMTITLEBARSHARED_EXPORT Q_DECL_IMPORT -#endif - -#include -#include -#include -#include - -#include "titlebar.h" - -namespace Ui { -class NMainWindow; -} - -class CUSTOMTITLEBARSHARED_EXPORT NMainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit NMainWindow(QWidget *parent = nullptr); - ~NMainWindow(); - - void setCustomWidgets(QWidget *newCentralWidget, QStatusBar *newStatusBar); - void setNewCentralWidget(QWidget *newCentralWidget); - void setCustomStatusBar(QStatusBar *newStatusBar); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - - void setTitlebarStylesheet(const QString &styleSheet); - QString titlebarStylesheet() const; - -protected: - void closeEvent(QCloseEvent *event); - - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void undefMouseMoveEvent(QObject *object, QMouseEvent* event); - bool eventFilter(QObject *watched, QEvent *event); - - enum LockMoveType{ - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight, - None - }; - -private: - Ui::NMainWindow *ui; - const int RESIZE_LIMIT; - - QPoint posCursor; - LockMoveType locked; -}; - -#endif // NMAINWINDOW_H diff --git a/sample/CustomFrame-Dynamic/customtitlebar/qcustomwindow.h b/sample/CustomFrame-Dynamic/customtitlebar/qcustomwindow.h new file mode 100644 index 0000000..84cb2de --- /dev/null +++ b/sample/CustomFrame-Dynamic/customtitlebar/qcustomwindow.h @@ -0,0 +1,92 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 24 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + +#ifndef QCUSTOMWINDOW_H +#define QCUSTOMWINDOW_H + +#include + +#if defined(QCUSTOMTITLEBAR_LIBRARY) +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_EXPORT +#else +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_IMPORT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qtitlebar.h" + +class QCUSTOMTITLEBARSHARED_EXPORT QCustomWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit QCustomWindow(QWidget *parent = nullptr); + ~QCustomWindow() override; + + QMenu * createPopupMenu() override; + + void setMenuBar(QMenuBar *menuBar); + QMenuBar * menuBar() const; + + void setMenuWidget(QWidget *widget); + QWidget * menuWidget() const; + + inline QTitleBar& titleBar() const { return *this->m_titleBar; } + +protected: + const int RESIZE_LIMIT; + + bool event(QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + + void customMouseMoveEvent(QMouseEvent* event); + +private: + bool init; + + QWidget *m_titleBarW; + QWidget *m_menuWidget; + QMenuBar *m_menuBar; + QTitleBar *m_titleBar; + + QToolBar *m_leftBorder; + QToolBar *m_rightBorder; + QToolBar *m_bottomBorder; + + Qt::Edges m_lock; + QPoint m_posCursor; + + QToolBar * generateBorder(Qt::ToolBarArea area, Qt::Orientation orientation); +}; + +#endif // QCUSTOMWINDOW_H diff --git a/sample/CustomFrame-Dynamic/customtitlebar/qtitlebar.h b/sample/CustomFrame-Dynamic/customtitlebar/qtitlebar.h new file mode 100644 index 0000000..58f4d0e --- /dev/null +++ b/sample/CustomFrame-Dynamic/customtitlebar/qtitlebar.h @@ -0,0 +1,97 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 24 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + +#ifndef QTITLEBAR_H +#define QTITLEBAR_H + +#include + +#if defined(QCUSTOMTITLEBAR_LIBRARY) +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_EXPORT +#else +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_IMPORT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QCustomAttrs { + enum WindowButton { + Minimize = 0x01, + Maximize = 0x02, + Close = 0x04, + All = Minimize | Maximize | Close + }; + + Q_DECLARE_FLAGS(WindowButtons, WindowButton) + Q_DECLARE_OPERATORS_FOR_FLAGS(WindowButtons) +} + +class QCUSTOMTITLEBARSHARED_EXPORT QTitleBar : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QCustomAttrs::WindowButtons windowButtons READ windowButtons WRITE setWindowButtons) + Q_CLASSINFO("custom_obj_type", "QTitleBar") +public: + explicit QTitleBar(QMainWindow *parent = nullptr); + + void setWindowButtons(QCustomAttrs::WindowButtons btns); + inline QCustomAttrs::WindowButtons windowButtons() const { return this->m_frameButtons; } + + void setWindowButtonEnabled(QCustomAttrs::WindowButton btn, bool enabled = true); + +protected: + void paintEvent(QPaintEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + +private: + bool canMove; + bool maximizing; + + QPoint m_pCursor; + const QSize FRAME_BUTTON_SIZE; + + QWidget *m_parentWindow; + + QCustomAttrs::WindowButtons m_frameButtons; + + QLabel lbl_windowTitle; + QHBoxLayout m_layout; + QPushButton btn_minimize; + QPushButton btn_maximize; + QPushButton btn_close; + +signals: + void requestClose(); + void requestMaximize(); + void requestMinimize(); +}; + +#endif // QTITLEBAR_H diff --git a/sample/CustomFrame-Dynamic/customtitlebar/titlebar.h b/sample/CustomFrame-Dynamic/customtitlebar/titlebar.h deleted file mode 100644 index 2c170d4..0000000 --- a/sample/CustomFrame-Dynamic/customtitlebar/titlebar.h +++ /dev/null @@ -1,65 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#ifndef TITLEBAR_H -#define TITLEBAR_H - -#include -#include -#include -#include - -namespace Ui { -class TitleBar; -} - -class TitleBar : public QWidget -{ - Q_OBJECT - -public: - explicit TitleBar(QWidget *parent = nullptr); - ~TitleBar(); - - void setMainWindow(QWidget *mainWindow); - QWidget* mainWindow(); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - -private: - bool canMove; - - Ui::TitleBar *ui; - QWidget *parent; - QPoint m_pCursor; - -protected: - void paintEvent(QPaintEvent *); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseDoubleClickEvent(QMouseEvent *event); - -protected slots: - void maximizeParent(); - void onCloseRequest(); - -signals: - void closeRequest(); -}; - -#endif // TITLEBAR_H diff --git a/sample/CustomFrame-Dynamic/main.cpp b/sample/CustomFrame-Dynamic/main.cpp index 153b157..6154b5e 100644 --- a/sample/CustomFrame-Dynamic/main.cpp +++ b/sample/CustomFrame-Dynamic/main.cpp @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # diff --git a/sample/CustomFrame-Dynamic/testwindow.cpp b/sample/CustomFrame-Dynamic/testwindow.cpp index 9493aa2..d71831a 100644 --- a/sample/CustomFrame-Dynamic/testwindow.cpp +++ b/sample/CustomFrame-Dynamic/testwindow.cpp @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # @@ -18,19 +18,24 @@ #include "ui_testwindow.h" TestWindow::TestWindow(QWidget *parent) : - NMainWindow(parent), + QCustomWindow(parent), ui(new Ui::TestWindow) { ui->setupUi(this); + // A simple test to check if QTitleBar has been successfully exported + this->titleBar().setWindowButtonEnabled(QCustomAttrs::Maximize, false); - // Sets the custom Widgets on the parent Class - // Otherwise, the window resizing feature will not work - NMainWindow::setCustomWidgets(ui->centralWidget, ui->statusBar); + /* + * QMenuBar should never be inserted using Qt Designer. + * Insert them manually instead setMenuBar(QMenuBar*) + * and setMenuWidget(QWidget*) have been reimplemented. + */ clicks = 0; connect(ui->pushButton, &QPushButton::clicked, [this]{ setWindowTitle("Number of clicks : " + QString::number(++clicks)); }); + this->setMinimumSize(500, 400); } TestWindow::~TestWindow() diff --git a/sample/CustomFrame-Dynamic/testwindow.h b/sample/CustomFrame-Dynamic/testwindow.h index 40c7d03..cdba79e 100644 --- a/sample/CustomFrame-Dynamic/testwindow.h +++ b/sample/CustomFrame-Dynamic/testwindow.h @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # @@ -19,13 +19,14 @@ #include -#include "nmainwindow.h" +#include "qtitlebar.h" +#include "qcustomwindow.h" namespace Ui { class TestWindow; } -class TestWindow : public NMainWindow +class TestWindow : public QCustomWindow { Q_OBJECT diff --git a/sample/CustomFrame-Static/CustomFrameTestIn.pro b/sample/CustomFrame-Static/CustomFrameTestIn.pro index 76a6640..838876c 100644 --- a/sample/CustomFrame-Static/CustomFrameTestIn.pro +++ b/sample/CustomFrame-Static/CustomFrameTestIn.pro @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # diff --git a/sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp index 5447b1a..88611d2 100644 --- a/sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp +++ b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.cpp @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #include "qcustomwindow.h" QCustomWindow::QCustomWindow(QWidget *parent) : diff --git a/sample/CustomFrame-Static/customtitlebar/qcustomwindow.h b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.h index 20015d2..00d8909 100644 --- a/sample/CustomFrame-Static/customtitlebar/qcustomwindow.h +++ b/sample/CustomFrame-Static/customtitlebar/qcustomwindow.h @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #ifndef QCUSTOMWINDOW_H #define QCUSTOMWINDOW_H diff --git a/sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp b/sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp index 6b364c0..7cfb340 100644 --- a/sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp +++ b/sample/CustomFrame-Static/customtitlebar/qtitlebar.cpp @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #include "qtitlebar.h" QTitleBar::QTitleBar(QMainWindow *parent) : diff --git a/sample/CustomFrame-Static/customtitlebar/qtitlebar.h b/sample/CustomFrame-Static/customtitlebar/qtitlebar.h index e0b83c9..caff740 100644 --- a/sample/CustomFrame-Static/customtitlebar/qtitlebar.h +++ b/sample/CustomFrame-Static/customtitlebar/qtitlebar.h @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #ifndef QTITLEBAR_H #define QTITLEBAR_H diff --git a/sample/CustomFrame-Static/main.cpp b/sample/CustomFrame-Static/main.cpp index 153b157..6154b5e 100644 --- a/sample/CustomFrame-Static/main.cpp +++ b/sample/CustomFrame-Static/main.cpp @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # diff --git a/sample/CustomFrame-Static/testwindow.cpp b/sample/CustomFrame-Static/testwindow.cpp index d25cd0e..91fbf98 100644 --- a/sample/CustomFrame-Static/testwindow.cpp +++ b/sample/CustomFrame-Static/testwindow.cpp @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # diff --git a/sample/CustomFrame-Static/testwindow.h b/sample/CustomFrame-Static/testwindow.h index afe47f3..c20556c 100644 --- a/sample/CustomFrame-Static/testwindow.h +++ b/sample/CustomFrame-Static/testwindow.h @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # diff --git a/src/CustomTitlebar-Dynamic/CustomTitlebar.pro b/src/CustomTitlebar-Dynamic/CustomTitlebar.pro index 280c1ea..0879fb5 100644 --- a/src/CustomTitlebar-Dynamic/CustomTitlebar.pro +++ b/src/CustomTitlebar-Dynamic/CustomTitlebar.pro @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # @@ -18,11 +18,11 @@ QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -TARGET = CustomTitlebar +TARGET = QCustomTitlebar TEMPLATE = lib win32 { - VERSION = 1.0.0.4 + VERSION = 2.0.0.7 QMAKE_TARGET_COMPANY = Nintersoft QMAKE_TARGET_PRODUCT = Custom Titlebar @@ -32,11 +32,11 @@ win32 { CONFIG += skip_target_version_ext } else { - VERSION = 1.0.0 + VERSION = 2.0.0 CONFIG += unversioned_libname } -DEFINES += CUSTOMTITLEBAR_LIBRARY +DEFINES += QCUSTOMTITLEBAR_LIBRARY # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings @@ -51,16 +51,12 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ - titlebar.cpp \ - nmainwindow.cpp + qtitlebar.cpp \ + qcustomwindow.cpp HEADERS += \ - titlebar.h \ - nmainwindow.h - -FORMS += \ - titlebar.ui \ - nmainwindow.ui + qtitlebar.h \ + qcustomwindow.h unix { target.path = /usr/lib diff --git a/src/CustomTitlebar-Dynamic/nmainwindow.cpp b/src/CustomTitlebar-Dynamic/nmainwindow.cpp deleted file mode 100644 index 1eb3714..0000000 --- a/src/CustomTitlebar-Dynamic/nmainwindow.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#include "nmainwindow.h" -#include "ui_nmainwindow.h" - -NMainWindow::NMainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::NMainWindow), - RESIZE_LIMIT(2) -{ - ui->setupUi(this); - setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); - centralWidget()->installEventFilter(this); - ui->titleBar->installEventFilter(this); - ui->statusBar->installEventFilter(this); - - centralWidget()->setMouseTracking(true); - ui->titleBar->setMouseTracking(true); - ui->statusBar->setMouseTracking(true); - - setWindowTitle("Custom Window Border"); - locked = LockMoveType::None; - - ui->headerWidget->setTitleBarWidget(ui->titleBar); - if (ui->titleBar->mainWindow() != this) - ui->titleBar->setMainWindow(this); - - if (this->maximumSize() == this->minimumSize()) - setMaximizeButtonEnabled(false); - - connect(ui->titleBar, &TitleBar::closeRequest, this, &NMainWindow::close); -} - -NMainWindow::~NMainWindow() -{ - delete ui; -} - -void NMainWindow::setCustomWidgets(QWidget *newCentralWidget, QStatusBar *newStatusBar){ - setCustomStatusBar(newStatusBar); - setNewCentralWidget(newCentralWidget); -} - -void NMainWindow::setCustomStatusBar(QStatusBar *newStatusBar){ - if (!newStatusBar) return; - newStatusBar->installEventFilter(this); - newStatusBar->setMouseTracking(true); -} - -void NMainWindow::setNewCentralWidget(QWidget *newCentralWidget){ - if (!newCentralWidget) return; - newCentralWidget->installEventFilter(this); - newCentralWidget->setMouseTracking(true); - - if (newCentralWidget->layout()) - newCentralWidget->layout()->setContentsMargins(10, 0, 10, 0); -} - -void NMainWindow::setCloseButtonEnabled(bool enable){ - ui->titleBar->setCloseButtonEnabled(enable); -} - -void NMainWindow::setMaximizeButtonEnabled(bool enable){ - ui->titleBar->setMaximizeButtonEnabled(enable); -} - -void NMainWindow::setMinimizeButtonEnabled(bool enable){ - ui->titleBar->setMinimizeButtonEnabled(enable); -} - -void NMainWindow::setTitlebarStylesheet(const QString &styleSheet){ - ui->titleBar->setStyleSheet(styleSheet); -} - -QString NMainWindow::titlebarStylesheet() const { - return ui->titleBar->styleSheet(); -} - -void NMainWindow::closeEvent(QCloseEvent *event){ - if (event->isAccepted()) QMainWindow::closeEvent(event); - else event->ignore(); -} - -/* - * GUI Protected Methods (do not change them, unless it's really necessary) - */ - -void NMainWindow::mousePressEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - int x = event->x(), y = event->y(), bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; - if (x < RESIZE_LIMIT && y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topLeft(); - locked = LockMoveType::TopLeft; - } - else if (x < RESIZE_LIMIT && y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomLeft(); - locked = LockMoveType::BottomLeft; - } - else if (x > right && y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topRight(); - locked = LockMoveType::TopRight; - } - else if (x > right && y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomRight(); - locked = LockMoveType::BottomRight; - } - else if (x < RESIZE_LIMIT || y < RESIZE_LIMIT){ - posCursor = event->globalPos() - this->geometry().topLeft(); - locked = x < RESIZE_LIMIT ? LockMoveType::Left : LockMoveType::Top; - } - else if (x > right || y > bottom){ - posCursor = event->globalPos() - this->geometry().bottomRight(); - locked = x > right ? LockMoveType::Right : LockMoveType::Bottom; - } - event->accept(); - } -} - -void NMainWindow::undefMouseMoveEvent(QObject* object, QMouseEvent* event){ - if (locked != LockMoveType::None){ - switch (locked) { - case LockMoveType::TopLeft: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), event->globalPos().y() - posCursor.y()), - this->geometry().bottomRight())); - break; - case LockMoveType::TopRight: - this->setGeometry(QRect(QPoint(this->geometry().left(), event->globalPos().y() - posCursor.y()), - QPoint(event->globalPos().x() - posCursor.x(), this->geometry().bottom()))); - break; - case LockMoveType::BottomLeft: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), this->geometry().top()), - QPoint(this->geometry().right(), event->globalPos().y() - posCursor.y()))); - break; - case LockMoveType::BottomRight: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(event->globalPos().x() - posCursor.x(), event->globalPos().y() - posCursor.y()))); - break; - case LockMoveType::Left: - this->setGeometry(QRect(QPoint(event->globalPos().x() - posCursor.x(), this->geometry().top()), - this->geometry().bottomRight())); - break; - case LockMoveType::Right: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(event->globalPos().x() - posCursor.x(), this->geometry().bottom()))); - break; - case LockMoveType::Top: - this->setGeometry(QRect(QPoint(this->geometry().left(), event->globalPos().y() - posCursor.y()), - this->geometry().bottomRight())); - break; - default: - this->setGeometry(QRect(this->geometry().topLeft(), - QPoint(this->geometry().right(), event->globalPos().y() - posCursor.y()))); - break; - } - return; - } - - int x = event->x(), y = event->y(), right = this->width() - RESIZE_LIMIT; - if (object->objectName() == "statusBar"){ - if (x < RESIZE_LIMIT && y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeBDiagCursor)); - return; - } - else if (x > right && y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeFDiagCursor)); - return; - } - else if (y > (19 - RESIZE_LIMIT)){ - this->setCursor(QCursor(Qt::SizeVerCursor)); - return; - } - } - else if (object->objectName() == "titleBar"){ - if (x < RESIZE_LIMIT && y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeFDiagCursor)); - return; - } - if (x > right && y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeBDiagCursor)); - return; - } - else if (y < RESIZE_LIMIT){ - this->setCursor(QCursor(Qt::SizeVerCursor)); - return; - } - } - - this->setCursor(QCursor(x < RESIZE_LIMIT || x > right ? Qt::SizeHorCursor : Qt::ArrowCursor)); -} - -void NMainWindow::mouseReleaseEvent(QMouseEvent *event){ - locked = LockMoveType::None; - event->accept(); -} - -bool NMainWindow::eventFilter(QObject* object, QEvent* event) -{ - if(event->type() == QEvent::MouseMove) - undefMouseMoveEvent(object, static_cast(event)); - else if (event->type() == QEvent::MouseButtonPress && object->objectName() == "titleBar") - mousePressEvent(static_cast(event)); - return false; -} diff --git a/src/CustomTitlebar-Dynamic/nmainwindow.h b/src/CustomTitlebar-Dynamic/nmainwindow.h deleted file mode 100644 index 55ea5cb..0000000 --- a/src/CustomTitlebar-Dynamic/nmainwindow.h +++ /dev/null @@ -1,86 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#ifndef NMAINWINDOW_H -#define NMAINWINDOW_H - -#include - -#if defined(CUSTOMTITLEBAR_LIBRARY) -# define CUSTOMTITLEBARSHARED_EXPORT Q_DECL_EXPORT -#else -# define CUSTOMTITLEBARSHARED_EXPORT Q_DECL_IMPORT -#endif - -#include -#include -#include -#include - -#include "titlebar.h" - -namespace Ui { -class NMainWindow; -} - -class CUSTOMTITLEBARSHARED_EXPORT NMainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit NMainWindow(QWidget *parent = nullptr); - ~NMainWindow(); - - void setCustomWidgets(QWidget *newCentralWidget, QStatusBar *newStatusBar); - void setNewCentralWidget(QWidget *newCentralWidget); - void setCustomStatusBar(QStatusBar *newStatusBar); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - - void setTitlebarStylesheet(const QString &styleSheet); - QString titlebarStylesheet() const; - -protected: - void closeEvent(QCloseEvent *event); - - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void undefMouseMoveEvent(QObject *object, QMouseEvent* event); - bool eventFilter(QObject *watched, QEvent *event); - - enum LockMoveType{ - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight, - None - }; - -private: - Ui::NMainWindow *ui; - const int RESIZE_LIMIT; - - QPoint posCursor; - LockMoveType locked; -}; - -#endif // NMAINWINDOW_H diff --git a/src/CustomTitlebar-Dynamic/nmainwindow.ui b/src/CustomTitlebar-Dynamic/nmainwindow.ui deleted file mode 100644 index bdc7d03..0000000 --- a/src/CustomTitlebar-Dynamic/nmainwindow.ui +++ /dev/null @@ -1,144 +0,0 @@ - - - NMainWindow - - - - 0 - 0 - 400 - 300 - - - - ArrowCursor - - - NMainWindow - - - QWidget#titleBar{ - background-color: #FFF; - border-top: 1px solid orange; - border-left: 1px solid orange; - border-right: 1px solid orange; - color: #000; -} - -QDockWidget#headerWidget, QDockWidget#headerWidget::title{ - background-color: #FFF; - border-top: 1px solid orange; - border-left: 1px solid orange; - border-right: 1px solid orange; - color: #000; -} - -QWidget#centralWidget, QWidget#headerWidgetContents, QStatusBar{ - background-color: #FFF; - border-left: 1px solid orange; - border-right: 1px solid orange; -} - -QMainWindow::separator { - background-color: #FFF; - background: #FFF; - border-left: 1px solid orange; - border-right: 1px solid orange; - height: 0px; -} - -QStatusBar{ - border-bottom: 1px solid orange; -} - - - - - 10 - - - 0 - - - 10 - - - 0 - - - 0 - - - - - - - - 36 - 35 - - - - - 524287 - 35 - - - - QDockWidget::NoDockWidgetFeatures - - - Qt::TopDockWidgetArea - - - 4 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 35 - - - - - 16777215 - 35 - - - - - - - - - - - - TitleBar - QWidget -
titlebar.h
- 1 -
-
- - -
diff --git a/src/CustomTitlebar-Dynamic/qcustomwindow.cpp b/src/CustomTitlebar-Dynamic/qcustomwindow.cpp new file mode 100644 index 0000000..88611d2 --- /dev/null +++ b/src/CustomTitlebar-Dynamic/qcustomwindow.cpp @@ -0,0 +1,276 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + +#include "qcustomwindow.h" + +QCustomWindow::QCustomWindow(QWidget *parent) : + QMainWindow(parent), RESIZE_LIMIT(4) +{ + setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); + + this->setMinimumSize(QSize(256, 64)); + this->setStyleSheet(QStringLiteral( + "\nQCustomWindow {\n" + " background : white;\n" + " border : 1px solid orange;\n" + "}\n" + "QStatusBar {\n" + " background : white;\n" + " border-bottom: 1px solid orange;\n" + " border-left: 1px solid orange;\n" + " border-right: 1px solid orange;\n" + "}\n" + "QToolBar {\n" + " margin : 1px;\n" + "}" + )); + + this->m_titleBar = new QTitleBar(this); + connect(this->m_titleBar, &QTitleBar::requestClose, + this, &QCustomWindow::close); + connect(this->m_titleBar, &QTitleBar::requestMaximize, [this]{ + if (this->isMaximized()) this->showNormal(); + else this->showMaximized(); + }); + connect(this->m_titleBar, &QTitleBar::requestMinimize, + this, &QCustomWindow::showMinimized); + connect(this, &QMainWindow::windowTitleChanged, this->m_titleBar, + &QWidget::setWindowTitle); + + this->m_titleBarW = new QWidget(); + this->m_titleBarW->setMouseTracking(true); + this->m_titleBarW->installEventFilter(this); + + QVBoxLayout *titleLayout = new QVBoxLayout(); + titleLayout->setContentsMargins(6, 6, 6, 0); + titleLayout->setSpacing(0); + titleLayout->addWidget(this->m_titleBar); + + this->m_titleBarW->setLayout(titleLayout); + QMainWindow::setMenuWidget(this->m_titleBarW); + + this->m_leftBorder = generateBorder(Qt::LeftToolBarArea, Qt::Vertical); + this->m_rightBorder = generateBorder(Qt::RightToolBarArea, Qt::Vertical); + this->m_bottomBorder = generateBorder(Qt::BottomToolBarArea, Qt::Horizontal); + + this->m_menuBar = nullptr; + this->m_menuWidget = nullptr; +} + +QCustomWindow::~QCustomWindow(){ + delete this->m_titleBar; + delete this->m_leftBorder; + delete this->m_rightBorder; + delete this->m_bottomBorder; + + if (this->m_menuBar) delete this->m_menuBar; + else if (this->m_menuWidget) delete this->m_menuWidget; +} + +QToolBar *QCustomWindow::generateBorder(Qt::ToolBarArea area, + Qt::Orientation orientation){ + QToolBar *border = new QToolBar("___border___"); + border->setStyleSheet( + "\nQToolBar {\n" + " margin : 1px;\n" + " border : 0px;\n" + " background : transparent;\n" + "}" + ); + + if (orientation & Qt::Horizontal){ + border->setMinimumHeight(6); + border->setMaximumHeight(6); + } + else { + border->setMinimumWidth(6); + border->setMaximumWidth(6); + } + border->setMovable(false); + border->setFloatable(false); + border->setAllowedAreas(area); + border->setMouseTracking(true); + border->installEventFilter(this); + + this->addToolBar(area, border); + return border; +} + +QMenu * QCustomWindow::createPopupMenu(){ + QMenu *menu = QMainWindow::createPopupMenu(); + QList removal; + foreach (QAction *a, menu->actions()) + if (a->text() == "___border___") removal.append(a); + foreach (QAction *a, removal) menu->removeAction(a); + return menu; +} + +void QCustomWindow::setMenuBar(QMenuBar *menuBar){ + if (this->m_menuBar == menuBar) return; + + if (this->m_menuBar) { + if (this->m_menuBar != this->m_menuWidget && this->m_menuWidget){ + this->m_menuWidget->setParent(nullptr); + this->m_menuWidget->deleteLater(); + } + + this->m_menuBar->hide(); + this->m_menuBar->setParent(nullptr); + this->m_menuBar->deleteLater(); + } + + this->m_menuBar = menuBar; + this->m_menuWidget = qobject_cast(menuBar); + + if (menuBar){ + menuBar->setParent(this); + this->m_titleBarW->layout()->addWidget(menuBar); + } +} + +QMenuBar* QCustomWindow::menuBar() const{ + return this->m_menuBar; +} + +void QCustomWindow::setMenuWidget(QWidget *widget){ + if (this->m_menuWidget == widget) return; + + widget->setParent(this); + + if (this->m_menuWidget){ + this->m_menuWidget->hide(); + this->m_menuWidget->setParent(nullptr); + this->m_menuWidget->deleteLater(); + } + + this->m_menuBar = nullptr; + this->m_menuWidget = widget; + + if (this->m_menuWidget){ + this->m_menuWidget->setParent(this); + this->m_titleBarW->layout()->addWidget(widget); + } +} + +QWidget * QCustomWindow::menuWidget() const{ + return this->m_menuWidget; +} + +bool QCustomWindow::eventFilter(QObject*, QEvent *event){ + if (event->type() == QEvent::MouseMove) + customMouseMoveEvent(static_cast(event)); + else if (event->type() == QEvent::MouseButtonPress) + mousePressEvent(static_cast(event)); + return false; +} + +bool QCustomWindow::event(QEvent *event){ + if (event->type() == QEvent::ChildRemoved){ + QChildEvent *evt = static_cast(event); + if (evt->child()->isWidgetType()) evt->child()->removeEventFilter(this); + } + else if (event->type() == QEvent::ChildAdded){ + QChildEvent *evt = static_cast(event); + if (evt->child()->isWidgetType()){ + QWidget *child = qobject_cast(evt->child()); + + child->setMouseTracking(true); + child->installEventFilter(this); + + if (child->metaObject()->indexOfClassInfo("custom_obj_type") == -1){ + child->setStyleSheet(child->styleSheet() + + "\nQToolBar {\n" + " margin : 1px;\n" + " padding: 0px 6px 0px 6px;\n" + " border: 1px transparent solid;" + "}\n" + "QToolBar:top:first, QToolBar:bottom:first, QToolBar:left:first {\n" + " margin : 0px 0px 0px 6px;\n" + "}\n" + "QToolBar:top:only-one, QToolBar:bottom:only-one {\n" + " margin : 0px 6px 0px 6px;\n" + "}\n" + "QToolBar:top:last, QToolBar:bottom:last, QToolBar:right:last {\n" + " margin : 0px 6px 0px 0px;\n" + "}\n"); + } + } + } + return QMainWindow::event(event); +} + +void QCustomWindow::mousePressEvent(QMouseEvent *event){ + if (event->button() & Qt::LeftButton){ + int x = event->x(), y = event->y(); + int bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; + + QPoint posCursor = event->globalPos(); + Qt::Edges nFlags = {}; + if (x < RESIZE_LIMIT) { + nFlags |= Qt::LeftEdge; + posCursor.rx() -= this->x(); + } + if (y < RESIZE_LIMIT) { + nFlags |= Qt::TopEdge; + posCursor.ry() -= this->y(); + } + if (x > right) { + nFlags |= Qt::RightEdge; + posCursor.rx() -= (this->x() + this->width()); + } + if (y > bottom) { + nFlags |= Qt::BottomEdge; + posCursor.ry() -= (this->y() + this->height()); + } + this->m_lock = nFlags; + this->m_posCursor = posCursor; + } + QMainWindow::mousePressEvent(event); +} + +void QCustomWindow::mouseReleaseEvent(QMouseEvent *event){ + this->m_lock = {}; + this->unsetCursor(); + QMainWindow::mouseMoveEvent(event); +} + +void QCustomWindow::customMouseMoveEvent(QMouseEvent *event){ + if (this->m_lock){ + QPoint tL = this->geometry().topLeft(), bR = this->geometry().bottomRight(); + if (this->m_lock & Qt::TopEdge) tL.ry() = event->globalY() - this->m_posCursor.y(); + if (this->m_lock & Qt::BottomEdge) bR.ry() = event->globalY() - this->m_posCursor.y(); + if (this->m_lock & Qt::LeftEdge) tL.rx() = event->globalX() - this->m_posCursor.x(); + if (this->m_lock & Qt::RightEdge) bR.rx() = event->globalX() - this->m_posCursor.x(); + this->setGeometry(QRect(tL, bR)); + return; + } + + int x = event->globalX() - this->x(), y = event->globalY() - this->y(); + int bottom = this->height() - RESIZE_LIMIT, right = this->width() - RESIZE_LIMIT; + + if (x < RESIZE_LIMIT){ + if (y < RESIZE_LIMIT) this->setCursor(QCursor(Qt::SizeFDiagCursor)); + else if (y > bottom) this->setCursor(QCursor(Qt::SizeBDiagCursor)); + else this->setCursor(QCursor(Qt::SizeHorCursor)); + } + else if (x > right){ + if (y < RESIZE_LIMIT) this->setCursor(QCursor(Qt::SizeBDiagCursor)); + else if (y > bottom) this->setCursor(QCursor(Qt::SizeFDiagCursor)); + else this->setCursor(QCursor(Qt::SizeHorCursor)); + } + else if (y < RESIZE_LIMIT || y > bottom) this->setCursor(Qt::SizeVerCursor); + else this->unsetCursor(); +} diff --git a/src/CustomTitlebar-Dynamic/qcustomwindow.h b/src/CustomTitlebar-Dynamic/qcustomwindow.h new file mode 100644 index 0000000..78c62d8 --- /dev/null +++ b/src/CustomTitlebar-Dynamic/qcustomwindow.h @@ -0,0 +1,92 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + +#ifndef QCUSTOMWINDOW_H +#define QCUSTOMWINDOW_H + +#include + +#if defined(QCUSTOMTITLEBAR_LIBRARY) +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_EXPORT +#else +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_IMPORT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qtitlebar.h" + +class QCUSTOMTITLEBARSHARED_EXPORT QCustomWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit QCustomWindow(QWidget *parent = nullptr); + ~QCustomWindow() override; + + QMenu * createPopupMenu() override; + + void setMenuBar(QMenuBar *menuBar); + QMenuBar * menuBar() const; + + void setMenuWidget(QWidget *widget); + QWidget * menuWidget() const; + + inline QTitleBar& titleBar() const { return *this->m_titleBar; } + +protected: + const int RESIZE_LIMIT; + + bool event(QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + + void customMouseMoveEvent(QMouseEvent* event); + +private: + bool init; + + QWidget *m_titleBarW; + QWidget *m_menuWidget; + QMenuBar *m_menuBar; + QTitleBar *m_titleBar; + + QToolBar *m_leftBorder; + QToolBar *m_rightBorder; + QToolBar *m_bottomBorder; + + Qt::Edges m_lock; + QPoint m_posCursor; + + QToolBar * generateBorder(Qt::ToolBarArea area, Qt::Orientation orientation); +}; + +#endif // QCUSTOMWINDOW_H diff --git a/src/CustomTitlebar-Dynamic/qtitlebar.cpp b/src/CustomTitlebar-Dynamic/qtitlebar.cpp new file mode 100644 index 0000000..7cfb340 --- /dev/null +++ b/src/CustomTitlebar-Dynamic/qtitlebar.cpp @@ -0,0 +1,127 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + +#include "qtitlebar.h" + +QTitleBar::QTitleBar(QMainWindow *parent) : + QWidget(parent), FRAME_BUTTON_SIZE(24, 24) +{ + this->canMove = false; + this->maximizing = false; + this->m_frameButtons = QCustomAttrs::All; + + this->setStyleSheet(QStringLiteral( + "QPushButton {\n" + " border : 0.5px solid gray;\n" + " border-radius: 3px;\n" + " min-height: 20px;\n" + " margin: 1px;\n" + " background: white;\n" + " color: gray;\n" + "}\n" + "QPushButton::hover {\n" + " background: darkorange;\n" + " border : 0.5px solid darkorange;\n" + " color: white;\n" + "}\n" + "QPushButton::pressed, QPushButton::!enabled {\n" + " background: orange;\n" + " border : 0.5px solid orange;\n" + " color: white;\n" + "}\n" + "QTitleBar { background: white; }\n" + )); + + if (!parent) throw std::exception("Parent must be a QCustomWindow object (cannot be null)."); + this->m_parentWindow = parent; + + this->lbl_windowTitle.setText("QCustomWindow"); + this->lbl_windowTitle.setAlignment(Qt::AlignCenter); + + this->btn_close.setText("X"); + this->btn_maximize.setText("+"); + this->btn_minimize.setText("-"); + + this->btn_close.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_close.setMinimumSize(FRAME_BUTTON_SIZE); + this->btn_maximize.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_maximize.setMinimumSize(FRAME_BUTTON_SIZE); + this->btn_minimize.setMaximumSize(FRAME_BUTTON_SIZE); + this->btn_minimize.setMinimumSize(FRAME_BUTTON_SIZE); + + this->m_layout.addWidget(&this->lbl_windowTitle, 1); + this->m_layout.addWidget(&this->btn_minimize); + this->m_layout.addWidget(&this->btn_maximize); + this->m_layout.addWidget(&this->btn_close); + this->m_layout.setContentsMargins(0, 0, 0, 4); + this->m_layout.setSpacing(0); + + this->setLayout(&this->m_layout); + + connect(&this->btn_close, &QPushButton::clicked, [this]{ emit this->requestClose(); }); + connect(&this->btn_minimize, &QPushButton::clicked, [this]{ emit this->requestMinimize(); }); + connect(&this->btn_maximize, &QPushButton::clicked, [this]{ emit this->requestMaximize(); }); + + connect(this, &QWidget::windowTitleChanged, &this->lbl_windowTitle, &QLabel::setText); + + this->setMaximumHeight(35); + this->setMinimumHeight(35); +} + +void QTitleBar::setWindowButtons(QCustomAttrs::WindowButtons btns){ + this->m_frameButtons = btns; + this->btn_close.setVisible(btns & QCustomAttrs::Close); + this->btn_maximize.setVisible(btns & QCustomAttrs::Maximize); + this->btn_minimize.setVisible(btns & QCustomAttrs::Minimize); +} + +void QTitleBar::setWindowButtonEnabled(QCustomAttrs::WindowButton btn, bool enabled){ + if (btn & QCustomAttrs::Close) this->btn_close.setEnabled(enabled); + if (btn & QCustomAttrs::Maximize) this->btn_maximize.setEnabled(enabled); + if (btn & QCustomAttrs::Minimize) this->btn_minimize.setEnabled(enabled); +} + +void QTitleBar::paintEvent(QPaintEvent *event){ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} + +void QTitleBar::mousePressEvent(QMouseEvent *event){ + if (event->button() & Qt::LeftButton){ + this->canMove = (event->x() > 5 && event->y() > 5 && event->x() < (this->m_parentWindow->width() - 5)); + this->m_pCursor = event->globalPos() - this->m_parentWindow->geometry().topLeft(); + } + QWidget::mousePressEvent(event); +} + +void QTitleBar::mouseMoveEvent(QMouseEvent *event){ + if (!this->maximizing && canMove && event->buttons() & Qt::LeftButton + && !this->m_parentWindow->isMaximized()) this->m_parentWindow->move(event->globalPos() - m_pCursor); + this->maximizing = false; + QWidget::mouseMoveEvent(event); +} + +void QTitleBar::mouseDoubleClickEvent(QMouseEvent *event){ + if (m_frameButtons & QCustomAttrs::Maximize && btn_maximize.isEnabled() + && event->buttons() & Qt::LeftButton) { + this->maximizing = true; + emit requestMaximize(); + } + QWidget::mouseDoubleClickEvent(event); +} diff --git a/src/CustomTitlebar-Dynamic/qtitlebar.h b/src/CustomTitlebar-Dynamic/qtitlebar.h new file mode 100644 index 0000000..a1e12cb --- /dev/null +++ b/src/CustomTitlebar-Dynamic/qtitlebar.h @@ -0,0 +1,97 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + +#ifndef QTITLEBAR_H +#define QTITLEBAR_H + +#include + +#if defined(QCUSTOMTITLEBAR_LIBRARY) +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_EXPORT +#else +# define QCUSTOMTITLEBARSHARED_EXPORT Q_DECL_IMPORT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QCustomAttrs { + enum WindowButton { + Minimize = 0x01, + Maximize = 0x02, + Close = 0x04, + All = Minimize | Maximize | Close + }; + + Q_DECLARE_FLAGS(WindowButtons, WindowButton) + Q_DECLARE_OPERATORS_FOR_FLAGS(WindowButtons) +} + +class QCUSTOMTITLEBARSHARED_EXPORT QTitleBar : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QCustomAttrs::WindowButtons windowButtons READ windowButtons WRITE setWindowButtons) + Q_CLASSINFO("custom_obj_type", "QTitleBar") +public: + explicit QTitleBar(QMainWindow *parent = nullptr); + + void setWindowButtons(QCustomAttrs::WindowButtons btns); + inline QCustomAttrs::WindowButtons windowButtons() const { return this->m_frameButtons; } + + void setWindowButtonEnabled(QCustomAttrs::WindowButton btn, bool enabled = true); + +protected: + void paintEvent(QPaintEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + +private: + bool canMove; + bool maximizing; + + QPoint m_pCursor; + const QSize FRAME_BUTTON_SIZE; + + QWidget *m_parentWindow; + + QCustomAttrs::WindowButtons m_frameButtons; + + QLabel lbl_windowTitle; + QHBoxLayout m_layout; + QPushButton btn_minimize; + QPushButton btn_maximize; + QPushButton btn_close; + +signals: + void requestClose(); + void requestMaximize(); + void requestMinimize(); +}; + +#endif // QTITLEBAR_H diff --git a/src/CustomTitlebar-Dynamic/titlebar.cpp b/src/CustomTitlebar-Dynamic/titlebar.cpp deleted file mode 100644 index 831009a..0000000 --- a/src/CustomTitlebar-Dynamic/titlebar.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#include "titlebar.h" -#include "ui_titlebar.h" - -TitleBar::TitleBar(QWidget *parent) : - QWidget(parent), - ui(new Ui::TitleBar) -{ - ui->setupUi(this); - setMainWindow(parent->parentWidget() ? parent->parentWidget() : parent); - - canMove = false; -} - -TitleBar::~TitleBar() -{ - delete ui; -} - -void TitleBar::setMainWindow(QWidget *mainWindow){ - this->parent = mainWindow; - - disconnect(ui->btClose, &QPushButton::clicked, this, &TitleBar::onCloseRequest); - disconnect(ui->btMinimize, &QPushButton::clicked, this->parent, &QWidget::showMinimized); - disconnect(ui->btMaximize, &QPushButton::clicked, this, &TitleBar::maximizeParent); - - disconnect(this->parent, &QWidget::windowTitleChanged, ui->lblFormTitle, &QLabel::setText); - - connect(ui->btClose, &QPushButton::clicked, this, &TitleBar::onCloseRequest); - connect(ui->btMinimize, &QPushButton::clicked, this->parent, &QWidget::showMinimized); - connect(ui->btMaximize, &QPushButton::clicked, this, &TitleBar::maximizeParent); - - connect(this->parent, &QWidget::windowTitleChanged, ui->lblFormTitle, &QLabel::setText); -} - -void TitleBar::maximizeParent(){ - if (parent->isMaximized()) parent->showNormal(); - else parent->showMaximized(); -} - -void TitleBar::onCloseRequest(){ - emit closeRequest(); -} - -QWidget* TitleBar::mainWindow(){ - return this->parent; -} - -void TitleBar::setCloseButtonEnabled(bool enable){ - ui->btClose->setEnabled(enable); -} - -void TitleBar::setMaximizeButtonEnabled(bool enable){ - ui->btMaximize->setEnabled(enable); -} - -void TitleBar::setMinimizeButtonEnabled(bool enable){ - ui->btMinimize->setEnabled(enable); -} - -void TitleBar::mousePressEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - canMove = !(event->x() < 5 || event->x() > (parent->width() - 5) || event->y() < 5); - m_pCursor = event->globalPos() - parent->geometry().topLeft(); - event->accept(); - } -} - -void TitleBar::mouseMoveEvent(QMouseEvent *event) -{ - if (!canMove){ - event->accept(); - return; - } - if (event->buttons() & Qt::LeftButton){ - if (parent->isMaximized()){ - event->accept(); - return; - } - parent->move(event->globalPos() - m_pCursor); - event->accept(); - } -} - -void TitleBar::mouseDoubleClickEvent(QMouseEvent *event){ - if (!ui->btMaximize->isEnabled()){ - event->accept(); - return; - } - if (event->button() == Qt::LeftButton) maximizeParent(); -} - -void TitleBar::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} diff --git a/src/CustomTitlebar-Dynamic/titlebar.h b/src/CustomTitlebar-Dynamic/titlebar.h deleted file mode 100644 index 2c170d4..0000000 --- a/src/CustomTitlebar-Dynamic/titlebar.h +++ /dev/null @@ -1,65 +0,0 @@ -/*------------------------------------------------- -# -# Project developed by Nintersoft team -# Developer: Mauro Mascarenhas de Araújo -# Contact: mauro.mascarenhas@nintersoft.com -# Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 -# -# Licence notice -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# -------------------------------------------------- */ - -#ifndef TITLEBAR_H -#define TITLEBAR_H - -#include -#include -#include -#include - -namespace Ui { -class TitleBar; -} - -class TitleBar : public QWidget -{ - Q_OBJECT - -public: - explicit TitleBar(QWidget *parent = nullptr); - ~TitleBar(); - - void setMainWindow(QWidget *mainWindow); - QWidget* mainWindow(); - - void setCloseButtonEnabled(bool enable); - void setMaximizeButtonEnabled(bool enable); - void setMinimizeButtonEnabled(bool enable); - -private: - bool canMove; - - Ui::TitleBar *ui; - QWidget *parent; - QPoint m_pCursor; - -protected: - void paintEvent(QPaintEvent *); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseDoubleClickEvent(QMouseEvent *event); - -protected slots: - void maximizeParent(); - void onCloseRequest(); - -signals: - void closeRequest(); -}; - -#endif // TITLEBAR_H diff --git a/src/CustomTitlebar-Dynamic/titlebar.ui b/src/CustomTitlebar-Dynamic/titlebar.ui deleted file mode 100644 index c397b65..0000000 --- a/src/CustomTitlebar-Dynamic/titlebar.ui +++ /dev/null @@ -1,148 +0,0 @@ - - - TitleBar - - - - 0 - 0 - 721 - 35 - - - - - 0 - 35 - - - - - 16777215 - 35 - - - - Form - - - QPushButton{ - border : 0.5px solid gray; - border-radius: 3px; - min-height: 20px; - margin: 1px; - background: white; - color: gray; -} - -QPushButton::hover{ - background: darkorange; - border : 0.5px solid darkorange; - color: white; -} - -QPushButton::pressed, QPushButton::!enabled{ - background: orange; - border : 0.5px solid orange; - color: white; -} - -QWidget::lblFormTitle{ - background-color: white; -} - -QPushButton#lblFormTitle{ - background: transparent; - border-radius: 0px; - border: transparent; - color: black; -} - - - - 0 - - - 6 - - - 6 - - - 6 - - - 0 - - - - - frmTitle - - - Qt::AlignCenter - - - - - - - - 24 - 24 - - - - - 25 - 16777215 - - - - _ - - - - - - - - 24 - 24 - - - - - 25 - 16777215 - - - - + - - - - - - - - 24 - 24 - - - - - 30 - 16777215 - - - - x - - - - - - - - diff --git a/src/CustomTitlebar-Static/CustomTitlebar.pro b/src/CustomTitlebar-Static/CustomTitlebar.pro index 5e40d43..ef4e194 100644 --- a/src/CustomTitlebar-Static/CustomTitlebar.pro +++ b/src/CustomTitlebar-Static/CustomTitlebar.pro @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 25 of December of 2019 +# 25 of August of 2020 # # Licence notice # diff --git a/src/CustomTitlebar-Static/main.cpp b/src/CustomTitlebar-Static/main.cpp index 7cb0ebe..b9a6789 100644 --- a/src/CustomTitlebar-Static/main.cpp +++ b/src/CustomTitlebar-Static/main.cpp @@ -4,7 +4,7 @@ # Developer: Mauro Mascarenhas de Araújo # Contact: mauro.mascarenhas@nintersoft.com # Licence: Mozilla Public Licence 2.0 -# Date: 21 of December of 2019 +# Date: 25 of August of 2020 # # Licence notice # diff --git a/src/CustomTitlebar-Static/qcustomwindow.cpp b/src/CustomTitlebar-Static/qcustomwindow.cpp index 5447b1a..88611d2 100644 --- a/src/CustomTitlebar-Static/qcustomwindow.cpp +++ b/src/CustomTitlebar-Static/qcustomwindow.cpp @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #include "qcustomwindow.h" QCustomWindow::QCustomWindow(QWidget *parent) : diff --git a/src/CustomTitlebar-Static/qcustomwindow.h b/src/CustomTitlebar-Static/qcustomwindow.h index 20015d2..00d8909 100644 --- a/src/CustomTitlebar-Static/qcustomwindow.h +++ b/src/CustomTitlebar-Static/qcustomwindow.h @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #ifndef QCUSTOMWINDOW_H #define QCUSTOMWINDOW_H diff --git a/src/CustomTitlebar-Static/qtitlebar.cpp b/src/CustomTitlebar-Static/qtitlebar.cpp index 6b364c0..7cfb340 100644 --- a/src/CustomTitlebar-Static/qtitlebar.cpp +++ b/src/CustomTitlebar-Static/qtitlebar.cpp @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #include "qtitlebar.h" QTitleBar::QTitleBar(QMainWindow *parent) : diff --git a/src/CustomTitlebar-Static/qtitlebar.h b/src/CustomTitlebar-Static/qtitlebar.h index e0b83c9..caff740 100644 --- a/src/CustomTitlebar-Static/qtitlebar.h +++ b/src/CustomTitlebar-Static/qtitlebar.h @@ -1,3 +1,19 @@ +/*------------------------------------------------- +# +# Project developed by Nintersoft team +# Developer: Mauro Mascarenhas de Araújo +# Contact: mauro.mascarenhas@nintersoft.com +# Licence: Mozilla Public Licence 2.0 +# Date: 25 of August of 2020 +# +# Licence notice +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +------------------------------------------------- */ + #ifndef QTITLEBAR_H #define QTITLEBAR_H From 65cdfc986d31f80780ca9a071ae76ff7566b8635 Mon Sep 17 00:00:00 2001 From: Mauro Mascarenhas Date: Tue, 25 Aug 2020 21:57:32 -0300 Subject: [PATCH 3/4] Update README.md --- README.md | 58 ++++++++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 811e421..cb51ded 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,39 @@ # CustomTitlebar -CustomTitlebar é um projeto cuja classe principal (NMainWindow) deriva da classe QMainWindow da biblioteca Qt. -Ao derivar a janela principal da classe NMainWindow, você poderá desenvolver programas que usarão bordas personalizadas (não geradas pelo SO) - veja as capturas de tela para maiores detalhes. +CustomTitlebar is a project where the main class (QCustomWindow) inherits its properties from QMainWindow, part of Qt Library. +Inheriting the main window from the QCustomWindow class, will allow you to develop programs which will use customized borders (not generated by the OS) - have a look at the screenshots for major details. -## Suporte e Documentação +

+ +

-Você poderá obter o suporte e documentação para o uso deste projeto em sua [página no site da Nintersoft](https://www.nintersoft.com/portfolio/custom-titlebar/) ou ainda em sua [página de documentação](https://docwiki.nintersoft.com/custom-titlebar/). - -## Características: -- Redimensionável (100% funcional) -- Botões personalizáveis -- Atualização do título da janela (QMainWindow::setWindowTitle(QString)) funcionando; - -Código aberto CustomTitlebar -- Você poderá baixar e fazer suas modificações à vontade, conforme a licença de código Mozilla v2.0. -- A licença está disponível na raiz da pasta do projeto. - -Caso importe este repositório para seu Git, por favor coloque um link para nossa página e as referências cabíveis à nossa equipe, além de manter o README original. +## Support and Documentation -Atenciosamente, -Mauro Mascarenhas - Nintersoft. +Docs elaboration is still in progress :( ... +Should you have any doubts, do not hesitate in contacting me through [my email](mailto:mauro.mascarenhas@nintersoft.com). -## EN +## Features: -# CustomTitlebar +- Customized stylesheet (light theme); +- Resizeable (100% functional); +- Window title update (QMainWindow::setWindowTitle(QString)) working properly; +- Better performance (when compared to previous versions); +- QTabWidgets and QDockWidgets working properly; +- QMenuBar/menu widget can be through QCustomWindow methods (DO NOT CALL IT THROUGH ITS PARENT (QMAINWINDOW) METHODS); +- Available as a Dynamically Linked Library (DLL). -CustomTitlebar is a project where the main class (NMainWindow) inherits its properties from QMainWindow, which is part of Qt Library. -Inheriting the main window from the NMainWindow class, will allow you to develop programs which will use customized borders (not generated by the OS) - have a look at the screenshots for major details. +## Limitations: -## Support and Documentation +- It is not recommended to change QToolBar's stylesheet (after it has been added to the QCustomWindow's layout), since the auto-generated one plays and important role in the layout disposition; +- Setting the menu bar/widget through QMainWindow's methods will possibly crash the application (so, it is not possible to generate it through Qt Designer). Should you do that, the window's titlebar is going to be removed and every reference to it is going to break (since the widget gets deleted); -You are able to get the proper support and documentation in order to use this project on its [page at Nintersoft's website](https://www.nintersoft.com/en/portfolio/custom-titlebar/) or yet on its [documentation page](https://docwiki.nintersoft.com/en/custom-titlebar/). +## Distribution (available versions): -## Features: -- Resizeable (100% functional) -- BotõesCustomizable buttons -- Window title update (QMainWindow::setWindowTitle(QString)) working properly; +-[X] DLL + LIB + headers; +-[X] Stactic linking/source files; +-[ ] Qt Creator/Designer plugin; ## CustomTitlebar open source code -- You can download and make some modifications in the source code, according to Mozilla Public Licence v2.0. -- The licence is available in the root of the project folder. - -If you clone this directory into yours, please insert a linkback to our webpage and the correct references to our team, also leaving the original README in your repository. -Graciously, -Mauro Mascarenhas - Nintersoft. +- You can download and make modifications in the source code, accordingly to Mozilla Public Licence v2.0 terms. +- The licence is available in the root of the project folder and must be always shipped with it. \ No newline at end of file From 59c2d82350db498bd6afe5d5db4bb65f8b64231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauro=20Mascarenhas=20de=20Ara=C3=BAjo?= Date: Tue, 25 Aug 2020 21:59:17 -0300 Subject: [PATCH 4/4] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cb51ded..558e3a6 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,11 @@ Should you have any doubts, do not hesitate in contacting me through [my email]( ## Distribution (available versions): --[X] DLL + LIB + headers; --[X] Stactic linking/source files; --[ ] Qt Creator/Designer plugin; +- [x] DLL + LIB + headers; +- [x] Stactic linking/source files; +- [ ] Qt Creator/Designer plugin; ## CustomTitlebar open source code - You can download and make modifications in the source code, accordingly to Mozilla Public Licence v2.0 terms. -- The licence is available in the root of the project folder and must be always shipped with it. \ No newline at end of file +- The licence is available in the root of the project folder and must be always shipped with it.