1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qglobal.h"
41 
42 #include <QtCore/qdebug.h>
43 #include <QtCore/qnumeric.h>
44 #include "qgraphicswidget_p.h"
45 #include "qgraphicslayoutitem_p.h"
46 #include "qgraphicslayout.h"
47 #include "qgraphicsscene_p.h"
48 #include <QtWidgets/qapplication.h>
49 #include <QtWidgets/qgraphicsscene.h>
50 #include <QtWidgets/qstyleoption.h>
51 #include <QtWidgets/QStyleOptionTitleBar>
52 #include <QtWidgets/QGraphicsSceneMouseEvent>
53 
54 #include <private/qmemory_p.h>
55 
56 QT_BEGIN_NAMESPACE
57 
init(QGraphicsItem * parentItem,Qt::WindowFlags wFlags)58 void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags)
59 {
60     Q_Q(QGraphicsWidget);
61 
62     attributes = 0;
63     isWidget = 1; // QGraphicsItem::isWidget() returns true.
64     focusNext = focusPrev = q;
65     focusPolicy = Qt::NoFocus;
66 
67     adjustWindowFlags(&wFlags);
68     windowFlags = wFlags;
69 
70     q->setParentItem(parentItem);
71     q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType));
72     q->setGraphicsItem(q);
73 
74     resolveLayoutDirection();
75     q->unsetWindowFrameMargins();
76     flags |= QGraphicsItem::ItemUsesExtendedStyleOption;
77     flags |= QGraphicsItem::ItemSendsGeometryChanges;
78     if (windowFlags & Qt::Window)
79         flags |= QGraphicsItem::ItemIsPanel;
80 }
81 
titleBarHeight(const QStyleOptionTitleBar & options) const82 qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const
83 {
84     Q_Q(const QGraphicsWidget);
85     int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options);
86     return (qreal)height;
87 }
88 
89 /*!
90     \internal
91 */
QGraphicsWidgetPrivate()92 QGraphicsWidgetPrivate::QGraphicsWidgetPrivate()
93     : margins(nullptr),
94       layout(nullptr),
95       inheritedPaletteResolveMask(0),
96       inheritedFontResolveMask(0),
97       inSetGeometry(false),
98       polished(false),
99       inSetPos(false),
100       autoFillBackground(false),
101       focusPolicy(Qt::NoFocus),
102       focusNext(nullptr),
103       focusPrev(nullptr),
104       windowFlags(),
105       windowData(nullptr),
106       setWindowFrameMargins(false),
107       windowFrameMargins(nullptr)
108 {
109 }
110 
~QGraphicsWidgetPrivate()111 QGraphicsWidgetPrivate::~QGraphicsWidgetPrivate()
112 {
113 }
114 
115 /*!
116     \internal
117 
118      Ensures that margins is allocated.
119      This function must be called before any dereferencing.
120 */
ensureMargins() const121 void QGraphicsWidgetPrivate::ensureMargins() const
122 {
123     if (!margins)
124         margins = qt_make_unique<QMarginsF>();
125 }
126 
127 /*!
128     \internal
129 
130      Ensures that windowFrameMargins is allocated.
131      This function must be called before any dereferencing.
132 */
ensureWindowFrameMargins() const133 void QGraphicsWidgetPrivate::ensureWindowFrameMargins() const
134 {
135     if (!windowFrameMargins)
136         windowFrameMargins = qt_make_unique<QMarginsF>();
137 }
138 
139 /*!
140     \internal
141 
142      Ensures that windowData is allocated.
143      This function must be called before any dereferencing.
144 */
ensureWindowData()145 void QGraphicsWidgetPrivate::ensureWindowData()
146 {
147     if (!windowData)
148         windowData = qt_make_unique<WindowData>();
149 }
150 
setPalette_helper(const QPalette & palette)151 void QGraphicsWidgetPrivate::setPalette_helper(const QPalette &palette)
152 {
153     if (this->palette == palette && this->palette.resolve() == palette.resolve())
154         return;
155     updatePalette(palette);
156 }
157 
resolvePalette(uint inheritedMask)158 void QGraphicsWidgetPrivate::resolvePalette(uint inheritedMask)
159 {
160     inheritedPaletteResolveMask = inheritedMask;
161     QPalette naturalPalette = naturalWidgetPalette();
162     QPalette resolvedPalette = palette.resolve(naturalPalette);
163     updatePalette(resolvedPalette);
164 }
165 
updatePalette(const QPalette & palette)166 void QGraphicsWidgetPrivate::updatePalette(const QPalette &palette)
167 {
168     Q_Q(QGraphicsWidget);
169     // Update local palette setting.
170     this->palette = palette;
171 
172     // Calculate new mask.
173     if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation))
174         inheritedPaletteResolveMask = 0;
175     int mask = palette.resolve() | inheritedPaletteResolveMask;
176 
177     // Propagate to children.
178     for (int i = 0; i < children.size(); ++i) {
179         QGraphicsItem *item = children.at(i);
180         if (item->isWidget()) {
181             QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
182             if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
183                 w->d_func()->resolvePalette(mask);
184         } else {
185             item->d_ptr->resolvePalette(mask);
186         }
187     }
188 
189     // Notify change.
190     QEvent event(QEvent::PaletteChange);
191     QCoreApplication::sendEvent(q, &event);
192 }
193 
setLayoutDirection_helper(Qt::LayoutDirection direction)194 void QGraphicsWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction)
195 {
196     Q_Q(QGraphicsWidget);
197     if ((direction == Qt::RightToLeft) == (testAttribute(Qt::WA_RightToLeft)))
198         return;
199     q->setAttribute(Qt::WA_RightToLeft, (direction == Qt::RightToLeft));
200 
201     // Propagate this change to all children.
202     for (int i = 0; i < children.size(); ++i) {
203         QGraphicsItem *item = children.at(i);
204         if (item->isWidget()) {
205             QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
206             if (widget->parentWidget() && !widget->testAttribute(Qt::WA_SetLayoutDirection))
207                 widget->d_func()->setLayoutDirection_helper(direction);
208         }
209     }
210 
211     // Send the notification event to this widget item.
212     QEvent e(QEvent::LayoutDirectionChange);
213     QCoreApplication::sendEvent(q, &e);
214 }
215 
resolveLayoutDirection()216 void QGraphicsWidgetPrivate::resolveLayoutDirection()
217 {
218     Q_Q(QGraphicsWidget);
219     if (q->testAttribute(Qt::WA_SetLayoutDirection)) {
220         return;
221     }
222     if (QGraphicsWidget *parentWidget = q->parentWidget()) {
223         setLayoutDirection_helper(parentWidget->layoutDirection());
224     } else if (scene) {
225         // ### shouldn't the scene have a layoutdirection really? how does
226         // ### QGraphicsWidget get changes from QApplication::layoutDirection?
227         setLayoutDirection_helper(QGuiApplication::layoutDirection());
228     } else {
229         setLayoutDirection_helper(QGuiApplication::layoutDirection());
230     }
231 }
232 
naturalWidgetPalette() const233 QPalette QGraphicsWidgetPrivate::naturalWidgetPalette() const
234 {
235     Q_Q(const QGraphicsWidget);
236     QPalette palette;
237     if (QGraphicsWidget *parent = q->parentWidget()) {
238         palette = parent->palette();
239     } else if (scene) {
240         palette = scene->palette();
241     }
242     palette.resolve(0);
243     return palette;
244 }
245 
setFont_helper(const QFont & font)246 void QGraphicsWidgetPrivate::setFont_helper(const QFont &font)
247 {
248     if (this->font == font && this->font.resolve() == font.resolve())
249         return;
250     updateFont(font);
251 }
252 
resolveFont(uint inheritedMask)253 void QGraphicsWidgetPrivate::resolveFont(uint inheritedMask)
254 {
255     Q_Q(QGraphicsWidget);
256     inheritedFontResolveMask = inheritedMask;
257     if (QGraphicsWidget *p = q->parentWidget())
258         inheritedFontResolveMask |= p->d_func()->inheritedFontResolveMask;
259     QFont naturalFont = naturalWidgetFont();
260     QFont resolvedFont = font.resolve(naturalFont);
261     updateFont(resolvedFont);
262 }
263 
updateFont(const QFont & font)264 void QGraphicsWidgetPrivate::updateFont(const QFont &font)
265 {
266     Q_Q(QGraphicsWidget);
267     // Update the local font setting.
268     this->font = font;
269 
270     // Calculate new mask.
271     if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation))
272         inheritedFontResolveMask = 0;
273     int mask = font.resolve() | inheritedFontResolveMask;
274 
275     // Propagate to children.
276     for (int i = 0; i < children.size(); ++i) {
277         QGraphicsItem *item = children.at(i);
278         if (item->isWidget()) {
279             QGraphicsWidget *w = static_cast<QGraphicsWidget *>(item);
280             if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
281                 w->d_func()->resolveFont(mask);
282         } else {
283             item->d_ptr->resolveFont(mask);
284         }
285     }
286 
287     if (!polished)
288         return;
289     // Notify change.
290     QEvent event(QEvent::FontChange);
291     QCoreApplication::sendEvent(q, &event);
292 }
293 
naturalWidgetFont() const294 QFont QGraphicsWidgetPrivate::naturalWidgetFont() const
295 {
296     Q_Q(const QGraphicsWidget);
297     QFont naturalFont; // ### no application font support
298     if (QGraphicsWidget *parent = q->parentWidget()) {
299         naturalFont = parent->font();
300     } else if (scene) {
301         naturalFont = scene->font();
302     }
303     naturalFont.resolve(0);
304     return naturalFont;
305 }
306 
initStyleOptionTitleBar(QStyleOptionTitleBar * option)307 void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *option)
308 {
309     Q_Q(QGraphicsWidget);
310     ensureWindowData();
311     q->initStyleOption(option);
312     option->rect.setHeight(titleBarHeight(*option));
313     option->titleBarFlags = windowFlags;
314     option->subControls = QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarSysMenu;
315     option->activeSubControls = windowData->hoveredSubControl;
316     bool isActive = q->isActiveWindow();
317     option->state.setFlag(QStyle::State_Active, isActive);
318     if (isActive) {
319         option->titleBarState = Qt::WindowActive;
320         option->titleBarState |= QStyle::State_Active;
321     } else {
322         option->titleBarState = Qt::WindowNoState;
323     }
324     QFont windowTitleFont = QApplication::font("QMdiSubWindowTitleBar");
325     QRect textRect = q->style()->subControlRect(QStyle::CC_TitleBar, option, QStyle::SC_TitleBarLabel, nullptr);
326     option->text = QFontMetrics(windowTitleFont).elidedText(
327         windowData->windowTitle, Qt::ElideRight, textRect.width());
328 }
329 
adjustWindowFlags(Qt::WindowFlags * flags)330 void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags)
331 {
332     bool customize =  (*flags & (Qt::CustomizeWindowHint
333             | Qt::FramelessWindowHint
334             | Qt::WindowTitleHint
335             | Qt::WindowSystemMenuHint
336             | Qt::WindowMinimizeButtonHint
337             | Qt::WindowMaximizeButtonHint
338             | Qt::WindowContextHelpButtonHint));
339 
340     uint type = (*flags & Qt::WindowType_Mask);
341     if (customize)
342         ;
343     else if (type == Qt::Dialog || type == Qt::Sheet)
344         *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowContextHelpButtonHint;
345     else if (type == Qt::Tool)
346         *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint;
347     else if (type == Qt::Window || type == Qt::SubWindow)
348         *flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint
349                   | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
350 }
351 
windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent * event)352 void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
353 {
354     Q_Q(QGraphicsWidget);
355     ensureWindowData();
356     if (windowData->grabbedSection != Qt::NoSection) {
357         if (windowData->grabbedSection == Qt::TitleBarArea) {
358             windowData->buttonSunken = false;
359             QStyleOptionTitleBar bar;
360             initStyleOptionTitleBar(&bar);
361             // make sure that the coordinates (rect and pos) we send to the style are positive.
362             bar.rect = q->windowFrameRect().toRect();
363             bar.rect.moveTo(0,0);
364             bar.rect.setHeight(q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &bar));
365             QPointF pos = event->pos();
366             if (windowFrameMargins) {
367                 pos.rx() += windowFrameMargins->left();
368                 pos.ry() += windowFrameMargins->top();
369             }
370             bar.subControls = QStyle::SC_TitleBarCloseButton;
371             if (q->style()->subControlRect(QStyle::CC_TitleBar, &bar,
372                                            QStyle::SC_TitleBarCloseButton,
373                                            event->widget()).contains(pos.toPoint())) {
374                 q->close();
375             }
376         }
377         if (!(static_cast<QGraphicsSceneMouseEvent *>(event)->buttons()))
378             windowData->grabbedSection = Qt::NoSection;
379         event->accept();
380     }
381 }
382 
windowFrameMousePressEvent(QGraphicsSceneMouseEvent * event)383 void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event)
384 {
385     Q_Q(QGraphicsWidget);
386     if (event->button() != Qt::LeftButton)
387         return;
388 
389     ensureWindowData();
390     windowData->startGeometry = q->geometry();
391     windowData->grabbedSection = q->windowFrameSectionAt(event->pos());
392     ensureWindowData();
393     if (windowData->grabbedSection == Qt::TitleBarArea
394         && windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton) {
395         windowData->buttonSunken = true;
396         q->update();
397     }
398     event->setAccepted(windowData->grabbedSection != Qt::NoSection);
399 }
400 
401 /*
402   Used to calculate the
403   Precondition:
404   \a widget should support either hfw or wfh
405 
406   If \a heightForWidth is set to false, this function will query the width for height
407   instead. \a width will then be interpreted as height, \a minh and \a maxh will be interpreted
408   as minimum width and maximum width.
409  */
minimumHeightForWidth(qreal width,qreal minh,qreal maxh,const QGraphicsWidget * widget,bool heightForWidth=true)410 static qreal minimumHeightForWidth(qreal width, qreal minh, qreal maxh,
411                                    const QGraphicsWidget *widget,
412                                    bool heightForWidth = true)
413 {
414     qreal minimumHeightForWidth = -1;
415     const bool hasHFW = QGraphicsLayoutItemPrivate::get(widget)->hasHeightForWidth();
416     if (hasHFW == heightForWidth) {
417         minimumHeightForWidth = hasHFW
418                                 ? widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(width, -1)).height()
419                                 : widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, width)).width();    //"width" is here height!
420     } else {
421         // widthForHeight
422         const qreal constraint = width;
423         while (maxh - minh > 0.1) {
424             qreal middle = minh + (maxh - minh)/2;
425             // ### really bad, if we are a widget with a layout it will call
426             // layout->effectiveSizeHint(Qt::MiniumumSize), which again will call
427             // sizeHint three times because of how the cache works
428             qreal hfw = hasHFW
429                         ? widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(middle, -1)).height()
430                         : widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, middle)).width();
431             if (hfw > constraint) {
432                 minh = middle;
433             } else if (hfw <= constraint) {
434                 maxh = middle;
435             }
436         }
437         minimumHeightForWidth = maxh;
438     }
439     return minimumHeightForWidth;
440 }
441 
minimumWidthForHeight(qreal height,qreal minw,qreal maxw,const QGraphicsWidget * widget)442 static qreal minimumWidthForHeight(qreal height, qreal minw, qreal maxw,
443                                    const QGraphicsWidget *widget)
444 {
445     return minimumHeightForWidth(height, minw, maxw, widget, false);
446 }
447 
closestAcceptableSize(const QSizeF & proposed,const QGraphicsWidget * widget)448 static QSizeF closestAcceptableSize(const QSizeF &proposed,
449                                     const QGraphicsWidget *widget)
450 {
451     const QSizeF current = widget->size();
452 
453     qreal minw = proposed.width();
454     qreal maxw = current.width();
455     qreal minh = proposed.height();
456     qreal maxh = current.height();
457 
458     qreal middlew = maxw;
459     qreal middleh = maxh;
460     qreal min_hfw;
461     min_hfw = minimumHeightForWidth(maxw, minh, maxh, widget);
462 
463     do {
464         if (maxw - minw < 0.1) {
465             // we still haven't found anything, cut off binary search
466             minw = maxw;
467             minh = maxh;
468         }
469         middlew = minw + (maxw - minw)/2.0;
470         middleh = minh + (maxh - minh)/2.0;
471 
472         min_hfw = minimumHeightForWidth(middlew, minh, maxh, widget);
473 
474         if (min_hfw > middleh) {
475             minw = middlew;
476             minh = middleh;
477         } else if (min_hfw <= middleh) {
478             maxw = middlew;
479             maxh = middleh;
480         }
481     } while (maxw != minw);
482 
483     min_hfw = minimumHeightForWidth(middlew, minh, maxh, widget);
484 
485     QSizeF result;
486     if (min_hfw < maxh) {
487         result = QSizeF(middlew, min_hfw);
488     } else {
489         // Needed because of the cut-off we do above.
490         result = QSizeF(minimumWidthForHeight(maxh, proposed.width(), current.width(), widget), maxh);
491     }
492     return result;
493 }
494 
_q_boundGeometryToSizeConstraints(const QRectF & startGeometry,QRectF * rect,Qt::WindowFrameSection section,const QSizeF & min,const QSizeF & max,const QGraphicsWidget * widget)495 static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry,
496                                               QRectF *rect, Qt::WindowFrameSection section,
497                                               const QSizeF &min, const QSizeF &max,
498                                               const QGraphicsWidget *widget)
499 {
500     const QRectF proposedRect = *rect;
501     qreal width = qBound(min.width(), proposedRect.width(), max.width());
502     qreal height = qBound(min.height(), proposedRect.height(), max.height());
503 
504     const bool hasHFW = QGraphicsLayoutItemPrivate::get(widget)->hasHeightForWidth();
505     const bool hasWFH = QGraphicsLayoutItemPrivate::get(widget)->hasWidthForHeight();
506 
507     const bool widthChanged = proposedRect.width() != widget->size().width();
508     const bool heightChanged = proposedRect.height() != widget->size().height();
509 
510     if (hasHFW || hasWFH) {
511         if (widthChanged || heightChanged) {
512             qreal minExtent;
513             qreal maxExtent;
514             qreal constraint;
515             qreal proposed;
516             if (hasHFW) {
517                 minExtent = min.height();
518                 maxExtent = max.height();
519                 constraint = width;
520                 proposed = proposedRect.height();
521             } else {
522                 // width for height
523                 minExtent = min.width();
524                 maxExtent = max.width();
525                 constraint = height;
526                 proposed = proposedRect.width();
527             }
528             if (minimumHeightForWidth(constraint, minExtent, maxExtent, widget, hasHFW) > proposed) {
529                 QSizeF effectiveSize = closestAcceptableSize(QSizeF(width, height), widget);
530                 width = effectiveSize.width();
531                 height = effectiveSize.height();
532             }
533         }
534     }
535 
536     switch (section) {
537     case Qt::LeftSection:
538         rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(),
539                       qRound(width), startGeometry.height());
540         break;
541     case Qt::TopLeftSection:
542         rect->setRect(startGeometry.right() - qRound(width), startGeometry.bottom() - qRound(height),
543                       qRound(width), qRound(height));
544         break;
545     case Qt::TopSection:
546         rect->setRect(startGeometry.left(), startGeometry.bottom() - qRound(height),
547                       startGeometry.width(), qRound(height));
548         break;
549     case Qt::TopRightSection:
550         rect->setTop(rect->bottom() - qRound(height));
551         rect->setWidth(qRound(width));
552         break;
553     case Qt::RightSection:
554         rect->setWidth(qRound(width));
555         break;
556     case Qt::BottomRightSection:
557         rect->setWidth(qRound(width));
558         rect->setHeight(qRound(height));
559         break;
560     case Qt::BottomSection:
561         rect->setHeight(qRound(height));
562         break;
563     case Qt::BottomLeftSection:
564         rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(),
565                       qRound(width), qRound(height));
566         break;
567     default:
568         break;
569     }
570 }
571 
windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent * event)572 void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event)
573 {
574     Q_Q(QGraphicsWidget);
575     ensureWindowData();
576     if (!(event->buttons() & Qt::LeftButton) || windowData->hoveredSubControl != QStyle::SC_TitleBarLabel)
577         return;
578 
579     QLineF delta(q->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)), event->pos());
580     QLineF parentDelta(q->mapToParent(delta.p1()), q->mapToParent(delta.p2()));
581     QLineF parentXDelta(q->mapToParent(QPointF(delta.p1().x(), 0)), q->mapToParent(QPointF(delta.p2().x(), 0)));
582     QLineF parentYDelta(q->mapToParent(QPointF(0, delta.p1().y())), q->mapToParent(QPointF(0, delta.p2().y())));
583 
584     QRectF newGeometry;
585     switch (windowData->grabbedSection) {
586     case Qt::LeftSection:
587         newGeometry = QRectF(windowData->startGeometry.topLeft()
588                              + QPointF(parentXDelta.dx(), parentXDelta.dy()),
589                              windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy()));
590         break;
591     case Qt::TopLeftSection:
592         newGeometry = QRectF(windowData->startGeometry.topLeft()
593                              + QPointF(parentDelta.dx(), parentDelta.dy()),
594                              windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy()));
595         break;
596     case Qt::TopSection:
597         newGeometry = QRectF(windowData->startGeometry.topLeft()
598                              + QPointF(parentYDelta.dx(), parentYDelta.dy()),
599                              windowData->startGeometry.size() - QSizeF(0, delta.dy()));
600         break;
601     case Qt::TopRightSection:
602         newGeometry = QRectF(windowData->startGeometry.topLeft()
603                              + QPointF(parentYDelta.dx(), parentYDelta.dy()),
604                              windowData->startGeometry.size() - QSizeF(-delta.dx(), delta.dy()));
605         break;
606     case Qt::RightSection:
607         newGeometry = QRectF(windowData->startGeometry.topLeft(),
608                              windowData->startGeometry.size() + QSizeF(delta.dx(), 0));
609         break;
610     case Qt::BottomRightSection:
611         newGeometry = QRectF(windowData->startGeometry.topLeft(),
612                              windowData->startGeometry.size() + QSizeF(delta.dx(), delta.dy()));
613         break;
614     case Qt::BottomSection:
615         newGeometry = QRectF(windowData->startGeometry.topLeft(),
616                              windowData->startGeometry.size() + QSizeF(0, delta.dy()));
617         break;
618     case Qt::BottomLeftSection:
619         newGeometry = QRectF(windowData->startGeometry.topLeft()
620                              + QPointF(parentXDelta.dx(), parentXDelta.dy()),
621                              windowData->startGeometry.size() - QSizeF(delta.dx(), -delta.dy()));
622         break;
623     case Qt::TitleBarArea:
624         newGeometry = QRectF(windowData->startGeometry.topLeft()
625                              + QPointF(parentDelta.dx(), parentDelta.dy()),
626                              windowData->startGeometry.size());
627         break;
628     case Qt::NoSection:
629         break;
630     }
631 
632     if (windowData->grabbedSection != Qt::NoSection) {
633         _q_boundGeometryToSizeConstraints(windowData->startGeometry, &newGeometry,
634                                           windowData->grabbedSection,
635                                           q->effectiveSizeHint(Qt::MinimumSize),
636                                           q->effectiveSizeHint(Qt::MaximumSize),
637                                           q);
638         q->setGeometry(newGeometry);
639     }
640 }
641 
windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent * event)642 void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event)
643 {
644     Q_Q(QGraphicsWidget);
645     if (!hasDecoration())
646         return;
647 
648     ensureWindowData();
649 
650     if (q->rect().contains(event->pos())) {
651         if (windowData->buttonMouseOver || windowData->hoveredSubControl != QStyle::SC_None)
652             windowFrameHoverLeaveEvent(event);
653         return;
654     }
655 
656     bool wasMouseOver = windowData->buttonMouseOver;
657     QRect oldButtonRect = windowData->buttonRect;
658     windowData->buttonRect = QRect();
659     windowData->buttonMouseOver = false;
660     QPointF pos = event->pos();
661     QStyleOptionTitleBar bar;
662     // make sure that the coordinates (rect and pos) we send to the style are positive.
663     if (windowFrameMargins) {
664         pos.rx() += windowFrameMargins->left();
665         pos.ry() += windowFrameMargins->top();
666     }
667     initStyleOptionTitleBar(&bar);
668     bar.rect = q->windowFrameRect().toRect();
669     bar.rect.moveTo(0,0);
670     bar.rect.setHeight(int(titleBarHeight(bar)));
671 
672     Qt::CursorShape cursorShape = Qt::ArrowCursor;
673     bool needsSetCursorCall = true;
674     switch (q->windowFrameSectionAt(event->pos())) {
675         case Qt::TopLeftSection:
676         case Qt::BottomRightSection:
677             cursorShape = Qt::SizeFDiagCursor;
678             break;
679         case Qt::TopRightSection:
680         case Qt::BottomLeftSection:
681             cursorShape = Qt::SizeBDiagCursor;
682             break;
683         case Qt::LeftSection:
684         case Qt::RightSection:
685             cursorShape = Qt::SizeHorCursor;
686             break;
687         case Qt::TopSection:
688         case Qt::BottomSection:
689             cursorShape = Qt::SizeVerCursor;
690             break;
691         case Qt::TitleBarArea:
692             windowData->buttonRect = q->style()->subControlRect(
693                 QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, nullptr);
694             if (windowData->buttonRect.contains(pos.toPoint()))
695                 windowData->buttonMouseOver = true;
696             event->ignore();
697             break;
698         default:
699             needsSetCursorCall = false;
700             event->ignore();
701         }
702 #ifndef QT_NO_CURSOR
703     if (needsSetCursorCall)
704         q->setCursor(cursorShape);
705 #else
706     Q_UNUSED(needsSetCursorCall);
707     Q_UNUSED(cursorShape);
708 #endif
709     // update buttons if we hover over them
710     windowData->hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), nullptr);
711     if (windowData->hoveredSubControl != QStyle::SC_TitleBarCloseButton)
712         windowData->hoveredSubControl = QStyle::SC_TitleBarLabel;
713 
714     if (windowData->buttonMouseOver != wasMouseOver) {
715         if (!oldButtonRect.isNull())
716             q->update(QRectF(oldButtonRect).translated(q->windowFrameRect().topLeft()));
717         if (!windowData->buttonRect.isNull())
718             q->update(QRectF(windowData->buttonRect).translated(q->windowFrameRect().topLeft()));
719     }
720 }
721 
windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent * event)722 void QGraphicsWidgetPrivate::windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent *event)
723 {
724     Q_UNUSED(event);
725     Q_Q(QGraphicsWidget);
726     if (hasDecoration()) {
727         // ### restore the cursor, don't override it
728 #ifndef QT_NO_CURSOR
729         q->unsetCursor();
730 #endif
731 
732         ensureWindowData();
733 
734         bool needsUpdate = false;
735         if (windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton
736             || windowData->buttonMouseOver)
737             needsUpdate = true;
738 
739         // update the hover state (of buttons etc...)
740         windowData->hoveredSubControl = QStyle::SC_None;
741         windowData->buttonMouseOver = false;
742         windowData->buttonRect = QRect();
743         if (needsUpdate)
744             q->update(windowData->buttonRect);
745     }
746 }
747 
hasDecoration() const748 bool QGraphicsWidgetPrivate::hasDecoration() const
749 {
750     return (windowFlags & Qt::Window) && (windowFlags & Qt::WindowTitleHint);
751 }
752 
753 /**
754  * is called after a reparent has taken place to fix up the focus chain(s)
755  */
fixFocusChainBeforeReparenting(QGraphicsWidget * newParent,QGraphicsScene * oldScene,QGraphicsScene * newScene)756 void QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *oldScene, QGraphicsScene *newScene)
757 {
758     Q_Q(QGraphicsWidget);
759     Q_ASSERT(focusNext && focusPrev);
760 
761     if (q_ptr->isPanel()) {
762         // panels are never a part of their parent's or ancestors' focus
763         // chains. so reparenting a panel is easy; there's nothing to
764         // do.
765         return;
766     }
767 
768     // we're not a panel, so find the first widget in the focus chain
769     // (this), and the last (this, or the last widget that is still
770     // a descendent of this). also find the widgets that currently /
771     // before reparenting point to this widgets' focus chain.
772     QGraphicsWidget *focusFirst = q;
773     QGraphicsWidget *focusBefore = focusPrev;
774     QGraphicsWidget *focusLast = focusFirst;
775     QGraphicsWidget *focusAfter = focusNext;
776     do {
777         if (!q->isAncestorOf(focusAfter))
778             break;
779         focusLast = focusAfter;
780     } while ((focusAfter = focusAfter->d_func()->focusNext));
781 
782     if (!parent && oldScene && oldScene != newScene && oldScene->d_func()->tabFocusFirst == q) {
783         // detach from old scene's top level focus chain.
784         oldScene->d_func()->tabFocusFirst = (focusAfter != q) ? focusAfter : nullptr;
785     }
786 
787     // detach from current focus chain; skip this widget subtree.
788     focusBefore->d_func()->focusNext = focusAfter;
789     focusAfter->d_func()->focusPrev = focusBefore;
790 
791     if (newParent) {
792         // attach to new parent's focus chain as the last element
793         // in its chain.
794         QGraphicsWidget *newFocusFirst = newParent;
795         QGraphicsWidget *newFocusLast = newFocusFirst;
796         QGraphicsWidget *newFocusAfter = newFocusFirst->d_func()->focusNext;
797         do {
798             if (!newParent->isAncestorOf(newFocusAfter))
799                 break;
800             newFocusLast = newFocusAfter;
801         } while ((newFocusAfter = newFocusAfter->d_func()->focusNext));
802 
803         newFocusLast->d_func()->focusNext = q;
804         focusLast->d_func()->focusNext = newFocusAfter;
805         newFocusAfter->d_func()->focusPrev = focusLast;
806         focusPrev = newFocusLast;
807     } else {
808         // no new parent, so just link up our own prev->last widgets.
809         focusPrev = focusLast;
810         focusLast->d_func()->focusNext = q;
811     }
812 }
813 
setLayout_helper(QGraphicsLayout * l)814 void QGraphicsWidgetPrivate::setLayout_helper(QGraphicsLayout *l)
815 {
816     delete (this->layout);
817     layout = l;
818     if (!l) {
819         Q_Q(QGraphicsWidget);
820         q->updateGeometry();
821     }
822 }
823 
width() const824 qreal QGraphicsWidgetPrivate::width() const
825 {
826     Q_Q(const QGraphicsWidget);
827     return q->geometry().width();
828 }
829 
setWidth(qreal w)830 void QGraphicsWidgetPrivate::setWidth(qreal w)
831 {
832     if (qIsNaN(w))
833         return;
834     Q_Q(QGraphicsWidget);
835     if (q->geometry().width() == w)
836         return;
837 
838     q->setGeometry(QRectF(q->x(), q->y(), w, height()));
839 }
840 
resetWidth()841 void QGraphicsWidgetPrivate::resetWidth()
842 {
843     Q_Q(QGraphicsWidget);
844     q->setGeometry(QRectF(q->x(), q->y(), 0, height()));
845 }
846 
height() const847 qreal QGraphicsWidgetPrivate::height() const
848 {
849     Q_Q(const QGraphicsWidget);
850     return q->geometry().height();
851 }
852 
setHeight(qreal h)853 void QGraphicsWidgetPrivate::setHeight(qreal h)
854 {
855     if (qIsNaN(h))
856         return;
857     Q_Q(QGraphicsWidget);
858     if (q->geometry().height() == h)
859         return;
860 
861     q->setGeometry(QRectF(q->x(), q->y(), width(), h));
862 }
863 
resetHeight()864 void QGraphicsWidgetPrivate::resetHeight()
865 {
866     Q_Q(QGraphicsWidget);
867     q->setGeometry(QRectF(q->x(), q->y(), width(), 0));
868 }
869 
setGeometryFromSetPos()870 void QGraphicsWidgetPrivate::setGeometryFromSetPos()
871 {
872     if (inSetGeometry)
873         return;
874     Q_Q(QGraphicsWidget);
875     inSetPos = 1;
876     // Ensure setGeometry is called (avoid recursion when setPos is
877     // called from within setGeometry).
878     q->setGeometry(QRectF(pos, q->size()));
879     inSetPos = 0 ;
880 }
881 
882 QT_END_NAMESPACE
883