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

source code of qtbase/src/widgets/graphicsview/qgraphicswidget_p.cpp