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
56QT_BEGIN_NAMESPACE
57
58void 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
82qreal 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*/
92QGraphicsWidgetPrivate::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
111QGraphicsWidgetPrivate::~QGraphicsWidgetPrivate()
112{
113}
114
115/*!
116 \internal
117
118 Ensures that margins is allocated.
119 This function must be called before any dereferencing.
120*/
121void 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*/
133void 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*/
145void QGraphicsWidgetPrivate::ensureWindowData()
146{
147 if (!windowData)
148 windowData = qt_make_unique<WindowData>();
149}
150
151void QGraphicsWidgetPrivate::setPalette_helper(const QPalette &palette)
152{
153 if (this->palette == palette && this->palette.resolveMask() == palette.resolveMask())
154 return;
155 updatePalette(palette);
156}
157
158void QGraphicsWidgetPrivate::resolvePalette(uint inheritedMask)
159{
160 inheritedPaletteResolveMask = inheritedMask;
161 QPalette naturalPalette = naturalWidgetPalette();
162 QPalette resolvedPalette = palette.resolve(naturalPalette);
163 updatePalette(resolvedPalette);
164}
165
166void 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.resolveMask() | 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
194void 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
216void 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
233QPalette 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.setResolveMask(0);
243 return palette;
244}
245
246void QGraphicsWidgetPrivate::setFont_helper(const QFont &font)
247{
248 if (this->font == font && this->font.resolveMask() == font.resolveMask())
249 return;
250 updateFont(font);
251}
252
253void 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
264void 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.resolveMask() | 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
294QFont 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.setResolveMask(0);
304 return naturalFont;
305}
306
307void 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
330void 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
352void 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
383void 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 */
410static 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
442static qreal minimumWidthForHeight(qreal height, qreal minw, qreal maxw,
443 const QGraphicsWidget *widget)
444{
445 return minimumHeightForWidth(height, minw, maxw, widget, false);
446}
447
448static 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
495static 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
572void 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
642void 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
722void 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
748bool 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 */
756void 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
814void 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
824qreal QGraphicsWidgetPrivate::width() const
825{
826 Q_Q(const QGraphicsWidget);
827 return q->geometry().width();
828}
829
830void 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
841void QGraphicsWidgetPrivate::resetWidth()
842{
843 Q_Q(QGraphicsWidget);
844 q->setGeometry(QRectF(q->x(), q->y(), 0, height()));
845}
846
847qreal QGraphicsWidgetPrivate::height() const
848{
849 Q_Q(const QGraphicsWidget);
850 return q->geometry().height();
851}
852
853void 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
864void QGraphicsWidgetPrivate::resetHeight()
865{
866 Q_Q(QGraphicsWidget);
867 q->setGeometry(QRectF(q->x(), q->y(), width(), 0));
868}
869
870void 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
882QT_END_NAMESPACE
883