После многих дней проб и ошибок я выполнил модифицированную версию кода предыдущих пользователей по ссылке, указанной ранее.Этот класс Titlebar
может быть присоединен к любому большему QWidget
, который будет действовать как область всего окна.По сути, вам нужно создать QWidget
с QVBoxLayout
.Затем создайте еще один QWidget
, который будет содержать фактическое содержимое окна, и просто установите весь внешний родительский элемент QWidget
в качестве этого Titlebar's
родительского элемента.Это позволит вам свободно стилизовать Titlebar
и добавлять любые кнопки, значки, стили, которые вы пожелаете, независимо от вашей операционной системы.
#ifndef TITLEBAR_H
#define TITLEBAR_H
#include "ViewerItem.h"
#include <QWidget>
#include <QHBoxLayout>
#include <QPushButton>
#include <QPointer>
#include <QStyle>
#include <QLabel>
#include <QPainter>
#include <QPen>
#include <QMouseEvent>
#include <QEvent>
#include <QApplication>
#include <QRubberBand>
#include <QEvent>
#include <QPoint>
#include <QResizeEvent>
#include <QObject>
#include <QDebug>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
class ViewerItem;
class TitleBar : public QWidget{
Q_OBJECT
public:
TitleBar(QString title = "default", QWidget *parent = nullptr);
void setWindowTitle(const QString& title);
enum Edge{
None = 0x0,
Left = 0x1,
Top = 0x2,
Right = 0x4,
Bottom = 0x8,
TopLeft = 0x10,
TopRight = 0x20,
BottomLeft = 0x40,
BottomRight = 0x80,
};
Q_ENUM(Edge);
Q_DECLARE_FLAGS(Edges, Edge);
void setBorderWidth(int);
int borderWidth() const;
ViewerItem *_parent = nullptr;
QRubberBand *_rubberband = nullptr;
bool _cursorchanged;
bool _leftButtonPressed;
Edges* _mousePress;
Edges* _mouseMove;
int _borderWidth;
bool _dragStart = false;
Qt::CursorShape _prevCursorShape = Qt::ArrowCursor;
protected slots:
void paintEvent(QPaintEvent* event);
protected:
bool eventFilter(QObject *o, QEvent *e) override;
void mouseHover(QHoverEvent*);
void mouseLeave(QHoverEvent *);
void mousePress(QMouseEvent*);
void mouseRelease(QMouseEvent*);
void mouseMove(QMouseEvent*);
void updateCursorShape(const QPoint &);
void calculateCursorPosition(const QPoint &, const QRect &, Edges *);
private:
QPointer<QStyle> style;
QIcon closeIcon;
QIcon maxIcon;
QIcon minIcon;
QPointer<QLabel> titleLabel;
QPointer<QPushButton> minimizeButton;
QPointer<QPushButton> maximizeButton;
QPointer<QPushButton> closeButton;
QPointer<QVBoxLayout> mainVLayout;
QPointer<QHBoxLayout> mainHLayout;
QPointer<QHBoxLayout> leftHLayout;
QPointer<QHBoxLayout> rightHLayout;
QPoint currentPos;
QPointer<QGraphicsView> view;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(TitleBar::Edges);
#endif // TITLEBAR_H
#include "Titlebar.h"
TitleBar::TitleBar(QString title, QWidget *parent) :
QWidget(parent),
_parent(qobject_cast<ViewerItem*>(parent)),
_cursorchanged(false),
_leftButtonPressed(false),
_borderWidth(5){
titleLabel = new QLabel;
minimizeButton = new QPushButton;
maximizeButton = new QPushButton;
closeButton = new QPushButton;
mainVLayout = new QVBoxLayout;
mainHLayout = new QHBoxLayout;
leftHLayout = new QHBoxLayout;
rightHLayout = new QHBoxLayout;
leftHLayout->addWidget(titleLabel);
rightHLayout->addWidget(minimizeButton);
rightHLayout->addWidget(maximizeButton);
rightHLayout->addWidget(closeButton);
mainHLayout->addLayout(leftHLayout);
mainHLayout->addLayout(rightHLayout);
mainVLayout->setAlignment(Qt::AlignTop);
leftHLayout->setAlignment(Qt::AlignLeft);
rightHLayout->setAlignment(Qt::AlignRight);
mainVLayout->setContentsMargins(5,2.5,5,0);
mainVLayout->addLayout(mainHLayout);
setLayout(mainVLayout);
setWindowTitle(title);
style = qApp->style();
closeIcon = style->standardIcon(QStyle::SP_TitleBarCloseButton);
maxIcon = style->standardIcon(QStyle::SP_TitleBarMaxButton);
minIcon = style->standardIcon(QStyle::SP_TitleBarMinButton);
minimizeButton->setIcon(minIcon);
maximizeButton->setIcon(maxIcon);
closeButton->setIcon(closeIcon);
minimizeButton->setMinimumWidth(30);
maximizeButton->setMinimumWidth(30);
closeButton->setMinimumWidth(30);
titleLabel->setFixedHeight(20);
minimizeButton->setFixedHeight(20);
maximizeButton->setFixedHeight(20);
closeButton->setFixedHeight(20);
this->setFixedHeight(25);
this->setMinimumWidth(90);
//_parent->setMouseTracking(true);
_parent->setWindowFlags(Qt::FramelessWindowHint);
_parent->setAttribute(Qt::WA_Hover);
_parent->installEventFilter(this);
_rubberband = new QRubberBand(QRubberBand::Rectangle);
_mousePress = new Edges(Edge::None);
_mouseMove = new Edges(Edge::None);
}
void TitleBar::paintEvent(QPaintEvent *event){
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRect(QRectF(0, 0, width(), height()));
QPen pen(Qt::black, 1);
p.setPen(pen);
p.fillPath(path, Qt::white);
p.drawPath(path);
QWidget::paintEvent(event);
}
void TitleBar::setWindowTitle(const QString& title){
this->titleLabel->setText(title);
QWidget::setWindowTitle(title);
}
bool TitleBar::eventFilter(QObject *o, QEvent*e) {
if (e->type() == QEvent::MouseMove ||
e->type() == QEvent::HoverMove ||
e->type() == QEvent::Leave ||
e->type() == QEvent::MouseButtonPress ||
e->type() == QEvent::MouseButtonRelease) {
switch (e->type()) {
case QEvent::MouseMove:
mouseMove(static_cast<QMouseEvent*>(e));
return true;
break;
case QEvent::HoverMove:
mouseHover(static_cast<QHoverEvent*>(e));
return true;
break;
case QEvent::Leave: //We're actually leaving the widget after we go out of the geometry + borderWidth
mouseLeave(static_cast<QHoverEvent*>(e));
return true;
break;
case QEvent::MouseButtonPress:
mousePress(static_cast<QMouseEvent*>(e));
return true;
break;
case QEvent::MouseButtonRelease:
mouseRelease(static_cast<QMouseEvent*>(e));
return true;
break;
default:
//e->accept();
return true;
break;
}
} else {
return _parent->eventFilter(o, e);
}
}
void TitleBar::mouseHover(QHoverEvent *e) {
//if this parent object is in a container we need to mapToParent, not mapToGlobal like the original implementer's code
updateCursorShape(_parent->mapToParent(e->pos()));
//We need to make the bar transparent because it messes with the leave event of the widget "behind" it for the top, top left, and top right cursors
//Only re-enable it when we want to for example, double click maximize or click the buttons
//This will be determined if we're within the the bar's bounding rect minus the border value, in other words the cursor should be defaulted
//This will prevent accidental drag events also
if(this->geometry().marginsRemoved(QMargins(borderWidth(),borderWidth(),borderWidth(), 0)).contains(e->pos())){
setAttribute(Qt::WA_TransparentForMouseEvents, false);
}else{
setAttribute(Qt::WA_TransparentForMouseEvents, true);
}
e->accept();
}
void TitleBar::mouseLeave(QHoverEvent *e) {
if (!_leftButtonPressed) {
*_mousePress = None;
*_mouseMove = None;
QApplication::restoreOverrideCursor();
_cursorchanged = false;
_prevCursorShape = Qt::ArrowCursor;
}
QWidget::leaveEvent(e);
}
void TitleBar::mousePress(QMouseEvent *e) {
if(e->button() & Qt::LeftButton){
_leftButtonPressed = true;
currentPos = e->pos();
calculateCursorPosition(e->pos(), _parent->geometry().marginsRemoved(QMargins(_borderWidth, _borderWidth, _borderWidth, _borderWidth)), _mousePress);
if (!_mouseMove->testFlag(Edge::None)) {
_rubberband->setGeometry(_parent->geometry());
}
//This area is ONLY on the title bar itself and determines repositioning of the parent
//We're actually selecting the parent, not the bar, but it looks and feels like we are using the bar
if(this->geometry().marginsRemoved(QMargins(borderWidth(),borderWidth() + 1,borderWidth(),0)).contains(e->pos())){
_dragStart = true;
_cursorchanged = false;
}
}
e->accept();
}
void TitleBar::mouseRelease(QMouseEvent *e) {
if (e->button() & Qt::LeftButton) {
_leftButtonPressed = false;
_dragStart = false;
}
e->accept();
}
void TitleBar::mouseMove(QMouseEvent *e) {
if (_leftButtonPressed) {
//For dragging the parent by its titlebar
if (_dragStart) {
QPoint diff = e->pos() - currentPos;
_parent->move(_parent->pos() + diff);
return;
}
//For determining how to resize if we have an edge
if (!_mouseMove->testFlag(Edge::None)) {
int left = _rubberband->geometry().left();
int top = _rubberband->geometry().top();
int right = _rubberband->geometry().right();
int bottom = _rubberband->geometry().bottom();
switch (*_mouseMove) {
case Edge::Top:
top = e->pos().y();
if(_parent->height() - top <= _parent->minimumHeight()) top = 0;
_parent->resize(QSize(_parent->width(), _parent->height() - top));
_rubberband->resize(QSize(_parent->width(), _parent->height() - top));
_parent->move(QPoint(_parent->x(), _parent->y() + top));
_rubberband->move(QPoint(_parent->x(), _parent->y() + top));
break;
case Edge::Bottom: //Good
bottom = e->pos().y();
if(bottom > _parent->height()){ //growing down
_parent->resize(QSize(_parent->width(), _parent->height() + (bottom - _parent->height())));
_rubberband->resize(QSize(_parent->width(), _parent->height() + (bottom - _parent->height())));
}else{ //shrinking up
_parent->resize(QSize(_parent->width(), _parent->height() - (_parent->height() - bottom)));
_rubberband->resize(QSize(_parent->width(), _parent->height() - (_parent->height() - bottom)));
}
break;
case Edge::Left: //Good
left = e->pos().x();
if(left <= 0){ //Dragging left
if(_parent->width() - left <= _parent->minimumWidth()) left = 0;
_parent->resize(QSize(_parent->width() - left, _parent->height()));
_rubberband->resize(QSize(_parent->width() - left, _parent->height()));
_parent->move(QPoint(_parent->x() + left, _parent->y()));
_rubberband->move(QPoint(_parent->x() + left, _parent->y()));
}else{ //dragging right
if(_parent->width() - left <= _parent->minimumWidth()) left = 0;
_parent->resize(QSize(_parent->width() - left, _parent->height()));
_rubberband->resize(QSize(_parent->width() - left, _parent->height()));
_parent->move(QPoint(_parent->x() + left, _parent->y()));
_rubberband->move(QPoint(_parent->x() + left, _parent->y()));
}
break;
case Edge::Right:
right = e->pos().x();
if(right >= 0){ //Dragging right
_parent->resize(QSize(right, _parent->height()));
_rubberband->resize(QSize(right, _parent->height()));
if(_parent->width() - right <= _parent->minimumWidth()) break;
_parent->move(QPoint(_parent->x() + (_parent->width() - right), _parent->y()));
_rubberband->move(QPoint(_parent->x() + (_parent->width() - right), _parent->y()));
}
break;
case Edge::TopLeft:
top = e->pos().y();
left = e->pos().x();
if(_parent->height() - top <= _parent->minimumHeight()) top = 0;
if(_parent->width() - left <= _parent->minimumWidth()) left = 0;
_parent->resize(QSize(_parent->width() - left, _parent->height() - top));
_rubberband->resize(QSize(_parent->width() - left, _parent->height() - top));
_parent->move(QPoint(_parent->x() + left, _parent->y() + top));
_rubberband->move(QPoint(_parent->x() + left, _parent->y() + top));
break;
case Edge::TopRight:
right = e->pos().x();
top = e->pos().y();
if(_parent->height() - top <= _parent->minimumHeight()) top = 0;
_parent->resize(QSize(_parent->width(), _parent->height() - top));
_rubberband->resize(QSize(_parent->width(), _parent->height() - top));
_parent->move(QPoint(_parent->x(), _parent->y() + top));
_rubberband->move(QPoint(_parent->x(), _parent->y() + top));
if(right >= 0){ //Dragging right
_parent->resize(QSize(right, _parent->height()));
_rubberband->resize(QSize(right, _parent->height()));
if(_parent->width() - right <= _parent->minimumWidth()) break;
_parent->move(QPoint(_parent->x() + (_parent->width() - right), _parent->y()));
_rubberband->move(QPoint(_parent->x() + (_parent->width() - right), _parent->y()));
}
break;
case Edge::BottomLeft:
bottom = e->pos().y();
left = e->pos().x();
if(bottom > _parent->height()){ //growing down
_parent->resize(QSize(_parent->width(), _parent->height() + (bottom - _parent->height())));
_rubberband->resize(QSize(_parent->width(), _parent->height() + (bottom - _parent->height())));
}else{ //shrinking up
_parent->resize(QSize(_parent->width(), _parent->height() - (_parent->height() - bottom)));
_rubberband->resize(QSize(_parent->width(), _parent->height() - (_parent->height() - bottom)));
}
if(left <= 0){ //Dragging left
if(_parent->width() - left <= _parent->minimumWidth()) left = 0;
_parent->resize(QSize(_parent->width() - left, _parent->height()));
_rubberband->resize(QSize(_parent->width() - left, _parent->height()));
_parent->move(QPoint(_parent->x() + left, _parent->y()));
_rubberband->move(QPoint(_parent->x() + left, _parent->y()));
}else{ //dragging right
if(_parent->width() - left <= _parent->minimumWidth()) left = 0;
_parent->resize(QSize(_parent->width() - left, _parent->height()));
_rubberband->resize(QSize(_parent->width() - left, _parent->height()));
_parent->move(QPoint(_parent->x() + left, _parent->y()));
_rubberband->move(QPoint(_parent->x() + left, _parent->y()));
}
break;
case Edge::BottomRight:
bottom = e->pos().y();
right = e->pos().x();
if(bottom > _parent->height()){ //growing down
_parent->resize(QSize(_parent->width(), _parent->height() + (bottom - _parent->height())));
_rubberband->resize(QSize(_parent->width(), _parent->height() + (bottom - _parent->height())));
}else{ //shrinking up
_parent->resize(QSize(_parent->width(), _parent->height() - (_parent->height() - bottom)));
_rubberband->resize(QSize(_parent->width(), _parent->height() - (_parent->height() - bottom)));
}
if(right >= 0){ //Dragging right
_parent->resize(QSize(right, _parent->height()));
_rubberband->resize(QSize(right, _parent->height()));
if(_parent->width() - right <= _parent->minimumWidth()) break;
_parent->move(QPoint(_parent->x() + (_parent->width() - right), _parent->y()));
_rubberband->move(QPoint(_parent->x() + (_parent->width() - right), _parent->y()));
}
break;
default:
break;
}
}
}
e->accept();
}
void TitleBar::updateCursorShape(const QPoint &pos) {
if (_parent->isFullScreen() || _parent->isMaximized()) {
if (_cursorchanged) {
QApplication::restoreOverrideCursor();
}
return;
}
if (!_leftButtonPressed) {
calculateCursorPosition(pos, _parent->geometry(), _mouseMove);
_cursorchanged = true;
if (_mouseMove->testFlag(Edge::Top) || _mouseMove->testFlag(Edge::Bottom)) {
if(_prevCursorShape != Qt::SizeVerCursor){
QApplication::setOverrideCursor(Qt::SizeVerCursor);
}
_prevCursorShape = Qt::SizeVerCursor;
} else if (_mouseMove->testFlag(Edge::Left) || _mouseMove->testFlag(Edge::Right)) {
if(_prevCursorShape != Qt::SizeHorCursor){
QApplication::setOverrideCursor(Qt::SizeHorCursor);
}
_prevCursorShape = Qt::SizeHorCursor;
} else if (_mouseMove->testFlag(Edge::TopLeft) || _mouseMove->testFlag(Edge::BottomRight)) {
if(_prevCursorShape != Qt::SizeFDiagCursor){
QApplication::setOverrideCursor(Qt::SizeFDiagCursor);
}
_prevCursorShape = Qt::SizeFDiagCursor;
} else if (_mouseMove->testFlag(Edge::TopRight) || _mouseMove->testFlag(Edge::BottomLeft)) {
if(_prevCursorShape != Qt::SizeBDiagCursor){
QApplication::setOverrideCursor(Qt::SizeBDiagCursor);
}
_prevCursorShape = Qt::SizeBDiagCursor;
} else if (_cursorchanged && _mouseMove->testFlag(Edge::None)) {
QApplication::restoreOverrideCursor();
_cursorchanged = false;
_prevCursorShape = Qt::ArrowCursor;
}
}
}
void TitleBar::calculateCursorPosition(const QPoint &pos, const QRect &framerect, Edges* _edge) {
bool onLeft = pos.x() >= framerect.x() + _borderWidth && pos.x() <= framerect.x() + (_borderWidth * 2) &&
pos.y() <= framerect.y() + framerect.height() - (_borderWidth * 4) && pos.y() >= framerect.y() + (_borderWidth * 4);
bool onRight = pos.x() >= framerect.x() + framerect.width() - (_borderWidth * 2) && pos.x() <= framerect.x() + framerect.width() - (_borderWidth) &&
pos.y() >= framerect.y() + (_borderWidth * 4) && pos.y() <= framerect.y() + framerect.height() - (_borderWidth * 4);
bool onBottom = pos.x() >= framerect.x() + (_borderWidth * 4) && pos.x() <= framerect.x() + framerect.width() - (_borderWidth * 4) &&
pos.y() >= framerect.y() + framerect.height() - (_borderWidth * 2) && pos.y() <= framerect.y() + framerect.height() - _borderWidth;
bool onTop = pos.x() >= framerect.x() + (_borderWidth * 4) && pos.x() <= framerect.x() + framerect.width() - (_borderWidth * 4) &&
pos.y() >= framerect.y() + _borderWidth && pos.y() <= framerect.y() + (_borderWidth * 2);
bool onBottomLeft = pos.x() <= framerect.x() + (_borderWidth * 3) && pos.x() >= framerect.x() + _borderWidth &&
pos.y() <= framerect.y() + framerect.height() -_borderWidth && pos.y() >= framerect.y() + framerect.height() - (_borderWidth * 4);
bool onBottomRight = pos.x() >= framerect.x() + framerect.width() - (_borderWidth * 4) && pos.x() <= framerect.x() + framerect.width() - _borderWidth &&
pos.y() >= framerect.y() + framerect.height() - (_borderWidth * 4) && pos.y() <= framerect.y() + framerect.height() - _borderWidth;
bool onTopRight = pos.x() >= framerect.x() + framerect.width() - (_borderWidth * 4) && pos.x() <= framerect.x() + framerect.width() - _borderWidth &&
pos.y() >= framerect.y() + _borderWidth && pos.y() <= framerect.y() + (_borderWidth * 4);
bool onTopLeft = pos.x() >= framerect.x() - _borderWidth && pos.x() <= framerect.x() + (_borderWidth * 4) &&
pos.y() >= framerect.y() + _borderWidth && pos.y() <= framerect.y() + (_borderWidth * 4);
if (onLeft) {
*_edge = Left;
//qDebug() << "Left";
} else if (onRight) {
*_edge = Right;
//qDebug() << "Right";
} else if (onBottom) {
*_edge = Bottom;
//qDebug() << "bottom";
} else if (onTop) {
*_edge = Top;
//qDebug() << "top";
} else if (onBottomLeft) {
*_edge = BottomLeft;
//qDebug() << "bottom left";
} else if (onBottomRight) {
*_edge = BottomRight;
//qDebug() << "Bottom right";
} else if (onTopRight) {
*_edge = TopRight;
//qDebug() << "Top right";
} else if (onTopLeft) {
*_edge = TopLeft;
//qDebug() << "Top left";
} else {
*_edge = None;
//qDebug() << "None";
}
}
void TitleBar::setBorderWidth(int borderWidth) {
_borderWidth = borderWidth;
}
int TitleBar::borderWidth() const {
return _borderWidth;
}