Make a frameless QWidget resizable with QRubberBand ?
-
Hi
Good work.
So for top/left what happens? -
Ok
First. good job. Seem to work great.
I can see that dragging up seems not to work but
did not spot anything looking at code.Is the code complete?
I can run it if I put in a default project? -
@IMAN4K
Yep. Besides some #include it ran.The reason up dont work is that it adjust the window the wrong way
(y++) so when u drag up, it goes down 1 pixel and then mouse is outside and the drag stops.Not sure where the actual error is yet. Seems to be need minus and not plus
for drag that way. -
Thanks @mrjj for all answers.
You were right calculating was mistake
I just used an inner space for calculating mouse area but you also need to consider an outer area (the green border) :
https://onedrive.live.com/redir?resid=8882D1E3BC0F61AB!7402&authkey=!ACT6umOQeAEcN7A&v=3&ithint=photo%2Cjpg
Here for any one else :frameless.h
:#include <QtWidgets/QWidget> #include <QtWidgets/QRubberBand> #include <QtCore/QObject> #include <QtCore/QEvent> #include <QtCore/QRect> #include <QtCore/QPoint> #include <QtCore/Qt> #include <QtGui/QHoverEvent> #include <QtGui/QMouseEvent> class FrameLess : public QObject { public: 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); FrameLess(QWidget *parent); void setBorderWidth(int); int borderWidth() const; QWidget *_parent = nullptr; QRubberBand *_rubberband = nullptr; bool _cursorchanged; bool _leftButtonPressed; Edges _mousePress = Edge::None; Edges _mouseMove = Edge::None; int _borderWidth; QPoint _dragPos; bool _dragStart = false; protected: bool eventFilter(QObject *o, QEvent *e) override; void mouseHover(QHoverEvent*); void mouseLeave(QEvent*); void mousePress(QMouseEvent*); void mouseRealese(QMouseEvent*); void mouseMove(QMouseEvent*); void updateCursorShape(const QPoint &); void calculateCursorPosition(const QPoint &, const QRect &, Edges &); }; Q_DECLARE_OPERATORS_FOR_FLAGS(FrameLess::Edges);
frameless.cpp
:#include "FrameLess.h" FrameLess::FrameLess(QWidget *parent) : _parent(parent), _cursorchanged(false), _leftButtonPressed(false), _borderWidth(5), _dragPos(QPoint()) { _parent->setMouseTracking(true); _parent->setWindowFlags(Qt::FramelessWindowHint); _parent->setAttribute(Qt::WA_Hover); _parent->installEventFilter(this); _rubberband = new QRubberBand(QRubberBand::Rectangle); } bool FrameLess::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: mouseLeave(e); return true; break; case QEvent::MouseButtonPress: mousePress(static_cast<QMouseEvent*>(e)); return true; break; case QEvent::MouseButtonRelease: mouseRealese(static_cast<QMouseEvent*>(e)); return true; break; } } else { return _parent->eventFilter(o, e); } } void FrameLess::mouseHover(QHoverEvent *e) { updateCursorShape(_parent->mapToGlobal(e->pos())); } void FrameLess::mouseLeave(QEvent *e) { if (!_leftButtonPressed) { _parent->unsetCursor(); } } void FrameLess::mousePress(QMouseEvent *e) { if (e->button() & Qt::LeftButton) { _leftButtonPressed = true; calculateCursorPosition(e->globalPos(), _parent->frameGeometry(), _mousePress); if (!_mousePress.testFlag(Edge::None)) { _rubberband->setGeometry(_parent->frameGeometry()); } if (_parent->rect().marginsRemoved(QMargins(borderWidth(),borderWidth(),borderWidth(),borderWidth())).contains(e->pos())) { _dragStart = true; _dragPos = e->pos(); } } } void FrameLess::mouseRealese(QMouseEvent *e) { if (e->button() & Qt::LeftButton) { _leftButtonPressed = false; _dragStart = false; } } void FrameLess::mouseMove(QMouseEvent *e) { if (_leftButtonPressed) { if (_dragStart) { _parent->move(_parent->frameGeometry().topLeft() + (e->pos() - _dragPos)); } if (!_mousePress.testFlag(Edge::None)) { int left = _rubberband->frameGeometry().left(); int top = _rubberband->frameGeometry().top(); int right = _rubberband->frameGeometry().right(); int bottom = _rubberband->frameGeometry().bottom(); switch (_mousePress) { case Edge::Top: top = e->globalPos().y(); break; case Edge::Bottom: bottom = e->globalPos().y(); break; case Edge::Left: left = e->globalPos().x(); break; case Edge::Right: right = e->globalPos().x(); break; case Edge::TopLeft: top = e->globalPos().y(); left = e->globalPos().x(); break; case Edge::TopRight: right = e->globalPos().x(); top = e->globalPos().y(); break; case Edge::BottomLeft: bottom = e->globalPos().y(); left = e->globalPos().x(); break; case Edge::BottomRight: bottom = e->globalPos().y(); right = e->globalPos().x(); break; } QRect newRect(QPoint(left, top), QPoint(right, bottom)); if (newRect.width() < _parent->minimumWidth()) { left = _parent->frameGeometry().x(); } else if (newRect.height() < _parent->minimumHeight()) { top = _parent->frameGeometry().y(); } _parent->setGeometry(QRect(QPoint(left, top), QPoint(right, bottom))); _rubberband->setGeometry(QRect(QPoint(left, top), QPoint(right, bottom))); } } else { updateCursorShape(e->globalPos()); } } void FrameLess::updateCursorShape(const QPoint &pos) { if (_parent->isFullScreen() || _parent->isMaximized()) { if (_cursorchanged) { _parent->unsetCursor(); } return; } if (!_leftButtonPressed) { calculateCursorPosition(pos, _parent->frameGeometry(), _mouseMove); _cursorchanged = true; if (_mouseMove.testFlag(Edge::Top) || _mouseMove.testFlag(Edge::Bottom)) { _parent->setCursor(Qt::SizeVerCursor); } else if (_mouseMove.testFlag(Edge::Left) || _mouseMove.testFlag(Edge::Right)) { _parent->setCursor(Qt::SizeHorCursor); } else if (_mouseMove.testFlag(Edge::TopLeft) || _mouseMove.testFlag(Edge::BottomRight)) { _parent->setCursor(Qt::SizeFDiagCursor); } else if (_mouseMove.testFlag(Edge::TopRight) || _mouseMove.testFlag(Edge::BottomLeft)) { _parent->setCursor(Qt::SizeBDiagCursor); } else if (_cursorchanged) { _parent->unsetCursor(); _cursorchanged = false; } } } void FrameLess::calculateCursorPosition(const QPoint &pos, const QRect &framerect, Edges &_edge) { bool onLeft = pos.x() >= framerect.x() - _borderWidth && pos.x() <= framerect.x() + _borderWidth && pos.y() <= framerect.y() + framerect.height() - _borderWidth && pos.y() >= framerect.y() + _borderWidth; bool onRight = pos.x() >= framerect.x() + framerect.width() - _borderWidth && pos.x() <= framerect.x() + framerect.width() && pos.y() >= framerect.y() + _borderWidth && pos.y() <= framerect.y() + framerect.height() - _borderWidth; bool onBottom = pos.x() >= framerect.x() + _borderWidth && pos.x() <= framerect.x() + framerect.width() - _borderWidth && pos.y() >= framerect.y() + framerect.height() - _borderWidth && pos.y() <= framerect.y() + framerect.height(); bool onTop = pos.x() >= framerect.x() + _borderWidth && pos.x() <= framerect.x() + framerect.width() - _borderWidth && pos.y() >= framerect.y() && pos.y() <= framerect.y() + _borderWidth; bool onBottomLeft = pos.x() <= framerect.x() + _borderWidth && pos.x() >= framerect.x() && pos.y() <= framerect.y() + framerect.height() && pos.y() >= framerect.y() + framerect.height() - _borderWidth; bool onBottomRight = pos.x() >= framerect.x() + framerect.width() - _borderWidth && pos.x() <= framerect.x() + framerect.width() && pos.y() >= framerect.y() + framerect.height() - _borderWidth && pos.y() <= framerect.y() + framerect.height(); bool onTopRight = pos.x() >= framerect.x() + framerect.width() - _borderWidth && pos.x() <= framerect.x() + framerect.width() && pos.y() >= framerect.y() && pos.y() <= framerect.y() + _borderWidth; bool onTopLeft = pos.x() >= framerect.x() && pos.x() <= framerect.x() + _borderWidth && pos.y() >= framerect.y() && pos.y() <= framerect.y() + _borderWidth; if (onLeft) { _edge = Left; } else if (onRight) { _edge = Right; } else if (onBottom) { _edge = Bottom; } else if (onTop) { _edge = Top; } else if (onBottomLeft) { _edge = BottomLeft; } else if (onBottomRight) { _edge = BottomRight; } else if (onTopRight) { _edge = TopRight; } else if (onTopLeft) { _edge = TopLeft; } else { _edge = None; } } void FrameLess::setBorderWidth(int borderWidth) { _borderWidth = borderWidth; } int FrameLess::borderWidth() const { return _borderWidth; }
-
Good found.
Thx for reporting back.
Im sure someone will re-use this some day. -
I found very usefull this class, I added some code to handle correctly a maximized FrameLess Widget.
Invoid FrameLess::mouseMove(QMouseEvent *e) { if (_dragStart) { if (_parent->isMaximized()) { //this will handle correctly dragging when it is maximized maximizeWidth = _parent->width(); //maximizeWidth is an int attribute _parent->showNormal(); } if (_dragPos.x() > _parent->width()) _dragPos.setX((_parent->width()*_dragPos.x()) / maximizeWidth); _parent->move(e->globalX() - _dragPos.x(), e->globalY() - _dragPos.y()); } if (!_mousePress.testFlag(Edge::None) && !_parent->isMaximized() ) { //this prevent resizing if it is maximized [...]
Now I'm wondering if there is a way to implement Windows 7/10 autosize function (the function when you drag a window on top/left/right/bottom edge of desktop and it will maximize/resize to half desktop size) on a frameless widget!