QMainWindow: move the separator handling in another class

So it will be re-used by QDockWidgetGroupWindow

The Layout template argument can be the QMainWindowLayout,
or the QDockWidgetGroupLayout. They have a function
dockAreaLayoutInfo which return a QDockAreaLayout* or a
QDockAreaLayoutInfo*, respectively. These two class
have the same interface as far as QMainWindowLayoutSeparatorHelper
is concerned. (QDockAreaLayout forward most of its call to
the relevant QDockAreaLayoutInfo)

The code is mostly moved without modification, but there is a change in
startSeparatorMove, because we don't have access to the layoutState to
set the fallbackToSizeHints variable, so instead, we do that from
QMainWindowLayout::hover.

Change-Id: Ic5fa3ee2def3d0155195f751d4770b207732139f
Reviewed-by: Sérgio Martins <sergio.martins@kdab.com>
This commit is contained in:
Olivier Goffart 2017-02-28 16:03:30 +01:00 committed by Olivier Goffart (Woboq GmbH)
parent 46458ce3ef
commit 60be2fb6a1
3 changed files with 254 additions and 238 deletions

View File

@ -72,9 +72,6 @@ public:
: layout(0), explicitIconSize(false), toolButtonStyle(Qt::ToolButtonIconOnly)
#ifdef Q_OS_OSX
, useUnifiedToolBar(false)
#endif
#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
, hasOldCursor(false) , cursorAdjusted(false)
#endif
{ }
QMainWindowLayout *layout;
@ -85,17 +82,6 @@ public:
bool useUnifiedToolBar;
#endif
void init();
QList<int> hoverSeparator;
QPoint hoverPos;
#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
QCursor separatorCursor(const QList<int> &path) const;
void adjustCursor(const QPoint &pos);
QCursor oldCursor;
QCursor adjustedCursor;
uint hasOldCursor : 1;
uint cursorAdjusted : 1;
#endif
static inline QMainWindowLayout *mainWindowLayout(const QMainWindow *mainWindow)
{
@ -1310,153 +1296,14 @@ bool QMainWindow::restoreState(const QByteArray &state, int version)
return restored;
}
#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
QCursor QMainWindowPrivate::separatorCursor(const QList<int> &path) const
{
QDockAreaLayoutInfo *info = layout->layoutState.dockAreaLayout.info(path);
Q_ASSERT(info != 0);
if (path.size() == 1) { // is this the "top-level" separator which separates a dock area
// from the central widget?
switch (path.first()) {
case QInternal::LeftDock:
case QInternal::RightDock:
return Qt::SplitHCursor;
case QInternal::TopDock:
case QInternal::BottomDock:
return Qt::SplitVCursor;
default:
break;
}
}
// no, it's a splitter inside a dock area, separating two dock widgets
return info->o == Qt::Horizontal
? Qt::SplitHCursor : Qt::SplitVCursor;
}
void QMainWindowPrivate::adjustCursor(const QPoint &pos)
{
Q_Q(QMainWindow);
hoverPos = pos;
if (pos == QPoint(0, 0)) {
if (!hoverSeparator.isEmpty())
q->update(layout->layoutState.dockAreaLayout.separatorRect(hoverSeparator));
hoverSeparator.clear();
if (cursorAdjusted) {
cursorAdjusted = false;
if (hasOldCursor)
q->setCursor(oldCursor);
else
q->unsetCursor();
}
} else if (layout->movingSeparator.isEmpty()) { // Don't change cursor when moving separator
QList<int> pathToSeparator
= layout->layoutState.dockAreaLayout.findSeparator(pos);
if (pathToSeparator != hoverSeparator) {
if (!hoverSeparator.isEmpty())
q->update(layout->layoutState.dockAreaLayout.separatorRect(hoverSeparator));
hoverSeparator = pathToSeparator;
if (hoverSeparator.isEmpty()) {
if (cursorAdjusted) {
cursorAdjusted = false;
if (hasOldCursor)
q->setCursor(oldCursor);
else
q->unsetCursor();
}
} else {
q->update(layout->layoutState.dockAreaLayout.separatorRect(hoverSeparator));
if (!cursorAdjusted) {
oldCursor = q->cursor();
hasOldCursor = q->testAttribute(Qt::WA_SetCursor);
}
adjustedCursor = separatorCursor(hoverSeparator);
q->setCursor(adjustedCursor);
cursorAdjusted = true;
}
}
}
}
#endif
/*! \reimp */
bool QMainWindow::event(QEvent *event)
{
Q_D(QMainWindow);
if (d->layout && d->layout->windowEvent(event))
return true;
switch (event->type()) {
#ifndef QT_NO_DOCKWIDGET
case QEvent::Paint: {
QPainter p(this);
QRegion r = static_cast<QPaintEvent*>(event)->region();
d->layout->layoutState.dockAreaLayout.paintSeparators(&p, this, r, d->hoverPos);
break;
}
#ifndef QT_NO_CURSOR
case QEvent::HoverMove: {
d->adjustCursor(static_cast<QHoverEvent*>(event)->pos());
break;
}
// We don't want QWidget to call update() on the entire QMainWindow
// on HoverEnter and HoverLeave, hence accept the event (return true).
case QEvent::HoverEnter:
return true;
case QEvent::HoverLeave:
d->adjustCursor(QPoint(0, 0));
return true;
case QEvent::ShortcutOverride: // when a menu pops up
d->adjustCursor(QPoint(0, 0));
break;
#endif // QT_NO_CURSOR
case QEvent::MouseButtonPress: {
QMouseEvent *e = static_cast<QMouseEvent*>(event);
if (e->button() == Qt::LeftButton && d->layout->startSeparatorMove(e->pos())) {
// The click was on a separator, eat this event
e->accept();
return true;
}
break;
}
case QEvent::MouseMove: {
QMouseEvent *e = static_cast<QMouseEvent*>(event);
#ifndef QT_NO_CURSOR
d->adjustCursor(e->pos());
#endif
if (e->buttons() & Qt::LeftButton) {
if (d->layout->separatorMove(e->pos())) {
// We're moving a separator, eat this event
e->accept();
return true;
}
}
break;
}
case QEvent::MouseButtonRelease: {
QMouseEvent *e = static_cast<QMouseEvent*>(event);
if (d->layout->endSeparatorMove(e->pos())) {
// We've released a separator, eat this event
e->accept();
return true;
}
break;
}
#endif
#ifndef QT_NO_TOOLBAR
case QEvent::ToolBarChange: {
d->layout->toggleToolBarsVisible();
@ -1482,20 +1329,6 @@ bool QMainWindow::event(QEvent *event)
if (!d->explicitIconSize)
setIconSize(QSize());
break;
#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
case QEvent::CursorChange:
// CursorChange events are triggered as mouse moves to new widgets even
// if the cursor doesn't actually change, so do not change oldCursor if
// the "changed" cursor has same shape as adjusted cursor.
if (d->cursorAdjusted && d->adjustedCursor.shape() != cursor().shape()) {
d->oldCursor = cursor();
d->hasOldCursor = testAttribute(Qt::WA_SetCursor);
// Ensure our adjusted cursor stays visible
setCursor(d->adjustedCursor);
}
break;
#endif
default:
break;
}

View File

@ -1689,39 +1689,6 @@ void QMainWindowLayout::tabMoved(int from, int to)
}
#endif // QT_NO_TABBAR
bool QMainWindowLayout::startSeparatorMove(const QPoint &pos)
{
movingSeparator = layoutState.dockAreaLayout.findSeparator(pos);
if (movingSeparator.isEmpty())
return false;
layoutState.dockAreaLayout.fallbackToSizeHints = false;
savedState = layoutState;
movingSeparatorPos = movingSeparatorOrigin = pos;
return true;
}
bool QMainWindowLayout::separatorMove(const QPoint &pos)
{
if (movingSeparator.isEmpty())
return false;
movingSeparatorPos = pos;
separatorMoveTimer.start(0, this);
return true;
}
bool QMainWindowLayout::endSeparatorMove(const QPoint&)
{
if (movingSeparator.isEmpty())
return false;
movingSeparator.clear();
savedState.clear();
return true;
}
void QMainWindowLayout::raise(QDockWidget *widget)
{
#ifndef QT_NO_TABBAR
@ -2424,6 +2391,7 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
}
}
setCurrentHoveredFloat(nullptr);
layoutState.dockAreaLayout.fallbackToSizeHints = false;
#endif //QT_NO_DOCKWIDGET
QPoint pos = parentWidget()->mapFromGlobal(mousePos);
@ -2575,30 +2543,6 @@ bool QMainWindowLayout::restoreState(QDataStream &stream)
return true;
}
void QMainWindowLayout::timerEvent(QTimerEvent *e)
{
#ifndef QT_NO_DOCKWIDGET
if (e->timerId() == separatorMoveTimer.timerId()) {
//let's move the separators
separatorMoveTimer.stop();
if (movingSeparator.isEmpty())
return;
if (movingSeparatorOrigin == movingSeparatorPos)
return;
//when moving the separator, we need to update the previous position
parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
layoutState = savedState;
layoutState.dockAreaLayout.separatorMove(movingSeparator, movingSeparatorOrigin,
movingSeparatorPos);
movingSeparatorPos = movingSeparatorOrigin;
}
#endif
QLayout::timerEvent(e);
}
QT_END_NAMESPACE
#include "moc_qmainwindowlayout_p.cpp"

View File

@ -58,6 +58,8 @@
#include "QtWidgets/qlayout.h"
#include "QtWidgets/qtabbar.h"
#include "QtGui/qpainter.h"
#include "QtGui/qevent.h"
#include "QtCore/qvector.h"
#include "QtCore/qset.h"
#include "QtCore/qbasictimer.h"
@ -72,6 +74,251 @@ QT_BEGIN_NAMESPACE
class QToolBar;
class QRubberBand;
template <typename Layout> // Make use of the "Curiously recurring template pattern"
class QMainWindowLayoutSeparatorHelper
{
Layout *layout() { return static_cast<Layout *>(this); }
const Layout *layout() const { return static_cast<const Layout *>(this); }
QWidget *window() { return layout()->parentWidget(); }
public:
QList<int> hoverSeparator;
QPoint hoverPos;
#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
QCursor separatorCursor(const QList<int> &path);
void adjustCursor(const QPoint &pos);
QCursor oldCursor;
QCursor adjustedCursor;
bool hasOldCursor = false;
bool cursorAdjusted = false;
QList<int> movingSeparator;
QPoint movingSeparatorOrigin, movingSeparatorPos;
QBasicTimer separatorMoveTimer;
bool startSeparatorMove(const QPoint &pos);
bool separatorMove(const QPoint &pos);
bool endSeparatorMove(const QPoint &pos);
#endif
bool windowEvent(QEvent *e);
};
#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_CURSOR)
template <typename Layout>
QCursor QMainWindowLayoutSeparatorHelper<Layout>::separatorCursor(const QList<int> &path)
{
const QDockAreaLayoutInfo *info = layout()->dockAreaLayoutInfo()->info(path);
Q_ASSERT(info != 0);
if (path.size() == 1) { // is this the "top-level" separator which separates a dock area
// from the central widget?
switch (path.first()) {
case QInternal::LeftDock:
case QInternal::RightDock:
return Qt::SplitHCursor;
case QInternal::TopDock:
case QInternal::BottomDock:
return Qt::SplitVCursor;
default:
break;
}
}
// no, it's a splitter inside a dock area, separating two dock widgets
return info->o == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor;
}
template <typename Layout>
void QMainWindowLayoutSeparatorHelper<Layout>::adjustCursor(const QPoint &pos)
{
QWidget *w = layout()->window();
hoverPos = pos;
if (pos == QPoint(0, 0)) {
if (!hoverSeparator.isEmpty())
w->update(layout()->dockAreaLayoutInfo()->separatorRect(hoverSeparator));
hoverSeparator.clear();
if (cursorAdjusted) {
cursorAdjusted = false;
if (hasOldCursor)
w->setCursor(oldCursor);
else
w->unsetCursor();
}
} else if (movingSeparator.isEmpty()) { // Don't change cursor when moving separator
QList<int> pathToSeparator = layout()->dockAreaLayoutInfo()->findSeparator(pos);
if (pathToSeparator != hoverSeparator) {
if (!hoverSeparator.isEmpty())
w->update(layout()->dockAreaLayoutInfo()->separatorRect(hoverSeparator));
hoverSeparator = pathToSeparator;
if (hoverSeparator.isEmpty()) {
if (cursorAdjusted) {
cursorAdjusted = false;
if (hasOldCursor)
w->setCursor(oldCursor);
else
w->unsetCursor();
}
} else {
w->update(layout()->dockAreaLayoutInfo()->separatorRect(hoverSeparator));
if (!cursorAdjusted) {
oldCursor = w->cursor();
hasOldCursor = w->testAttribute(Qt::WA_SetCursor);
}
adjustedCursor = separatorCursor(hoverSeparator);
w->setCursor(adjustedCursor);
cursorAdjusted = true;
}
}
}
}
template <typename Layout>
bool QMainWindowLayoutSeparatorHelper<Layout>::windowEvent(QEvent *event)
{
QWidget *w = window();
switch (event->type()) {
case QEvent::Paint: {
QPainter p(w);
QRegion r = static_cast<QPaintEvent *>(event)->region();
layout()->dockAreaLayoutInfo()->paintSeparators(&p, w, r, hoverPos);
break;
}
#ifndef QT_NO_CURSOR
case QEvent::HoverMove: {
adjustCursor(static_cast<QHoverEvent *>(event)->pos());
break;
}
// We don't want QWidget to call update() on the entire QMainWindow
// on HoverEnter and HoverLeave, hence accept the event (return true).
case QEvent::HoverEnter:
return true;
case QEvent::HoverLeave:
adjustCursor(QPoint(0, 0));
return true;
case QEvent::ShortcutOverride: // when a menu pops up
adjustCursor(QPoint(0, 0));
break;
#endif // QT_NO_CURSOR
case QEvent::MouseButtonPress: {
QMouseEvent *e = static_cast<QMouseEvent *>(event);
if (e->button() == Qt::LeftButton && startSeparatorMove(e->pos())) {
// The click was on a separator, eat this event
e->accept();
return true;
}
break;
}
case QEvent::MouseMove: {
QMouseEvent *e = static_cast<QMouseEvent *>(event);
#ifndef QT_NO_CURSOR
adjustCursor(e->pos());
#endif
if (e->buttons() & Qt::LeftButton) {
if (separatorMove(e->pos())) {
// We're moving a separator, eat this event
e->accept();
return true;
}
}
break;
}
case QEvent::MouseButtonRelease: {
QMouseEvent *e = static_cast<QMouseEvent *>(event);
if (endSeparatorMove(e->pos())) {
// We've released a separator, eat this event
e->accept();
return true;
}
break;
}
#if !defined(QT_NO_CURSOR)
case QEvent::CursorChange:
// CursorChange events are triggered as mouse moves to new widgets even
// if the cursor doesn't actually change, so do not change oldCursor if
// the "changed" cursor has same shape as adjusted cursor.
if (cursorAdjusted && adjustedCursor.shape() != w->cursor().shape()) {
oldCursor = w->cursor();
hasOldCursor = w->testAttribute(Qt::WA_SetCursor);
// Ensure our adjusted cursor stays visible
w->setCursor(adjustedCursor);
}
break;
#endif
case QEvent::Timer:
if (static_cast<QTimerEvent *>(event)->timerId() == separatorMoveTimer.timerId()) {
// let's move the separators
separatorMoveTimer.stop();
if (movingSeparator.isEmpty())
return true;
if (movingSeparatorOrigin == movingSeparatorPos)
return true;
// when moving the separator, we need to update the previous position
window()->update(layout()->dockAreaLayoutInfo()->separatorRegion());
layout()->layoutState = layout()->savedState;
layout()->dockAreaLayoutInfo()->separatorMove(movingSeparator, movingSeparatorOrigin,
movingSeparatorPos);
movingSeparatorPos = movingSeparatorOrigin;
return true;
}
break;
default:
break;
}
return false;
}
template <typename Layout>
bool QMainWindowLayoutSeparatorHelper<Layout>::startSeparatorMove(const QPoint &pos)
{
movingSeparator = layout()->dockAreaLayoutInfo()->findSeparator(pos);
if (movingSeparator.isEmpty())
return false;
layout()->savedState = layout()->layoutState;
movingSeparatorPos = movingSeparatorOrigin = pos;
return true;
}
template <typename Layout>
bool QMainWindowLayoutSeparatorHelper<Layout>::separatorMove(const QPoint &pos)
{
if (movingSeparator.isEmpty())
return false;
movingSeparatorPos = pos;
separatorMoveTimer.start(0, window());
return true;
}
template <typename Layout>
bool QMainWindowLayoutSeparatorHelper<Layout>::endSeparatorMove(const QPoint &)
{
if (movingSeparator.isEmpty())
return false;
movingSeparator.clear();
layout()->savedState.clear();
return true;
}
#endif
#ifndef QT_NO_DOCKWIDGET
class QDockWidgetGroupWindow : public QWidget
{
@ -168,7 +415,9 @@ public:
bool restoreState(QDataStream &stream, const QMainWindowLayoutState &oldState);
};
class Q_AUTOTEST_EXPORT QMainWindowLayout : public QLayout
class Q_AUTOTEST_EXPORT QMainWindowLayout
: public QLayout,
public QMainWindowLayoutSeparatorHelper<QMainWindowLayout>
{
Q_OBJECT
@ -181,8 +430,6 @@ public:
QMainWindow::DockOptions dockOptions;
void setDockOptions(QMainWindow::DockOptions opts);
void timerEvent(QTimerEvent *e) Q_DECL_OVERRIDE;
// status bar
QLayoutItem *statusbar;
@ -260,15 +507,7 @@ public:
#endif // QT_NO_TABWIDGET
#endif // QT_NO_TABBAR
// separators
QList<int> movingSeparator;
QPoint movingSeparatorOrigin, movingSeparatorPos;
QBasicTimer separatorMoveTimer;
bool startSeparatorMove(const QPoint &pos);
bool separatorMove(const QPoint &pos);
bool endSeparatorMove(const QPoint &pos);
QDockAreaLayout *dockAreaLayoutInfo() { return &layoutState.dockAreaLayout; }
void keepSize(QDockWidget *w);
#endif // QT_NO_DOCKWIDGET