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/*!
5 \class QMdiSubWindow
6 \brief The QMdiSubWindow class provides a subwindow class for
7 QMdiArea.
8 \since 4.3
9 \ingroup mainwindow-classes
10 \inmodule QtWidgets
11
12 QMdiSubWindow represents a top-level window in a QMdiArea, and consists
13 of a title bar with window decorations, an internal widget, and
14 (depending on the current style) a window frame and a size
15 grip. QMdiSubWindow has its own layout, which consists of the
16 title bar and a center area for the internal widget.
17
18 \image qmdisubwindowlayout.png
19
20 The most common way to construct a QMdiSubWindow is to call
21 QMdiArea::addSubWindow() with the internal widget as the argument.
22 You can also create a subwindow yourself, and set an internal
23 widget by calling setWidget().
24
25 You use the same API when programming with subwindows as with
26 regular top-level windows (e.g., you can call functions such as
27 show(), hide(), showMaximized(), and setWindowTitle()).
28
29 \section1 Subwindow Handling
30
31 QMdiSubWindow also supports behavior specific to subwindows in
32 an MDI area.
33
34 By default, each QMdiSubWindow is visible inside the MDI area
35 viewport when moved around, but it is also possible to specify
36 transparent window movement and resizing behavior, where only
37 the outline of a subwindow is updated during these operations.
38 The setOption() function is used to enable this behavior.
39
40 The isShaded() function detects whether the subwindow is
41 currently shaded (i.e., the window is collapsed so that only the
42 title bar is visible). To enter shaded mode, call showShaded().
43 QMdiSubWindow emits the windowStateChanged() signal whenever the
44 window state has changed (e.g., when the window becomes minimized,
45 or is restored). It also emits aboutToActivate() before it is
46 activated.
47
48 In keyboard-interactive mode, the windows are moved and resized
49 with the keyboard. You can enter this mode through the system menu
50 of the window. The keyboardSingleStep and keyboardPageStep
51 properties control the distance the widget is moved or resized for
52 each keypress event. When shift is pressed down page step is used;
53 otherwise single step is used.
54
55 You can also change the active window with the keyboard. By
56 pressing the control and tab keys at the same time, the next
57 (using the current \l{QMdiArea::}{WindowOrder}) subwindow will be
58 activated. By pressing control, shift, and tab, you will activate
59 the previous window. This is equivalent to calling
60 \l{QMdiArea::}{activateNextSubWindow()} and
61 \l{QMdiArea::}{activatePreviousSubWindow()}. Note that these
62 shortcuts overrides global shortcuts, but not the \l{QMdiArea}s
63 shortcuts.
64
65 \sa QMdiArea
66*/
67
68/*!
69 \enum QMdiSubWindow::SubWindowOption
70
71 This enum describes options that customize the behavior
72 of QMdiSubWindow.
73
74 \omitvalue AllowOutsideAreaHorizontally
75 \omitvalue AllowOutsideAreaVertically
76
77 \value RubberBandResize If you enable this option, a rubber band
78 control is used to represent the subwindow's outline, and the user
79 resizes this instead of the subwindow itself.
80 As a result, the subwindow maintains its original position and size
81 until the resize operation has been completed, at which time it will
82 receive a single QResizeEvent.
83 By default, this option is disabled.
84
85 \value RubberBandMove If you enable this option, a rubber band
86 control is used to represent the subwindow's outline, and the user
87 moves this instead of the subwindow itself.
88 As a result, the subwindow remains in its original position until
89 the move operation has completed, at which time a QMoveEvent is
90 sent to the window. By default, this option is disabled.
91*/
92
93/*!
94 \fn QMdiSubWindow::windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState)
95
96 QMdiSubWindow emits this signal after the window state changes. \a
97 oldState is the window state before it changed, and \a newState is the
98 new, current state.
99*/
100
101/*!
102 \fn QMdiSubWindow::aboutToActivate()
103
104 QMdiSubWindow emits this signal immediately before it is
105 activated. After the subwindow has been activated, the QMdiArea that
106 manages the subwindow will also emit the
107 \l{QMdiArea::}{subWindowActivated()} signal.
108
109 \sa QMdiArea::subWindowActivated()
110*/
111
112#include "qmdisubwindow_p.h"
113
114#include <QApplication>
115#include <QStylePainter>
116#include <QVBoxLayout>
117#include <QMouseEvent>
118#if QT_CONFIG(whatsthis)
119#include <QWhatsThis>
120#endif
121#if QT_CONFIG(tooltip)
122#include <QToolTip>
123#endif
124#if QT_CONFIG(mainwindow)
125#include <QMainWindow>
126#endif
127#include <QScrollBar>
128#include <QDebug>
129#include <QMdiArea>
130#include <QScopedValueRollback>
131#if QT_CONFIG(action)
132# include <qaction.h>
133#endif
134#if QT_CONFIG(menu)
135#include <QMenu>
136#endif
137#include <QProxyStyle>
138
139QT_BEGIN_NAMESPACE
140
141using namespace Qt::StringLiterals;
142using namespace std::chrono_literals;
143
144using namespace QMdi;
145
146static const QStyle::SubControl SubControls[] =
147{
148 QStyle::SC_TitleBarLabel, // 1
149 QStyle::SC_TitleBarSysMenu, // 2
150 QStyle::SC_TitleBarMinButton, // 3
151 QStyle::SC_TitleBarMaxButton, // 4
152 QStyle::SC_TitleBarShadeButton, // 5
153 QStyle::SC_TitleBarCloseButton, // 6
154 QStyle::SC_TitleBarNormalButton, // 7
155 QStyle::SC_TitleBarUnshadeButton, // 8
156 QStyle::SC_TitleBarContextHelpButton // 9
157};
158static const int NumSubControls = sizeof(SubControls) / sizeof(SubControls[0]);
159
160static const Qt::WindowFlags CustomizeWindowFlags =
161 Qt::FramelessWindowHint
162 | Qt::CustomizeWindowHint
163 | Qt::WindowTitleHint
164 | Qt::WindowSystemMenuHint
165 | Qt::WindowMinimizeButtonHint
166 | Qt::WindowMaximizeButtonHint
167 | Qt::WindowMinMaxButtonsHint;
168
169
170static const int BoundaryMargin = 5;
171
172static inline bool isMacStyle(QStyle *style)
173{
174 auto proxyStyle = qobject_cast<QProxyStyle *>(object: style);
175 auto styleToCheck = proxyStyle ? proxyStyle->baseStyle() : style;
176 return styleToCheck->inherits(classname: "QMacStyle");
177}
178
179static inline int getMoveDeltaComponent(uint cflags, uint moveFlag, uint resizeFlag,
180 int delta, int maxDelta, int minDelta)
181{
182 if (cflags & moveFlag) {
183 if (delta > 0)
184 return (cflags & resizeFlag) ? qMin(a: delta, b: maxDelta) : delta;
185 return (cflags & resizeFlag) ? qMax(a: delta, b: minDelta) : delta;
186 }
187 return 0;
188}
189
190static inline int getResizeDeltaComponent(uint cflags, uint resizeFlag,
191 uint resizeReverseFlag, int delta)
192{
193 if (cflags & resizeFlag) {
194 if (cflags & resizeReverseFlag)
195 return -delta;
196 return delta;
197 }
198 return 0;
199}
200
201static inline bool isChildOfQMdiSubWindow(const QWidget *child)
202{
203 Q_ASSERT(child);
204 QWidget *parent = child->parentWidget();
205 while (parent) {
206 if (qobject_cast<QMdiSubWindow *>(object: parent))
207 return true;
208 parent = parent->parentWidget();
209 }
210 return false;
211}
212
213static inline bool isChildOfTabbedQMdiArea(const QMdiSubWindow *child)
214{
215 Q_ASSERT(child);
216 if (QMdiArea *mdiArea = child->mdiArea()) {
217 if (mdiArea->viewMode() == QMdiArea::TabbedView)
218 return true;
219 }
220 return false;
221}
222
223template<typename T>
224static inline ControlElement<T> *ptr(QWidget *widget)
225{
226 if (widget && widget->qt_metacast("ControlElement")
227 && strcmp(widget->metaObject()->className(), T::staticMetaObject.className()) == 0) {
228 return static_cast<ControlElement<T> *>(widget);
229 }
230 return nullptr;
231}
232
233QString QMdiSubWindowPrivate::originalWindowTitleHelper() const
234{
235 Q_Q(const QMdiSubWindow);
236 // QTBUG-92240: When DontMaximizeSubWindowOnActivation is set and
237 // there is another subwindow maximized, use its original title.
238 if (auto *mdiArea = q->mdiArea()) {
239 const auto &subWindows = mdiArea->subWindowList();
240 for (auto *subWindow : subWindows) {
241 if (subWindow != q && subWindow->isMaximized()) {
242 auto *subWindowD = static_cast<QMdiSubWindowPrivate *>(qt_widget_private(widget: subWindow));
243 if (!subWindowD->originalTitle.isNull())
244 return subWindowD->originalTitle;
245 }
246 }
247 }
248 return q->window()->windowTitle();
249}
250
251QString QMdiSubWindowPrivate::originalWindowTitle()
252{
253 if (originalTitle.isNull()) {
254 originalTitle = originalWindowTitleHelper();
255 if (originalTitle.isNull())
256 originalTitle = ""_L1;
257 }
258 return originalTitle;
259}
260
261void QMdiSubWindowPrivate::setNewWindowTitle()
262{
263 Q_Q(QMdiSubWindow);
264 QString childTitle = q->windowTitle();
265 if (childTitle.isEmpty())
266 return;
267 QString original = originalWindowTitle();
268 if (!original.isEmpty()) {
269 if (!original.contains(s: QMdiSubWindow::tr(s: "- [%1]").arg(a: childTitle))) {
270 auto title = QMdiSubWindow::tr(s: "%1 - [%2]").arg(args&: original, args&: childTitle);
271 ignoreWindowTitleChange = true;
272 q->window()->setWindowTitle(title);
273 ignoreWindowTitleChange = false;
274 }
275
276 } else {
277 ignoreWindowTitleChange = true;
278 q->window()->setWindowTitle(childTitle);
279 ignoreWindowTitleChange = false;
280 }
281}
282
283static inline bool isHoverControl(QStyle::SubControl control)
284{
285 return control != QStyle::SC_None && control != QStyle::SC_TitleBarLabel;
286}
287
288#if QT_CONFIG(tooltip)
289static void showToolTip(QHelpEvent *helpEvent, QWidget *widget, const QStyleOptionComplex &opt,
290 QStyle::ComplexControl complexControl, QStyle::SubControl subControl)
291{
292 Q_ASSERT(helpEvent);
293 Q_ASSERT(helpEvent->type() == QEvent::ToolTip);
294 Q_ASSERT(widget);
295
296 if (widget->style()->styleHint(stylehint: QStyle::SH_TitleBar_ShowToolTipsOnButtons, opt: &opt, widget))
297 return;
298
299 // Convert CC_MdiControls to CC_TitleBar. Sub controls of different complex
300 // controls cannot be in the same switch as they might have the same value.
301 if (complexControl == QStyle::CC_MdiControls) {
302 if (subControl == QStyle::SC_MdiMinButton)
303 subControl = QStyle::SC_TitleBarMinButton;
304 else if (subControl == QStyle::SC_MdiCloseButton)
305 subControl = QStyle::SC_TitleBarCloseButton;
306 else if (subControl == QStyle::SC_MdiNormalButton)
307 subControl = QStyle::SC_TitleBarNormalButton;
308 else
309 subControl = QStyle::SC_None;
310 }
311
312 // Don't change the tooltip for the base widget itself.
313 if (subControl == QStyle::SC_None)
314 return;
315
316 QString toolTip;
317
318 switch (subControl) {
319 case QStyle::SC_TitleBarMinButton:
320 toolTip = QMdiSubWindow::tr(s: "Minimize");
321 break;
322 case QStyle::SC_TitleBarMaxButton:
323 toolTip = QMdiSubWindow::tr(s: "Maximize");
324 break;
325 case QStyle::SC_TitleBarUnshadeButton:
326 toolTip = QMdiSubWindow::tr(s: "Unshade");
327 break;
328 case QStyle::SC_TitleBarShadeButton:
329 toolTip = QMdiSubWindow::tr(s: "Shade");
330 break;
331 case QStyle::SC_TitleBarNormalButton:
332 if (widget->isMaximized() || !qobject_cast<QMdiSubWindow *>(object: widget))
333 toolTip = QMdiSubWindow::tr(s: "Restore Down");
334 else
335 toolTip = QMdiSubWindow::tr(s: "Restore");
336 break;
337 case QStyle::SC_TitleBarCloseButton:
338 toolTip = QMdiSubWindow::tr(s: "Close");
339 break;
340 case QStyle::SC_TitleBarContextHelpButton:
341 toolTip = QMdiSubWindow::tr(s: "Help");
342 break;
343 case QStyle::SC_TitleBarSysMenu:
344 toolTip = QMdiSubWindow::tr(s: "Menu");
345 break;
346 default:
347 break;
348 }
349
350 const QRect rect = widget->style()->subControlRect(cc: complexControl, opt: &opt, sc: subControl, widget);
351 QToolTip::showText(pos: helpEvent->globalPos(), text: toolTip, w: widget, rect);
352}
353#endif // QT_CONFIG(tooltip)
354
355namespace QMdi {
356/*
357 \class ControlLabel
358 \internal
359*/
360class ControlLabel : public QWidget
361{
362 Q_OBJECT
363public:
364 ControlLabel(QWidget *parent = nullptr);
365
366 QSize sizeHint() const override;
367
368signals:
369 void _q_clicked();
370 void _q_doubleClicked();
371
372protected:
373 bool event(QEvent *event) override;
374 void paintEvent(QPaintEvent *paintEvent) override;
375 void mousePressEvent(QMouseEvent *mouseEvent) override;
376 void mouseDoubleClickEvent(QMouseEvent *mouseEvent) override;
377 void mouseReleaseEvent(QMouseEvent *mouseEvent) override;
378
379private:
380 QPixmap label;
381 bool isPressed;
382 void updateWindowIcon();
383};
384} // namespace QMdi
385
386ControlLabel::ControlLabel(QWidget *parent)
387 : QWidget(parent), isPressed(false)
388{
389 setFocusPolicy(Qt::NoFocus);
390 updateWindowIcon();
391 setFixedSize(label.deviceIndependentSize().toSize());
392}
393
394/*
395 \internal
396*/
397QSize ControlLabel::sizeHint() const
398{
399 return label.deviceIndependentSize().toSize();
400}
401
402/*
403 \internal
404*/
405bool ControlLabel::event(QEvent *event)
406{
407 if (event->type() == QEvent::WindowIconChange)
408 updateWindowIcon();
409 else if (event->type() == QEvent::StyleChange) {
410 updateWindowIcon();
411 setFixedSize(label.size());
412 }
413#if QT_CONFIG(tooltip)
414 else if (event->type() == QEvent::ToolTip) {
415 QStyleOptionTitleBar options;
416 options.initFrom(w: this);
417 showToolTip(helpEvent: static_cast<QHelpEvent *>(event), widget: this, opt: options,
418 complexControl: QStyle::CC_TitleBar, subControl: QStyle::SC_TitleBarSysMenu);
419 }
420#endif
421 return QWidget::event(event);
422}
423
424/*
425 \internal
426*/
427void ControlLabel::paintEvent(QPaintEvent * /*paintEvent*/)
428{
429 QPainter painter(this);
430 painter.drawPixmap(x: 0, y: 0, pm: label);
431}
432
433/*
434 \internal
435*/
436void ControlLabel::mousePressEvent(QMouseEvent *mouseEvent)
437{
438 if (mouseEvent->button() != Qt::LeftButton) {
439 mouseEvent->ignore();
440 return;
441 }
442 isPressed = true;
443}
444
445/*
446 \internal
447*/
448void ControlLabel::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
449{
450 if (mouseEvent->button() != Qt::LeftButton) {
451 mouseEvent->ignore();
452 return;
453 }
454 isPressed = false;
455 emit _q_doubleClicked();
456}
457
458/*
459 \internal
460*/
461void ControlLabel::mouseReleaseEvent(QMouseEvent *mouseEvent)
462{
463 if (mouseEvent->button() != Qt::LeftButton) {
464 mouseEvent->ignore();
465 return;
466 }
467 if (isPressed) {
468 isPressed = false;
469 emit _q_clicked();
470 }
471}
472
473/*
474 \internal
475*/
476void ControlLabel::updateWindowIcon()
477{
478 QIcon menuIcon = windowIcon();
479 if (menuIcon.isNull())
480 menuIcon = style()->standardIcon(standardIcon: QStyle::SP_TitleBarMenuButton, option: nullptr, widget: parentWidget());
481 const int iconSize = style()->pixelMetric(metric: QStyle::PM_TitleBarButtonIconSize, option: nullptr, widget: parentWidget());
482 label = menuIcon.pixmap(size: QSize(iconSize, iconSize), devicePixelRatio: devicePixelRatio());
483 update();
484}
485
486namespace QMdi {
487/*
488 \class ControllerWidget
489 \internal
490*/
491class ControllerWidget : public QWidget
492{
493 Q_OBJECT
494public:
495 ControllerWidget(QWidget *parent = nullptr);
496 QSize sizeHint() const override;
497 void setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible);
498 inline bool hasVisibleControls() const
499 {
500 return (visibleControls & QStyle::SC_MdiMinButton)
501 || (visibleControls & QStyle::SC_MdiNormalButton)
502 || (visibleControls & QStyle::SC_MdiCloseButton);
503 }
504
505signals:
506 void _q_minimize();
507 void _q_restore();
508 void _q_close();
509
510protected:
511 void paintEvent(QPaintEvent *event) override;
512 void mousePressEvent(QMouseEvent *event) override;
513 void mouseReleaseEvent(QMouseEvent *event) override;
514 void mouseMoveEvent(QMouseEvent *event) override;
515 void leaveEvent(QEvent *event) override;
516 bool event(QEvent *event) override;
517
518private:
519 QStyle::SubControl activeControl;
520 QStyle::SubControl hoverControl;
521 QStyle::SubControls visibleControls;
522 void initStyleOption(QStyleOptionComplex *option) const;
523 inline QStyle::SubControl getSubControl(const QPoint &pos) const
524 {
525 QStyleOptionComplex opt;
526 initStyleOption(option: &opt);
527 return style()->hitTestComplexControl(cc: QStyle::CC_MdiControls, opt: &opt, pt: pos, widget: this);
528 }
529};
530} // namespace QMdi
531
532/*
533 \internal
534*/
535ControllerWidget::ControllerWidget(QWidget *parent)
536 : QWidget(parent),
537 activeControl(QStyle::SC_None),
538 hoverControl(QStyle::SC_None),
539 visibleControls(QStyle::SC_None)
540{
541 setFocusPolicy(Qt::NoFocus);
542 setSizePolicy(hor: QSizePolicy::Minimum, ver: QSizePolicy::Minimum);
543 setMouseTracking(true);
544}
545
546/*
547 \internal
548*/
549QSize ControllerWidget::sizeHint() const
550{
551 ensurePolished();
552 QStyleOptionComplex opt;
553 initStyleOption(option: &opt);
554 const int buttonSize = style()->pixelMetric(metric: QStyle::PM_TitleBarButtonSize, option: &opt, widget: this);
555 QSize size(3 * buttonSize, buttonSize);
556 return style()->sizeFromContents(ct: QStyle::CT_MdiControls, opt: &opt, contentsSize: size, w: this);
557}
558
559void ControllerWidget::setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible)
560{
561 QStyle::SubControl subControl = QStyle::SC_None;
562
563 // Map action from QMdiSubWindowPrivate::WindowStateAction to QStyle::SubControl.
564 if (action == QMdiSubWindowPrivate::MaximizeAction)
565 subControl = QStyle::SC_MdiNormalButton;
566 else if (action == QMdiSubWindowPrivate::CloseAction)
567 subControl = QStyle::SC_MdiCloseButton;
568 else if (action == QMdiSubWindowPrivate::MinimizeAction)
569 subControl = QStyle::SC_MdiMinButton;
570
571 if (subControl == QStyle::SC_None)
572 return;
573
574 visibleControls.setFlag(flag: subControl, on: visible && !(visibleControls & subControl));
575}
576
577/*
578 \internal
579*/
580void ControllerWidget::paintEvent(QPaintEvent * /*paintEvent*/)
581{
582 QStyleOptionComplex opt;
583 initStyleOption(option: &opt);
584 if (activeControl == hoverControl) {
585 opt.activeSubControls = activeControl;
586 opt.state |= QStyle::State_Sunken;
587 } else if (hoverControl != QStyle::SC_None && (activeControl == QStyle::SC_None)) {
588 opt.activeSubControls = hoverControl;
589 opt.state |= QStyle::State_MouseOver;
590 }
591 QPainter painter(this);
592 style()->drawComplexControl(cc: QStyle::CC_MdiControls, opt: &opt, p: &painter, widget: this);
593}
594
595/*
596 \internal
597*/
598void ControllerWidget::mousePressEvent(QMouseEvent *event)
599{
600 if (event->button() != Qt::LeftButton) {
601 event->ignore();
602 return;
603 }
604 activeControl = getSubControl(pos: event->position().toPoint());
605 update();
606}
607
608/*
609 \internal
610*/
611void ControllerWidget::mouseReleaseEvent(QMouseEvent *event)
612{
613 if (event->button() != Qt::LeftButton) {
614 event->ignore();
615 return;
616 }
617
618 QStyle::SubControl under_mouse = getSubControl(pos: event->position().toPoint());
619 if (under_mouse == activeControl) {
620 switch (activeControl) {
621 case QStyle::SC_MdiCloseButton:
622 emit _q_close();
623 break;
624 case QStyle::SC_MdiNormalButton:
625 emit _q_restore();
626 break;
627 case QStyle::SC_MdiMinButton:
628 emit _q_minimize();
629 break;
630 default:
631 break;
632 }
633 }
634
635 activeControl = QStyle::SC_None;
636 update();
637}
638
639/*
640 \internal
641*/
642void ControllerWidget::mouseMoveEvent(QMouseEvent *event)
643{
644 QStyle::SubControl under_mouse = getSubControl(pos: event->position().toPoint());
645 //test if hover state changes
646 if (hoverControl != under_mouse) {
647 hoverControl = under_mouse;
648 update();
649 }
650}
651
652/*
653 \internal
654*/
655void ControllerWidget::leaveEvent(QEvent * /*event*/)
656{
657 hoverControl = QStyle::SC_None;
658 update();
659}
660
661/*
662 \internal
663*/
664bool ControllerWidget::event(QEvent *event)
665{
666#if QT_CONFIG(tooltip)
667 if (event->type() == QEvent::ToolTip) {
668 QStyleOptionComplex opt;
669 initStyleOption(option: &opt);
670 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
671 showToolTip(helpEvent, widget: this, opt, complexControl: QStyle::CC_MdiControls, subControl: getSubControl(pos: helpEvent->pos()));
672 }
673#endif // QT_CONFIG(tooltip)
674 return QWidget::event(event);
675}
676
677/*
678 \internal
679*/
680void ControllerWidget::initStyleOption(QStyleOptionComplex *option) const
681{
682 option->initFrom(w: this);
683 option->subControls = visibleControls;
684 option->activeSubControls = QStyle::SC_None;
685}
686
687/*
688 \internal
689*/
690ControlContainer::ControlContainer(QMdiSubWindow *mdiChild)
691 : QObject(mdiChild),
692 previousLeft(nullptr),
693 previousRight(nullptr),
694#if QT_CONFIG(menubar)
695 m_menuBar(nullptr),
696#endif
697 mdiChild(mdiChild)
698{
699 Q_ASSERT(mdiChild);
700
701 m_controllerWidget = new ControlElement<ControllerWidget>(mdiChild);
702 connect(sender: m_controllerWidget, SIGNAL(_q_close()), receiver: mdiChild, SLOT(close()));
703 connect(sender: m_controllerWidget, SIGNAL(_q_restore()), receiver: mdiChild, SLOT(showNormal()));
704 connect(sender: m_controllerWidget, SIGNAL(_q_minimize()), receiver: mdiChild, SLOT(showMinimized()));
705
706 m_menuLabel = new ControlElement<ControlLabel>(mdiChild);
707 m_menuLabel->setWindowIcon(mdiChild->windowIcon());
708#if QT_CONFIG(menu)
709 connect(sender: m_menuLabel, SIGNAL(_q_clicked()), receiver: mdiChild, SLOT(showSystemMenu()));
710#endif
711 connect(sender: m_menuLabel, SIGNAL(_q_doubleClicked()), receiver: mdiChild, SLOT(close()));
712}
713
714ControlContainer::~ControlContainer()
715{
716#if QT_CONFIG(menubar)
717 removeButtonsFromMenuBar();
718#endif
719 delete m_menuLabel;
720 m_menuLabel = nullptr;
721 delete m_controllerWidget;
722 m_controllerWidget = nullptr;
723}
724
725#if QT_CONFIG(menubar)
726/*
727 \internal
728*/
729QMenuBar *QMdiSubWindowPrivate::menuBar() const
730{
731#if !QT_CONFIG(mainwindow)
732 return nullptr;
733#else
734 Q_Q(const QMdiSubWindow);
735 if (!q->isMaximized() || drawTitleBarWhenMaximized() || isChildOfTabbedQMdiArea(child: q))
736 return nullptr;
737
738 if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(object: q->window()))
739 return mainWindow->menuBar();
740
741 return nullptr;
742#endif
743}
744
745/*
746 \internal
747*/
748void ControlContainer::showButtonsInMenuBar(QMenuBar *menuBar)
749{
750 if (!menuBar || !mdiChild || mdiChild->windowFlags() & Qt::FramelessWindowHint)
751 return;
752 m_menuBar = menuBar;
753
754 if (m_menuLabel && mdiChild->windowFlags() & Qt::WindowSystemMenuHint) {
755 QWidget *currentLeft = menuBar->cornerWidget(corner: Qt::TopLeftCorner);
756 if (currentLeft)
757 currentLeft->hide();
758 if (currentLeft != m_menuLabel) {
759 menuBar->setCornerWidget(w: m_menuLabel, corner: Qt::TopLeftCorner);
760 previousLeft = currentLeft;
761 }
762 m_menuLabel->show();
763 }
764 ControllerWidget *controllerWidget = qobject_cast<ControllerWidget *>(object: m_controllerWidget);
765 if (controllerWidget && controllerWidget->hasVisibleControls()) {
766 QWidget *currentRight = menuBar->cornerWidget(corner: Qt::TopRightCorner);
767 if (currentRight)
768 currentRight->hide();
769 if (currentRight != m_controllerWidget) {
770 menuBar->setCornerWidget(w: m_controllerWidget, corner: Qt::TopRightCorner);
771 previousRight = currentRight;
772 }
773 m_controllerWidget->show();
774 }
775 mdiChild->d_func()->setNewWindowTitle();
776}
777
778/*
779 \internal
780*/
781void ControlContainer::removeButtonsFromMenuBar(QMenuBar *menuBar)
782{
783 if (menuBar && menuBar != m_menuBar) {
784 // m_menubar was deleted while sub-window was maximized
785 previousRight = nullptr;
786 previousLeft = nullptr;
787 m_menuBar = menuBar;
788 }
789
790 if (!m_menuBar || !mdiChild || qt_widget_private(widget: mdiChild->window())->data.in_destructor)
791 return;
792
793 QMdiSubWindow *child = nullptr;
794 if (m_controllerWidget) {
795 QWidget *currentRight = m_menuBar->cornerWidget(corner: Qt::TopRightCorner);
796 if (currentRight == m_controllerWidget) {
797 if (ControlElement<ControllerWidget> *ce = ptr<ControllerWidget>(widget: previousRight)) {
798 if (!ce->mdiChild || !ce->mdiChild->isMaximized())
799 previousRight = nullptr;
800 else
801 child = ce->mdiChild;
802 }
803 m_menuBar->setCornerWidget(w: previousRight, corner: Qt::TopRightCorner);
804 if (previousRight) {
805 previousRight->show();
806 previousRight = nullptr;
807 }
808 }
809 m_controllerWidget->hide();
810 m_controllerWidget->setParent(nullptr);
811 }
812 if (m_menuLabel) {
813 QWidget *currentLeft = m_menuBar->cornerWidget(corner: Qt::TopLeftCorner);
814 if (currentLeft == m_menuLabel) {
815 if (ControlElement<ControlLabel> *ce = ptr<ControlLabel>(widget: previousLeft)) {
816 if (!ce->mdiChild || !ce->mdiChild->isMaximized())
817 previousLeft = nullptr;
818 else if (!child)
819 child = mdiChild;
820 }
821 m_menuBar->setCornerWidget(w: previousLeft, corner: Qt::TopLeftCorner);
822 if (previousLeft) {
823 previousLeft->show();
824 previousLeft = nullptr;
825 }
826 }
827 m_menuLabel->hide();
828 m_menuLabel->setParent(nullptr);
829 }
830 m_menuBar->update();
831 if (child)
832 child->d_func()->setNewWindowTitle();
833 else if (mdiChild)
834 mdiChild->window()->setWindowTitle(mdiChild->d_func()->originalWindowTitle());
835}
836
837#endif // QT_CONFIG(menubar)
838
839void ControlContainer::updateWindowIcon(const QIcon &windowIcon)
840{
841 if (m_menuLabel)
842 m_menuLabel->setWindowIcon(windowIcon);
843}
844
845/*!
846 \internal
847*/
848QMdiSubWindowPrivate::QMdiSubWindowPrivate()
849 : baseWidget(nullptr),
850 restoreFocusWidget(nullptr),
851 controlContainer(nullptr),
852#if QT_CONFIG(sizegrip)
853 sizeGrip(nullptr),
854#endif
855#if QT_CONFIG(rubberband)
856 rubberBand(nullptr),
857#endif
858 userMinimumSize(0,0),
859 resizeEnabled(true),
860 moveEnabled(true),
861 isInInteractiveMode(false),
862#if QT_CONFIG(rubberband)
863 isInRubberBandMode(false),
864#endif
865 isShadeMode(false),
866 ignoreWindowTitleChange(false),
867 ignoreNextActivationEvent(false),
868 activationEnabled(true),
869 isShadeRequestFromMinimizeMode(false),
870 isMaximizeMode(false),
871 isWidgetHiddenByUs(false),
872 isActive(false),
873 isExplicitlyDeactivated(false),
874 keyboardSingleStep(5),
875 keyboardPageStep(20),
876 currentOperation(None),
877 hoveredSubControl(QStyle::SC_None),
878 activeSubControl(QStyle::SC_None),
879 focusInReason(Qt::ActiveWindowFocusReason)
880{
881 initOperationMap();
882}
883
884/*!
885 \internal
886*/
887void QMdiSubWindowPrivate::_q_updateStaysOnTopHint()
888{
889#if QT_CONFIG(action)
890 Q_Q(QMdiSubWindow);
891 if (QAction *senderAction = qobject_cast<QAction *>(object: q->sender())) {
892 if (senderAction->isChecked()) {
893 q->setWindowFlags(q->windowFlags() | Qt::WindowStaysOnTopHint);
894 q->raise();
895 } else {
896 q->setWindowFlags(q->windowFlags() & ~Qt::WindowStaysOnTopHint);
897 q->lower();
898 }
899 }
900#endif // QT_CONFIG(action)
901}
902
903/*!
904 \internal
905*/
906void QMdiSubWindowPrivate::_q_enterInteractiveMode()
907{
908#ifndef QT_NO_ACTION
909 Q_Q(QMdiSubWindow);
910 QAction *action = qobject_cast<QAction *>(object: q->sender());
911 if (!action)
912 return;
913
914 QPoint pressPos;
915 if (actions[MoveAction] && actions[MoveAction] == action) {
916 currentOperation = Move;
917 pressPos = QPoint(q->width() / 2, titleBarHeight() - 1);
918 } else if (actions[ResizeAction] && actions[ResizeAction] == action) {
919 currentOperation = q->isLeftToRight() ? BottomRightResize : BottomLeftResize;
920 int offset = q->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowFrameWidth, option: nullptr, widget: q) / 2;
921 int x = q->isLeftToRight() ? q->width() - offset : offset;
922 pressPos = QPoint(x, q->height() - offset);
923 } else {
924 return;
925 }
926
927 updateCursor();
928#ifndef QT_NO_CURSOR
929 q->cursor().setPos(q->mapToGlobal(pressPos));
930#endif
931 mousePressPosition = q->mapToParent(pressPos);
932 oldGeometry = q->geometry();
933 isInInteractiveMode = true;
934 q->setFocus();
935#if QT_CONFIG(rubberband)
936 if ((q->testOption(QMdiSubWindow::RubberBandResize)
937 && (currentOperation == BottomRightResize || currentOperation == BottomLeftResize))
938 || (q->testOption(QMdiSubWindow::RubberBandMove) && currentOperation == Move)) {
939 enterRubberBandMode();
940 } else
941#endif // QT_CONFIG(rubberband)
942 {
943 q->grabMouse();
944 }
945#endif // QT_NO_ACTION
946}
947
948/*!
949 \internal
950*/
951void QMdiSubWindowPrivate::_q_processFocusChanged(QWidget *old, QWidget *now)
952{
953 Q_UNUSED(old);
954 Q_Q(QMdiSubWindow);
955 if (now && (now == q || q->isAncestorOf(child: now))) {
956 if (now == q && !isInInteractiveMode)
957 setFocusWidget();
958 setActive(activate: true);
959 }
960}
961
962/*!
963 \internal
964*/
965void QMdiSubWindowPrivate::leaveInteractiveMode()
966{
967 Q_Q(QMdiSubWindow);
968#if QT_CONFIG(rubberband)
969 if (isInRubberBandMode)
970 leaveRubberBandMode();
971 else
972#endif
973 q->releaseMouse();
974 isInInteractiveMode = false;
975 currentOperation = None;
976 updateDirtyRegions();
977 updateCursor();
978 if (baseWidget && baseWidget->focusWidget())
979 baseWidget->focusWidget()->setFocus();
980}
981
982/*!
983 \internal
984*/
985void QMdiSubWindowPrivate::removeBaseWidget()
986{
987 if (!baseWidget)
988 return;
989
990 Q_Q(QMdiSubWindow);
991 baseWidget->removeEventFilter(obj: q);
992 if (layout)
993 layout->removeWidget(w: baseWidget);
994 if (baseWidget->windowTitle() == q->windowTitle()) {
995 ignoreWindowTitleChange = true;
996 q->setWindowTitle(QString());
997 ignoreWindowTitleChange = false;
998 q->setWindowModified(false);
999 }
1000 lastChildWindowTitle.clear();
1001 // QTBUG-47993: parent widget can be reset before this call
1002 if (baseWidget->parentWidget() == q)
1003 baseWidget->setParent(nullptr);
1004 baseWidget = nullptr;
1005 isWidgetHiddenByUs = false;
1006}
1007
1008/*!
1009 \internal
1010*/
1011void QMdiSubWindowPrivate::initOperationMap()
1012{
1013 operationMap.insert(key: Move, value: OperationInfo(HMove | VMove, Qt::ArrowCursor, false));
1014 operationMap.insert(key: TopResize, value: OperationInfo(VMove | VResize | VResizeReverse, Qt::SizeVerCursor));
1015 operationMap.insert(key: BottomResize, value: OperationInfo(VResize, Qt::SizeVerCursor));
1016 operationMap.insert(key: LeftResize, value: OperationInfo(HMove | HResize | HResizeReverse, Qt::SizeHorCursor));
1017 operationMap.insert(key: RightResize, value: OperationInfo(HResize, Qt::SizeHorCursor));
1018 operationMap.insert(key: TopLeftResize, value: OperationInfo(HMove | VMove | HResize | VResize | VResizeReverse
1019 | HResizeReverse, Qt::SizeFDiagCursor));
1020 operationMap.insert(key: TopRightResize, value: OperationInfo(VMove | HResize | VResize
1021 | VResizeReverse, Qt::SizeBDiagCursor));
1022 operationMap.insert(key: BottomLeftResize, value: OperationInfo(HMove | HResize | VResize | HResizeReverse,
1023 Qt::SizeBDiagCursor));
1024 operationMap.insert(key: BottomRightResize, value: OperationInfo(HResize | VResize, Qt::SizeFDiagCursor));
1025}
1026
1027#if QT_CONFIG(menu)
1028
1029/*!
1030 \internal
1031*/
1032void QMdiSubWindowPrivate::createSystemMenu()
1033{
1034 Q_Q(QMdiSubWindow);
1035 Q_ASSERT_X(q, "QMdiSubWindowPrivate::createSystemMenu",
1036 "You can NOT call this function before QMdiSubWindow's ctor");
1037 systemMenu = new QMenu(q);
1038 systemMenu->installEventFilter(filterObj: q);
1039 const QStyle *style = q->style();
1040 addToSystemMenu(RestoreAction, text: QMdiSubWindow::tr(s: "&Restore"), SLOT(showNormal()));
1041 actions[RestoreAction]->setIcon(style->standardIcon(standardIcon: QStyle::SP_TitleBarNormalButton, option: nullptr, widget: q));
1042 actions[RestoreAction]->setEnabled(false);
1043 addToSystemMenu(MoveAction, text: QMdiSubWindow::tr(s: "&Move"), SLOT(_q_enterInteractiveMode()));
1044 addToSystemMenu(ResizeAction, text: QMdiSubWindow::tr(s: "&Size"), SLOT(_q_enterInteractiveMode()));
1045 addToSystemMenu(MinimizeAction, text: QMdiSubWindow::tr(s: "Mi&nimize"), SLOT(showMinimized()));
1046 actions[MinimizeAction]->setIcon(style->standardIcon(standardIcon: QStyle::SP_TitleBarMinButton, option: nullptr, widget: q));
1047 addToSystemMenu(MaximizeAction, text: QMdiSubWindow::tr(s: "Ma&ximize"), SLOT(showMaximized()));
1048 actions[MaximizeAction]->setIcon(style->standardIcon(standardIcon: QStyle::SP_TitleBarMaxButton, option: nullptr, widget: q));
1049 addToSystemMenu(StayOnTopAction, text: QMdiSubWindow::tr(s: "Stay on &Top"), SLOT(_q_updateStaysOnTopHint()));
1050 actions[StayOnTopAction]->setCheckable(true);
1051 systemMenu->addSeparator();
1052 addToSystemMenu(CloseAction, text: QMdiSubWindow::tr(s: "&Close"), SLOT(close()));
1053 actions[CloseAction]->setIcon(style->standardIcon(standardIcon: QStyle::SP_TitleBarCloseButton, option: nullptr, widget: q));
1054#if !defined(QT_NO_SHORTCUT)
1055 actions[CloseAction]->setShortcuts(QKeySequence::Close);
1056#endif
1057 updateActions();
1058}
1059#endif
1060
1061/*!
1062 \internal
1063*/
1064void QMdiSubWindowPrivate::updateCursor()
1065{
1066#ifndef QT_NO_CURSOR
1067 Q_Q(QMdiSubWindow);
1068 if (isMacStyle(style: q->style()))
1069 return;
1070
1071 if (currentOperation == None) {
1072 q->unsetCursor();
1073 return;
1074 }
1075
1076 if (currentOperation == Move || operationMap.find(key: currentOperation).value().hover) {
1077 q->setCursor(operationMap.find(key: currentOperation).value().cursorShape);
1078 return;
1079 }
1080#endif
1081}
1082
1083/*!
1084 \internal
1085*/
1086void QMdiSubWindowPrivate::updateDirtyRegions()
1087{
1088 // No update necessary
1089 if (!parent)
1090 return;
1091
1092 for (OperationInfoMap::iterator it = operationMap.begin(), end = operationMap.end(); it != end; ++it)
1093 it.value().region = getRegion(operation: it.key());
1094}
1095
1096/*!
1097 \internal
1098*/
1099void QMdiSubWindowPrivate::updateGeometryConstraints()
1100{
1101 Q_Q(QMdiSubWindow);
1102 if (!parent)
1103 return;
1104
1105 internalMinimumSize = (!q->isMinimized() && !q->minimumSize().isNull())
1106 ? q->minimumSize() : q->minimumSizeHint();
1107 int margin, minWidth;
1108 sizeParameters(margin: &margin, minWidth: &minWidth);
1109 q->setContentsMargins(left: margin, top: titleBarHeight(), right: margin, bottom: margin);
1110 if (q->isMaximized() || (q->isMinimized() && !q->isShaded())) {
1111 moveEnabled = false;
1112 resizeEnabled = false;
1113 } else {
1114 moveEnabled = true;
1115 if ((q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || q->isShaded())
1116 resizeEnabled = false;
1117 else
1118 resizeEnabled = true;
1119 }
1120 updateDirtyRegions();
1121}
1122
1123/*!
1124 \internal
1125*/
1126void QMdiSubWindowPrivate::updateMask()
1127{
1128 Q_Q(QMdiSubWindow);
1129 if (!q->mask().isEmpty())
1130 q->clearMask();
1131
1132 if (!parent)
1133 return;
1134
1135 if ((q->isMaximized() && !drawTitleBarWhenMaximized())
1136 || q->windowFlags() & Qt::FramelessWindowHint)
1137 return;
1138
1139 if (!resizeTimer.isActive())
1140 cachedStyleOptions = titleBarOptions();
1141 cachedStyleOptions.rect = q->rect();
1142 QStyleHintReturnMask frameMask;
1143 q->style()->styleHint(stylehint: QStyle::SH_WindowFrame_Mask, opt: &cachedStyleOptions, widget: q, returnData: &frameMask);
1144 if (!frameMask.region.isEmpty())
1145 q->setMask(frameMask.region);
1146}
1147
1148/*!
1149 \internal
1150*/
1151void QMdiSubWindowPrivate::setNewGeometry(const QPoint &pos)
1152{
1153 Q_Q(QMdiSubWindow);
1154 Q_ASSERT(currentOperation != None);
1155 Q_ASSERT(parent);
1156
1157 uint cflags = operationMap.find(key: currentOperation).value().changeFlags;
1158 int posX = pos.x();
1159 int posY = pos.y();
1160
1161 const bool restrictHorizontal = !q->testOption(QMdiSubWindow::AllowOutsideAreaHorizontally);
1162 const bool restrictVertical = !q->testOption(QMdiSubWindow::AllowOutsideAreaVertically);
1163
1164 if (restrictHorizontal || restrictVertical) {
1165 QRect parentRect = q->parentWidget()->rect();
1166 if (restrictVertical && (cflags & VResizeReverse || currentOperation == Move)) {
1167 posY = qMin(a: qMax(a: mousePressPosition.y() - oldGeometry.y(), b: posY),
1168 b: parentRect.height() - BoundaryMargin);
1169 }
1170 if (currentOperation == Move) {
1171 if (restrictHorizontal)
1172 posX = qMin(a: qMax(a: BoundaryMargin, b: posX), b: parentRect.width() - BoundaryMargin);
1173 if (restrictVertical)
1174 posY = qMin(a: posY, b: parentRect.height() - BoundaryMargin);
1175 } else {
1176 if (restrictHorizontal) {
1177 if (cflags & HResizeReverse)
1178 posX = qMax(a: mousePressPosition.x() - oldGeometry.x(), b: posX);
1179 else
1180 posX = qMin(a: parentRect.width() - (oldGeometry.x() + oldGeometry.width()
1181 - mousePressPosition.x()), b: posX);
1182 }
1183 if (restrictVertical && !(cflags & VResizeReverse)) {
1184 posY = qMin(a: parentRect.height() - (oldGeometry.y() + oldGeometry.height()
1185 - mousePressPosition.y()), b: posY);
1186 }
1187 }
1188 }
1189
1190 QRect geometry;
1191 if (cflags & (HMove | VMove)) {
1192 int dx = getMoveDeltaComponent(cflags, moveFlag: HMove, resizeFlag: HResize, delta: posX - mousePressPosition.x(),
1193 maxDelta: oldGeometry.width() - internalMinimumSize.width(),
1194 minDelta: oldGeometry.width() - q->maximumWidth());
1195 int dy = getMoveDeltaComponent(cflags, moveFlag: VMove, resizeFlag: VResize, delta: posY - mousePressPosition.y(),
1196 maxDelta: oldGeometry.height() - internalMinimumSize.height(),
1197 minDelta: oldGeometry.height() - q->maximumHeight());
1198 geometry.setTopLeft(oldGeometry.topLeft() + QPoint(dx, dy));
1199 } else {
1200 geometry.setTopLeft(q->geometry().topLeft());
1201 }
1202
1203 if (cflags & (HResize | VResize)) {
1204 int dx = getResizeDeltaComponent(cflags, resizeFlag: HResize, resizeReverseFlag: HResizeReverse,
1205 delta: posX - mousePressPosition.x());
1206 int dy = getResizeDeltaComponent(cflags, resizeFlag: VResize, resizeReverseFlag: VResizeReverse,
1207 delta: posY - mousePressPosition.y());
1208 geometry.setSize(oldGeometry.size() + QSize(dx, dy));
1209 } else {
1210 geometry.setSize(q->geometry().size());
1211 }
1212
1213 setNewGeometry(&geometry);
1214}
1215
1216/*!
1217 \internal
1218*/
1219void QMdiSubWindowPrivate::setMinimizeMode()
1220{
1221 Q_Q(QMdiSubWindow);
1222 Q_ASSERT(parent);
1223
1224 ensureWindowState(state: Qt::WindowMinimized);
1225 isShadeRequestFromMinimizeMode = true;
1226 q->showShaded();
1227 isShadeRequestFromMinimizeMode = false;
1228
1229 moveEnabled = false;
1230#ifndef QT_NO_ACTION
1231 setEnabled(MoveAction, enable: moveEnabled);
1232#endif
1233
1234 Q_ASSERT(q->windowState() & Qt::WindowMinimized);
1235 Q_ASSERT(!(q->windowState() & Qt::WindowMaximized));
1236 // This should be a valid assert, but people can actually re-implement
1237 // setVisible and do crazy stuff, so we're not guaranteed that
1238 // the widget is hidden after calling hide().
1239 // Q_ASSERT(baseWidget ? baseWidget->isHidden() : true);
1240
1241 setActive(activate: true);
1242}
1243
1244/*!
1245 \internal
1246*/
1247void QMdiSubWindowPrivate::setNormalMode()
1248{
1249 Q_Q(QMdiSubWindow);
1250 Q_ASSERT(parent);
1251
1252 isShadeMode = false;
1253 isMaximizeMode = false;
1254
1255 ensureWindowState(state: Qt::WindowNoState);
1256#if QT_CONFIG(menubar)
1257 removeButtonsFromMenuBar();
1258#endif
1259
1260 // Hide the window before we change the geometry to avoid multiple resize
1261 // events and wrong window state.
1262 const bool wasVisible = q->isVisible();
1263 if (wasVisible)
1264 q->setVisible(false);
1265
1266 // Restore minimum size if set by user.
1267 if (!userMinimumSize.isNull()) {
1268 q->setMinimumSize(userMinimumSize);
1269 userMinimumSize = QSize(0, 0);
1270 }
1271
1272 // Show the internal widget if it was hidden by us,
1273 if (baseWidget && isWidgetHiddenByUs) {
1274 baseWidget->show();
1275 isWidgetHiddenByUs = false;
1276 }
1277
1278 updateGeometryConstraints();
1279 QRect newGeometry = oldGeometry;
1280 newGeometry.setSize(restoreSize.expandedTo(otherSize: internalMinimumSize));
1281 q->setGeometry(newGeometry);
1282
1283 if (wasVisible)
1284 q->setVisible(true);
1285
1286 // Invalidate the restore size.
1287 restoreSize.setWidth(-1);
1288 restoreSize.setHeight(-1);
1289
1290#if QT_CONFIG(sizegrip)
1291 setSizeGripVisible(true);
1292#endif
1293
1294#ifndef QT_NO_ACTION
1295 setEnabled(MoveAction, enable: true);
1296 setEnabled(MaximizeAction, enable: true);
1297 setEnabled(MinimizeAction, enable: true);
1298 setEnabled(RestoreAction, enable: false);
1299 setEnabled(ResizeAction, enable: resizeEnabled);
1300#endif // QT_NO_ACTION
1301
1302 Q_ASSERT(!(q_func()->windowState() & Qt::WindowMinimized));
1303 // This sub-window can be maximized when shown above if not the
1304 // QMdiArea::DontMaximizeSubWindowOnActionvation is set. Make sure
1305 // the Qt::WindowMaximized flag is set accordingly.
1306 Q_ASSERT((isMaximizeMode && q_func()->windowState() & Qt::WindowMaximized)
1307 || (!isMaximizeMode && !(q_func()->windowState() & Qt::WindowMaximized)));
1308 Q_ASSERT(!isShadeMode);
1309
1310 setActive(activate: true);
1311 restoreFocus();
1312 updateMask();
1313}
1314
1315inline void QMdiSubWindowPrivate::storeFocusWidget()
1316{
1317 if (QWidget *focus = QApplication::focusWidget()) {
1318 if (!restoreFocusWidget && q_func()->isAncestorOf(child: focus))
1319 restoreFocusWidget = focus;
1320 }
1321}
1322
1323/*!
1324 \internal
1325*/
1326void QMdiSubWindowPrivate::setMaximizeMode()
1327{
1328 Q_Q(QMdiSubWindow);
1329 Q_ASSERT(parent);
1330
1331 ensureWindowState(state: Qt::WindowMaximized);
1332 isShadeMode = false;
1333 isMaximizeMode = true;
1334
1335 storeFocusWidget();
1336
1337#if QT_CONFIG(sizegrip)
1338 setSizeGripVisible(false);
1339#endif
1340
1341 // Store old geometry and set restore size if not already set.
1342 if (!restoreSize.isValid()) {
1343 oldGeometry = q->geometry();
1344 restoreSize.setWidth(oldGeometry.width());
1345 restoreSize.setHeight(oldGeometry.height());
1346 }
1347
1348 // Hide the window before we change the geometry to avoid multiple resize
1349 // events and wrong window state.
1350 const bool wasVisible = q->isVisible();
1351 if (wasVisible)
1352 q->setVisible(false);
1353
1354 // Show the internal widget if it was hidden by us.
1355 if (baseWidget && isWidgetHiddenByUs) {
1356 baseWidget->show();
1357 isWidgetHiddenByUs = false;
1358 }
1359
1360 updateGeometryConstraints();
1361
1362 if (wasVisible) {
1363#if QT_CONFIG(menubar)
1364 if (QMenuBar *mBar = menuBar())
1365 showButtonsInMenuBar(menuBar: mBar);
1366 else
1367#endif
1368 if (!controlContainer)
1369 controlContainer = new ControlContainer(q);
1370 }
1371
1372 QWidget *parent = q->parentWidget();
1373 QRect availableRect = parent->contentsRect();
1374
1375 // Adjust geometry if the sub-window is inside a scroll area.
1376 QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(object: parent->parentWidget());
1377 if (scrollArea && scrollArea->viewport() == parent) {
1378 QScrollBar *hbar = scrollArea->horizontalScrollBar();
1379 QScrollBar *vbar = scrollArea->verticalScrollBar();
1380 const int xOffset = hbar ? hbar->value() : 0;
1381 const int yOffset = vbar ? vbar->value() : 0;
1382 availableRect.adjust(dx1: -xOffset, dy1: -yOffset, dx2: -xOffset, dy2: -yOffset);
1383 oldGeometry.adjust(dx1: xOffset, dy1: yOffset, dx2: xOffset, dy2: yOffset);
1384 }
1385
1386 setNewGeometry(&availableRect);
1387 // QWidget::setGeometry will reset Qt::WindowMaximized so we have to update it here.
1388 ensureWindowState(state: Qt::WindowMaximized);
1389
1390 if (wasVisible)
1391 q->setVisible(true);
1392
1393 resizeEnabled = false;
1394 moveEnabled = false;
1395
1396#ifndef QT_NO_ACTION
1397 setEnabled(MoveAction, enable: moveEnabled);
1398 setEnabled(MaximizeAction, enable: false);
1399 setEnabled(MinimizeAction, enable: true);
1400 setEnabled(RestoreAction, enable: true);
1401 setEnabled(ResizeAction, enable: resizeEnabled);
1402#endif // QT_NO_ACTION
1403
1404 Q_ASSERT(q->windowState() & Qt::WindowMaximized);
1405 Q_ASSERT(!(q->windowState() & Qt::WindowMinimized));
1406
1407 restoreFocus();
1408 updateMask();
1409}
1410
1411/*!
1412 \internal
1413*/
1414void QMdiSubWindowPrivate::setActive(bool activate, bool changeFocus)
1415{
1416 Q_Q(QMdiSubWindow);
1417 if (!parent || !activationEnabled)
1418 return;
1419
1420 if (activate && !isActive && q->isEnabled()) {
1421 isActive = true;
1422 isExplicitlyDeactivated = false;
1423 Qt::WindowStates oldWindowState = q->windowState();
1424 ensureWindowState(state: Qt::WindowActive);
1425 emit q->aboutToActivate();
1426#if QT_CONFIG(menubar)
1427 if (QMenuBar *mBar = menuBar())
1428 showButtonsInMenuBar(menuBar: mBar);
1429#endif
1430 Q_ASSERT(isActive);
1431 emit q->windowStateChanged(oldState: oldWindowState, newState: q->windowState());
1432 } else if (!activate && isActive) {
1433 isActive = false;
1434 Qt::WindowStates oldWindowState = q->windowState();
1435 q->overrideWindowState(state: q->windowState() & ~Qt::WindowActive);
1436 if (changeFocus) {
1437 storeFocusWidget();
1438 QWidget *focusWidget = QApplication::focusWidget();
1439 if (focusWidget && (focusWidget == q || q->isAncestorOf(child: focusWidget)))
1440 focusWidget->clearFocus();
1441 }
1442 if (baseWidget)
1443 baseWidget->overrideWindowState(state: baseWidget->windowState() & ~Qt::WindowActive);
1444 Q_ASSERT(!isActive);
1445 emit q->windowStateChanged(oldState: oldWindowState, newState: q->windowState());
1446 }
1447
1448 if (activate && isActive && q->isEnabled() && !q->hasFocus()
1449 && !q->isAncestorOf(child: QApplication::focusWidget())) {
1450 if (changeFocus)
1451 setFocusWidget();
1452 ensureWindowState(state: Qt::WindowActive);
1453 }
1454
1455 int frameWidth = q->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowFrameWidth, option: nullptr, widget: q);
1456 int titleBarHeight = this->titleBarHeight();
1457 QRegion windowDecoration = QRegion(0, 0, q->width(), q->height());
1458 windowDecoration -= QRegion(frameWidth, titleBarHeight, q->width() - 2 * frameWidth,
1459 q->height() - titleBarHeight - frameWidth);
1460
1461 // Make sure we don't use cached style options if we get
1462 // resize events right before activation/deactivation.
1463 if (resizeTimer.isActive()) {
1464 resizeTimer.stop();
1465 updateDirtyRegions();
1466 }
1467
1468 q->update(windowDecoration);
1469}
1470
1471/*!
1472 \internal
1473*/
1474void QMdiSubWindowPrivate::processClickedSubControl()
1475{
1476 Q_Q(QMdiSubWindow);
1477 switch (activeSubControl) {
1478 case QStyle::SC_TitleBarContextHelpButton:
1479#if QT_CONFIG(whatsthis)
1480 QWhatsThis::enterWhatsThisMode();
1481#endif
1482 break;
1483 case QStyle::SC_TitleBarShadeButton:
1484 q->showShaded();
1485 hoveredSubControl = QStyle::SC_TitleBarUnshadeButton;
1486 break;
1487 case QStyle::SC_TitleBarUnshadeButton:
1488 if (q->isShaded())
1489 hoveredSubControl = QStyle::SC_TitleBarShadeButton;
1490 q->showNormal();
1491 break;
1492 case QStyle::SC_TitleBarMinButton:
1493 if (isMacStyle(style: q->style())) {
1494 if (q->isMinimized())
1495 q->showNormal();
1496 else
1497 q->showMinimized();
1498 break;
1499 }
1500
1501 q->showMinimized();
1502 break;
1503 case QStyle::SC_TitleBarNormalButton:
1504 if (q->isShaded())
1505 hoveredSubControl = QStyle::SC_TitleBarMinButton;
1506 q->showNormal();
1507 break;
1508 case QStyle::SC_TitleBarMaxButton:
1509 if (isMacStyle(style: q->style())) {
1510 if (q->isMaximized())
1511 q->showNormal();
1512 else
1513 q->showMaximized();
1514 break;
1515 }
1516
1517 q->showMaximized();
1518 break;
1519 case QStyle::SC_TitleBarCloseButton:
1520 q->close();
1521 break;
1522 default:
1523 break;
1524 }
1525}
1526
1527/*!
1528 \internal
1529*/
1530QRegion QMdiSubWindowPrivate::getRegion(Operation operation) const
1531{
1532 Q_Q(const QMdiSubWindow);
1533 int width = q->width();
1534 int height = q->height();
1535 int titleBarHeight = this->titleBarHeight();
1536 int frameWidth = q->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowFrameWidth, option: nullptr, widget: q);
1537 int cornerConst = titleBarHeight - frameWidth;
1538 int titleBarConst = 2 * titleBarHeight;
1539
1540 if (operation == Move) {
1541 QStyleOptionTitleBar titleBarOptions = this->titleBarOptions();
1542 QRegion move(frameWidth, frameWidth, width - 2 * frameWidth, cornerConst);
1543 // Depending on which window flags are set, activated sub controllers will
1544 // be subtracted from the 'move' region.
1545 for (int i = 0; i < NumSubControls; ++i) {
1546 if (SubControls[i] == QStyle::SC_TitleBarLabel)
1547 continue;
1548 move -= QRegion(q->style()->subControlRect(cc: QStyle::CC_TitleBar, opt: &titleBarOptions,
1549 sc: SubControls[i]));
1550 }
1551 return move;
1552 }
1553
1554 QRegion region;
1555 if (isMacStyle(style: q->style()))
1556 return region;
1557
1558 switch (operation) {
1559 case TopResize:
1560 region = QRegion(titleBarHeight, 0, width - titleBarConst, frameWidth);
1561 break;
1562 case BottomResize:
1563 region = QRegion(titleBarHeight, height - frameWidth, width - titleBarConst, frameWidth);
1564 break;
1565 case LeftResize:
1566 region = QRegion(0, titleBarHeight, frameWidth, height - titleBarConst);
1567 break;
1568 case RightResize:
1569 region = QRegion(width - frameWidth, titleBarHeight, frameWidth, height - titleBarConst);
1570 break;
1571 case TopLeftResize:
1572 region = QRegion(0, 0, titleBarHeight, titleBarHeight)
1573 - QRegion(frameWidth, frameWidth, cornerConst, cornerConst);
1574 break;
1575 case TopRightResize:
1576 region = QRegion(width - titleBarHeight, 0, titleBarHeight, titleBarHeight)
1577 - QRegion(width - titleBarHeight, frameWidth, cornerConst, cornerConst);
1578 break;
1579 case BottomLeftResize:
1580 region = QRegion(0, height - titleBarHeight, titleBarHeight, titleBarHeight)
1581 - QRegion(frameWidth, height - titleBarHeight, cornerConst, cornerConst);
1582 break;
1583 case BottomRightResize:
1584 region = QRegion(width - titleBarHeight, height - titleBarHeight, titleBarHeight, titleBarHeight)
1585 - QRegion(width - titleBarHeight, height - titleBarHeight, cornerConst, cornerConst);
1586 break;
1587 default:
1588 break;
1589 }
1590
1591 return region;
1592}
1593
1594/*!
1595 \internal
1596*/
1597QMdiSubWindowPrivate::Operation QMdiSubWindowPrivate::getOperation(const QPoint &pos) const
1598{
1599 OperationInfoMap::const_iterator it;
1600 for (it = operationMap.constBegin(); it != operationMap.constEnd(); ++it)
1601 if (it.value().region.contains(p: pos))
1602 return it.key();
1603 return None;
1604}
1605
1606extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
1607
1608/*!
1609 \internal
1610*/
1611QStyleOptionTitleBar QMdiSubWindowPrivate::titleBarOptions() const
1612{
1613 Q_Q(const QMdiSubWindow);
1614 QStyleOptionTitleBar titleBarOptions;
1615 titleBarOptions.initFrom(w: q);
1616 if (activeSubControl != QStyle::SC_None) {
1617 if (hoveredSubControl == activeSubControl) {
1618 titleBarOptions.state |= QStyle::State_Sunken;
1619 titleBarOptions.activeSubControls = activeSubControl;
1620 }
1621 } else if (autoRaise() && hoveredSubControl != QStyle::SC_None
1622 && hoveredSubControl != QStyle::SC_TitleBarLabel) {
1623 titleBarOptions.state |= QStyle::State_MouseOver;
1624 titleBarOptions.activeSubControls = hoveredSubControl;
1625 } else {
1626 titleBarOptions.state &= ~QStyle::State_MouseOver;
1627 titleBarOptions.activeSubControls = QStyle::SC_None;
1628 }
1629
1630 titleBarOptions.subControls = QStyle::SC_All;
1631 titleBarOptions.titleBarFlags = q->windowFlags();
1632 titleBarOptions.titleBarState = q->windowState();
1633 titleBarOptions.palette = titleBarPalette;
1634 titleBarOptions.icon = menuIcon;
1635
1636 if (isActive) {
1637 titleBarOptions.state |= QStyle::State_Active;
1638 titleBarOptions.titleBarState |= QStyle::State_Active;
1639 titleBarOptions.palette.setCurrentColorGroup(QPalette::Active);
1640 } else {
1641 titleBarOptions.state &= ~QStyle::State_Active;
1642 titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive);
1643 }
1644
1645 int border = hasBorder(options: titleBarOptions) ? 4 : 0;
1646 int paintHeight = titleBarHeight(options: titleBarOptions);
1647 paintHeight -= q->isMinimized() ? 2 * border : border;
1648 titleBarOptions.rect = QRect(border, border, q->width() - 2 * border, paintHeight);
1649
1650 if (!windowTitle.isEmpty()) {
1651 // Set the text here before asking for the width of the title bar label
1652 // in case people uses the actual text to calculate the width.
1653 titleBarOptions.text = windowTitle;
1654 titleBarOptions.fontMetrics = QFontMetrics(font);
1655 int width = q->style()->subControlRect(cc: QStyle::CC_TitleBar, opt: &titleBarOptions,
1656 sc: QStyle::SC_TitleBarLabel, widget: q).width();
1657 // Set elided text if we don't have enough space for the entire title.
1658 titleBarOptions.text = titleBarOptions.fontMetrics.elidedText(text: windowTitle, mode: Qt::ElideRight, width);
1659 }
1660 return titleBarOptions;
1661}
1662
1663/*!
1664 \internal
1665*/
1666void QMdiSubWindowPrivate::ensureWindowState(Qt::WindowState state)
1667{
1668 Q_Q(QMdiSubWindow);
1669 Qt::WindowStates windowStates = q->windowState() | state;
1670 switch (state) {
1671 case Qt::WindowMinimized:
1672 windowStates &= ~Qt::WindowMaximized;
1673 windowStates &= ~Qt::WindowFullScreen;
1674 windowStates &= ~Qt::WindowNoState;
1675 break;
1676 case Qt::WindowMaximized:
1677 windowStates &= ~Qt::WindowMinimized;
1678 windowStates &= ~Qt::WindowFullScreen;
1679 windowStates &= ~Qt::WindowNoState;
1680 break;
1681 case Qt::WindowNoState:
1682 windowStates &= ~Qt::WindowMinimized;
1683 windowStates &= ~Qt::WindowMaximized;
1684 windowStates &= ~Qt::WindowFullScreen;
1685 break;
1686 default:
1687 break;
1688 }
1689 if (baseWidget) {
1690 if (!(baseWidget->windowState() & Qt::WindowActive) && windowStates & Qt::WindowActive)
1691 baseWidget->overrideWindowState(state: windowStates & ~Qt::WindowActive);
1692 else
1693 baseWidget->overrideWindowState(state: windowStates);
1694 }
1695 q->overrideWindowState(state: windowStates);
1696}
1697
1698/*!
1699 \internal
1700*/
1701int QMdiSubWindowPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const
1702{
1703 Q_Q(const QMdiSubWindow);
1704 if (!parent || q->windowFlags() & Qt::FramelessWindowHint
1705 || (q->isMaximized() && !drawTitleBarWhenMaximized())) {
1706 return 0;
1707 }
1708
1709 int height = q->style()->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: &options, widget: q);
1710 if (hasBorder(options))
1711 height += q->isMinimized() ? 8 : 4;
1712 return height;
1713}
1714
1715/*!
1716 \internal
1717*/
1718void QMdiSubWindowPrivate::sizeParameters(int *margin, int *minWidth) const
1719{
1720 Q_Q(const QMdiSubWindow);
1721 Qt::WindowFlags flags = q->windowFlags();
1722 if (!parent || flags & Qt::FramelessWindowHint) {
1723 *margin = 0;
1724 *minWidth = 0;
1725 return;
1726 }
1727
1728 if (q->isMaximized() && !drawTitleBarWhenMaximized())
1729 *margin = 0;
1730 else
1731 *margin = q->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowFrameWidth, option: nullptr, widget: q);
1732
1733 QStyleOptionTitleBar opt = this->titleBarOptions();
1734 int tempWidth = 0;
1735 for (int i = 0; i < NumSubControls; ++i) {
1736 if (SubControls[i] == QStyle::SC_TitleBarLabel) {
1737 tempWidth += 30;
1738 continue;
1739 }
1740 QRect rect = q->style()->subControlRect(cc: QStyle::CC_TitleBar, opt: &opt, sc: SubControls[i], widget: q);
1741 if (!rect.isValid())
1742 continue;
1743 tempWidth += rect.width();
1744 }
1745 *minWidth = tempWidth;
1746}
1747
1748/*!
1749 \internal
1750*/
1751bool QMdiSubWindowPrivate::drawTitleBarWhenMaximized() const
1752{
1753 Q_Q(const QMdiSubWindow);
1754 if (q->window()->testAttribute(attribute: Qt::WA_CanHostQMdiSubWindowTitleBar))
1755 return false;
1756
1757 if (isChildOfTabbedQMdiArea(child: q))
1758 return false;
1759
1760 if (q->style()->styleHint(stylehint: QStyle::SH_Workspace_FillSpaceOnMaximize, opt: nullptr, widget: q))
1761 return true;
1762#if !QT_CONFIG(menubar) || !QT_CONFIG(mainwindow)
1763 Q_UNUSED(isChildOfQMdiSubWindow);
1764 return true;
1765#else
1766 QMainWindow *mainWindow = qobject_cast<QMainWindow *>(object: q->window());
1767 if (!mainWindow || !qobject_cast<QMenuBar *>(object: mainWindow->menuWidget())
1768 || mainWindow->menuWidget()->isHidden())
1769 return true;
1770
1771 return isChildOfQMdiSubWindow(child: q);
1772#endif
1773}
1774
1775#if QT_CONFIG(menubar)
1776
1777/*!
1778 \internal
1779*/
1780void QMdiSubWindowPrivate::showButtonsInMenuBar(QMenuBar *menuBar)
1781{
1782 Q_Q(QMdiSubWindow);
1783 Q_ASSERT(q->isMaximized() && !drawTitleBarWhenMaximized());
1784
1785 if (isChildOfTabbedQMdiArea(child: q))
1786 return;
1787
1788 removeButtonsFromMenuBar();
1789 if (!controlContainer)
1790 controlContainer = new ControlContainer(q);
1791
1792 ignoreWindowTitleChange = true;
1793 controlContainer->showButtonsInMenuBar(menuBar);
1794 ignoreWindowTitleChange = false;
1795
1796 QWidget *topLevelWindow = q->window();
1797 topLevelWindow->setWindowModified(q->isWindowModified());
1798 topLevelWindow->installEventFilter(filterObj: q);
1799
1800 int buttonHeight = 0;
1801 if (controlContainer->controllerWidget())
1802 buttonHeight = controlContainer->controllerWidget()->height();
1803 else if (controlContainer->systemMenuLabel())
1804 buttonHeight = controlContainer->systemMenuLabel()->height();
1805
1806 // This will rarely happen.
1807 if (menuBar && menuBar->height() < buttonHeight
1808 && topLevelWindow->layout()) {
1809 // Make sure topLevelWindow->contentsRect returns correct geometry.
1810 // topLevelWidget->updateGeoemtry will not do the trick here since it will post the event.
1811 QEvent event(QEvent::LayoutRequest);
1812 QCoreApplication::sendEvent(receiver: topLevelWindow, event: &event);
1813 }
1814}
1815
1816/*!
1817 \internal
1818*/
1819void QMdiSubWindowPrivate::removeButtonsFromMenuBar()
1820{
1821 Q_Q(QMdiSubWindow);
1822
1823 if (!controlContainer || isChildOfTabbedQMdiArea(child: q))
1824 return;
1825
1826 QMenuBar *currentMenuBar = nullptr;
1827#if QT_CONFIG(mainwindow)
1828 if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(object: q->window())) {
1829 // NB! We can't use menuBar() here because that one will actually create
1830 // a menubar for us if not set. That's not what we want :-)
1831 currentMenuBar = qobject_cast<QMenuBar *>(object: mainWindow->menuWidget());
1832 }
1833#endif
1834
1835 ignoreWindowTitleChange = true;
1836 controlContainer->removeButtonsFromMenuBar(menuBar: currentMenuBar);
1837 ignoreWindowTitleChange = false;
1838
1839 QWidget *topLevelWindow = q->window();
1840 topLevelWindow->removeEventFilter(obj: q);
1841 if (baseWidget && !drawTitleBarWhenMaximized())
1842 topLevelWindow->setWindowModified(false);
1843 originalTitle.clear();
1844}
1845
1846#endif // QT_CONFIG(menubar)
1847
1848void QMdiSubWindowPrivate::updateWindowTitle(bool isRequestFromChild)
1849{
1850 Q_Q(QMdiSubWindow);
1851 if (isRequestFromChild && !q->windowTitle().isEmpty() && !lastChildWindowTitle.isEmpty()
1852 && lastChildWindowTitle != q->windowTitle()) {
1853 return;
1854 }
1855
1856 QWidget *titleWidget = nullptr;
1857 if (isRequestFromChild)
1858 titleWidget = baseWidget;
1859 else
1860 titleWidget = q;
1861 if (!titleWidget || titleWidget->windowTitle().isEmpty())
1862 return;
1863
1864 ignoreWindowTitleChange = true;
1865 q->setWindowTitle(titleWidget->windowTitle());
1866 if (q->maximizedButtonsWidget())
1867 setNewWindowTitle();
1868 ignoreWindowTitleChange = false;
1869}
1870
1871#if QT_CONFIG(rubberband)
1872void QMdiSubWindowPrivate::enterRubberBandMode()
1873{
1874 Q_Q(QMdiSubWindow);
1875 if (q->isMaximized())
1876 return;
1877 Q_ASSERT(oldGeometry.isValid());
1878 Q_ASSERT(parent);
1879 if (!rubberBand) {
1880 rubberBand = new QRubberBand(QRubberBand::Rectangle, q->parentWidget());
1881 // For accessibility to identify this special widget.
1882 rubberBand->setObjectName("qt_rubberband"_L1);
1883 }
1884 QPoint rubberBandPos = q->mapToParent(QPoint(0, 0));
1885 rubberBand->setGeometry(ax: rubberBandPos.x(), ay: rubberBandPos.y(),
1886 aw: oldGeometry.width(), ah: oldGeometry.height());
1887 rubberBand->show();
1888 isInRubberBandMode = true;
1889 q->grabMouse();
1890}
1891
1892void QMdiSubWindowPrivate::leaveRubberBandMode()
1893{
1894 Q_Q(QMdiSubWindow);
1895 Q_ASSERT(rubberBand);
1896 Q_ASSERT(isInRubberBandMode);
1897 q->releaseMouse();
1898 isInRubberBandMode = false;
1899 q->setGeometry(rubberBand->geometry());
1900 rubberBand->hide();
1901 currentOperation = None;
1902}
1903#endif // QT_CONFIG(rubberband)
1904
1905// Taken from the old QWorkspace (::readColors())
1906QPalette QMdiSubWindowPrivate::desktopPalette() const
1907{
1908 Q_Q(const QMdiSubWindow);
1909 QPalette newPalette = q->palette();
1910
1911 newPalette.setColor(acg: QPalette::Active, acr: QPalette::Highlight,
1912 acolor: newPalette.color(cg: QPalette::Active, cr: QPalette::Highlight));
1913 newPalette.setColor(acg: QPalette::Active, acr: QPalette::Base,
1914 acolor: newPalette.color(cg: QPalette::Active, cr: QPalette::Highlight));
1915 newPalette.setColor(acg: QPalette::Inactive, acr: QPalette::Highlight,
1916 acolor: newPalette.color(cg: QPalette::Inactive, cr: QPalette::Dark));
1917 newPalette.setColor(acg: QPalette::Inactive, acr: QPalette::Base,
1918 acolor: newPalette.color(cg: QPalette::Inactive, cr: QPalette::Dark));
1919 newPalette.setColor(acg: QPalette::Inactive, acr: QPalette::HighlightedText,
1920 acolor: newPalette.color(cg: QPalette::Inactive, cr: QPalette::Window));
1921
1922 return newPalette;
1923}
1924
1925void QMdiSubWindowPrivate::updateActions()
1926{
1927 Qt::WindowFlags windowFlags = q_func()->windowFlags();
1928 // Hide all
1929 for (int i = 0; i < NumWindowStateActions; ++i)
1930 setVisible(WindowStateAction(i), visible: false);
1931
1932#if defined(Q_OS_MACOS) && QT_CONFIG(action)
1933 if (q_func()->style()->inherits("QMacStyle"))
1934 for (int i = 0; i < NumWindowStateActions; ++i)
1935 if (QAction *action = actions[i])
1936 action->setIconVisibleInMenu(false);
1937#endif
1938
1939 if (windowFlags & Qt::FramelessWindowHint)
1940 return;
1941
1942 setVisible(StayOnTopAction, visible: true);
1943 setVisible(MoveAction, visible: moveEnabled);
1944 setVisible(ResizeAction, visible: resizeEnabled);
1945
1946 // CloseAction
1947 if (windowFlags & Qt::WindowSystemMenuHint)
1948 setVisible(CloseAction, visible: true);
1949
1950 // RestoreAction
1951 if (windowFlags & (Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint))
1952 setVisible(RestoreAction, visible: true);
1953
1954 // MinimizeAction
1955 if (windowFlags & Qt::WindowMinimizeButtonHint)
1956 setVisible(MinimizeAction, visible: true);
1957
1958 // MaximizeAction
1959 if (windowFlags & Qt::WindowMaximizeButtonHint)
1960 setVisible(MaximizeAction, visible: true);
1961}
1962
1963void QMdiSubWindowPrivate::setFocusWidget()
1964{
1965 Q_Q(QMdiSubWindow);
1966 if (!baseWidget) {
1967 q->setFocus();
1968 return;
1969 }
1970
1971 // This will give focus to the next child if possible, otherwise
1972 // do nothing, hence it's not possible to tab between windows with
1973 // just hitting tab (unless Qt::TabFocus is removed from the focus policy).
1974 if (focusInReason == Qt::TabFocusReason) {
1975 q->focusNextChild();
1976 return;
1977 }
1978
1979 // Same as above, but gives focus to the previous child.
1980 if (focusInReason == Qt::BacktabFocusReason) {
1981 q->focusPreviousChild();
1982 return;
1983 }
1984
1985 if (!(q->windowState() & Qt::WindowMinimized) && restoreFocus())
1986 return;
1987
1988 if (QWidget *focusWidget = baseWidget->focusWidget()) {
1989 if (!focusWidget->hasFocus() && q->isAncestorOf(child: focusWidget)
1990 && focusWidget->isVisible() && !q->isMinimized()
1991 && focusWidget->focusPolicy() != Qt::NoFocus) {
1992 focusWidget->setFocus();
1993 } else {
1994 q->setFocus();
1995 }
1996 return;
1997 }
1998
1999 QWidget *focusWidget = q->nextInFocusChain();
2000 while (focusWidget && focusWidget != q && focusWidget->focusPolicy() == Qt::NoFocus)
2001 focusWidget = focusWidget->nextInFocusChain();
2002 if (focusWidget && q->isAncestorOf(child: focusWidget))
2003 focusWidget->setFocus();
2004 else if (baseWidget->focusPolicy() != Qt::NoFocus)
2005 baseWidget->setFocus();
2006 else if (!q->hasFocus())
2007 q->setFocus();
2008}
2009
2010bool QMdiSubWindowPrivate::restoreFocus()
2011{
2012 if (restoreFocusWidget.isNull())
2013 return false;
2014 QWidget *candidate = restoreFocusWidget;
2015 restoreFocusWidget.clear();
2016 if (!candidate->hasFocus() && q_func()->isAncestorOf(child: candidate)
2017 && candidate->isVisible()
2018 && candidate->focusPolicy() != Qt::NoFocus) {
2019 candidate->setFocus();
2020 return true;
2021 }
2022 return candidate->hasFocus();
2023}
2024
2025/*!
2026 \internal
2027*/
2028void QMdiSubWindowPrivate::setWindowFlags(Qt::WindowFlags windowFlags)
2029{
2030 Q_Q(QMdiSubWindow);
2031
2032 if (!parent) {
2033 QWidgetPrivate::setWindowFlags(windowFlags);
2034 return;
2035 }
2036
2037 Qt::WindowFlags windowType = windowFlags & Qt::WindowType_Mask;
2038 if (windowType == Qt::Dialog || windowFlags & Qt::MSWindowsFixedSizeDialogHint)
2039 windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint;
2040
2041 // Set standard flags if none of the customize flags are set
2042 if (!(windowFlags & CustomizeWindowFlags))
2043 windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
2044 else if (windowFlags & Qt::FramelessWindowHint && windowFlags & Qt::WindowStaysOnTopHint)
2045 windowFlags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
2046 else if (windowFlags & Qt::FramelessWindowHint)
2047 windowFlags = Qt::FramelessWindowHint;
2048
2049 windowFlags &= ~windowType;
2050 windowFlags &= ~Qt::WindowFullscreenButtonHint;
2051 windowFlags |= Qt::SubWindow;
2052
2053#ifndef QT_NO_ACTION
2054 if (QAction *stayOnTopAction = actions[QMdiSubWindowPrivate::StayOnTopAction]) {
2055 if (windowFlags & Qt::WindowStaysOnTopHint)
2056 stayOnTopAction->setChecked(true);
2057 else
2058 stayOnTopAction->setChecked(false);
2059 }
2060#endif
2061
2062#if QT_CONFIG(sizegrip)
2063 if ((windowFlags & Qt::FramelessWindowHint) && sizeGrip)
2064 delete sizeGrip;
2065#endif
2066
2067 QWidgetPrivate::setWindowFlags(windowFlags);
2068 updateGeometryConstraints();
2069 updateActions();
2070 QSize currentSize = q->size();
2071 if (q->isVisible() && (currentSize.width() < internalMinimumSize.width()
2072 || currentSize.height() < internalMinimumSize.height())) {
2073 q->resize(currentSize.expandedTo(otherSize: internalMinimumSize));
2074 }
2075}
2076
2077void QMdiSubWindowPrivate::setVisible(WindowStateAction action, bool visible)
2078{
2079#ifndef QT_NO_ACTION
2080 if (actions[action])
2081 actions[action]->setVisible(visible);
2082#endif
2083
2084 Q_Q(QMdiSubWindow);
2085 if (!controlContainer)
2086 controlContainer = new ControlContainer(q);
2087
2088 if (ControllerWidget *ctrlWidget = qobject_cast<ControllerWidget *>
2089 (object: controlContainer->controllerWidget())) {
2090 ctrlWidget->setControlVisible(action, visible);
2091 }
2092 q->update();
2093}
2094
2095#ifndef QT_NO_ACTION
2096void QMdiSubWindowPrivate::setEnabled(WindowStateAction action, bool enable)
2097{
2098 if (actions[action])
2099 actions[action]->setEnabled(enable);
2100}
2101
2102#if QT_CONFIG(menu)
2103void QMdiSubWindowPrivate::addToSystemMenu(WindowStateAction action, const QString &text,
2104 const char *slot)
2105{
2106 if (!systemMenu)
2107 return;
2108 actions[action] = systemMenu->addAction(text, receiver: q_func(), member: slot);
2109}
2110#endif
2111#endif // QT_NO_ACTION
2112
2113/*!
2114 \internal
2115*/
2116QSize QMdiSubWindowPrivate::iconSize() const
2117{
2118 Q_Q(const QMdiSubWindow);
2119 if (!parent || q->windowFlags() & Qt::FramelessWindowHint)
2120 return QSize(-1, -1);
2121 return QSize(q->style()->pixelMetric(metric: QStyle::PM_MdiSubWindowMinimizedWidth, option: nullptr, widget: q), titleBarHeight());
2122}
2123
2124#if QT_CONFIG(sizegrip)
2125
2126/*!
2127 \internal
2128*/
2129void QMdiSubWindowPrivate::setSizeGrip(QSizeGrip *newSizeGrip)
2130{
2131 Q_Q(QMdiSubWindow);
2132 if (!newSizeGrip || sizeGrip || q->windowFlags() & Qt::FramelessWindowHint)
2133 return;
2134
2135 if (layout && layout->indexOf(newSizeGrip) != -1)
2136 return;
2137 newSizeGrip->setFixedSize(newSizeGrip->sizeHint());
2138 bool putSizeGripInLayout = layout ? true : false;
2139 if (isMacStyle(style: q->style()))
2140 putSizeGripInLayout = false;
2141 if (putSizeGripInLayout) {
2142 layout->addWidget(w: newSizeGrip);
2143 layout->setAlignment(w: newSizeGrip, alignment: Qt::AlignBottom | Qt::AlignRight);
2144 } else {
2145 newSizeGrip->setParent(q);
2146 newSizeGrip->move(ax: q->isLeftToRight() ? q->width() - newSizeGrip->width() : 0,
2147 ay: q->height() - newSizeGrip->height());
2148 sizeGrip = newSizeGrip;
2149 }
2150 newSizeGrip->raise();
2151 updateGeometryConstraints();
2152 newSizeGrip->installEventFilter(filterObj: q);
2153}
2154
2155/*!
2156 \internal
2157*/
2158void QMdiSubWindowPrivate::setSizeGripVisible(bool visible) const
2159{
2160 // See if we can find any size grips
2161 const QList<QSizeGrip *> sizeGrips = q_func()->findChildren<QSizeGrip *>();
2162 for (QSizeGrip *grip : sizeGrips)
2163 grip->setVisible(visible);
2164}
2165
2166#endif // QT_CONFIG(sizegrip)
2167
2168/*!
2169 \internal
2170*/
2171void QMdiSubWindowPrivate::updateInternalWindowTitle()
2172{
2173 Q_Q(QMdiSubWindow);
2174 if (q->isWindowModified()) {
2175 windowTitle = q->windowTitle();
2176 windowTitle.replace(before: "[*]"_L1, after: "*"_L1);
2177 } else {
2178 windowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
2179 }
2180 q->update(ax: 0, ay: 0, aw: q->width(), ah: titleBarHeight());
2181}
2182
2183/*!
2184 Constructs a new QMdiSubWindow widget. The \a parent and \a
2185 flags arguments are passed to QWidget's constructor.
2186
2187 Instead of using addSubWindow(), it is also simply possible to
2188 use setParent() when you add the subwindow to a QMdiArea.
2189
2190 Note that only \l{QMdiSubWindow}s can be set as children of
2191 QMdiArea; you cannot, for instance, write:
2192
2193 \code
2194 //bad code
2195 QMdiArea mdiArea;
2196 QTextEdit editor(&mdiArea); // invalid child widget
2197 \endcode
2198
2199 \sa QMdiArea::addSubWindow()
2200*/
2201QMdiSubWindow::QMdiSubWindow(QWidget *parent, Qt::WindowFlags flags)
2202 : QWidget(*new QMdiSubWindowPrivate, parent, { })
2203{
2204 Q_D(QMdiSubWindow);
2205#if QT_CONFIG(menu)
2206 d->createSystemMenu();
2207 addActions(actions: d->systemMenu->actions());
2208#endif
2209 d->setWindowFlags(flags);
2210 setBackgroundRole(QPalette::Window);
2211 setAutoFillBackground(true);
2212 setMouseTracking(true);
2213 setLayout(new QVBoxLayout);
2214 setFocusPolicy(Qt::StrongFocus);
2215 layout()->setContentsMargins(QMargins());
2216 d->updateGeometryConstraints();
2217 setAttribute(Qt::WA_Resized, on: false);
2218 d->titleBarPalette = d->desktopPalette();
2219 d->font = QApplication::font(className: "QMdiSubWindowTitleBar");
2220 // We don't want the menu icon by default on mac.
2221#ifndef Q_OS_MAC
2222 if (windowIcon().isNull())
2223 d->menuIcon = style()->standardIcon(standardIcon: QStyle::SP_TitleBarMenuButton, option: nullptr, widget: this);
2224 else
2225 d->menuIcon = windowIcon();
2226#endif
2227 connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
2228 receiver: this, SLOT(_q_processFocusChanged(QWidget*,QWidget*)));
2229}
2230
2231/*!
2232 Destroys the subwindow.
2233
2234 \sa QMdiArea::removeSubWindow()
2235*/
2236QMdiSubWindow::~QMdiSubWindow()
2237{
2238 Q_D(QMdiSubWindow);
2239#if QT_CONFIG(menubar)
2240 d->removeButtonsFromMenuBar();
2241#endif
2242 d->setActive(activate: false);
2243 delete d->controlContainer;
2244}
2245
2246/*!
2247 Sets \a widget as the internal widget of this subwindow. The
2248 internal widget is displayed in the center of the subwindow
2249 beneath the title bar.
2250
2251 QMdiSubWindow takes temporary ownership of \a widget; you do
2252 not have to delete it. Any existing internal widget will be
2253 removed and reparented to the root window.
2254
2255 \sa widget()
2256*/
2257void QMdiSubWindow::setWidget(QWidget *widget)
2258{
2259 Q_D(QMdiSubWindow);
2260 if (!widget) {
2261 d->removeBaseWidget();
2262 return;
2263 }
2264
2265 if (Q_UNLIKELY(widget == d->baseWidget)) {
2266 qWarning(msg: "QMdiSubWindow::setWidget: widget is already set");
2267 return;
2268 }
2269
2270 bool wasResized = testAttribute(attribute: Qt::WA_Resized);
2271 d->removeBaseWidget();
2272
2273 if (QLayout *layout = this->layout())
2274 layout->addWidget(w: widget);
2275 else
2276 widget->setParent(this);
2277
2278#if QT_CONFIG(sizegrip)
2279 QSizeGrip *sizeGrip = widget->findChild<QSizeGrip *>();
2280 if (sizeGrip)
2281 sizeGrip->installEventFilter(filterObj: this);
2282 if (d->sizeGrip)
2283 d->sizeGrip->raise();
2284#endif
2285
2286 d->baseWidget = widget;
2287 d->baseWidget->installEventFilter(filterObj: this);
2288
2289 d->ignoreWindowTitleChange = true;
2290 bool isWindowModified = this->isWindowModified();
2291 if (windowTitle().isEmpty()) {
2292 d->updateWindowTitle(isRequestFromChild: true);
2293 isWindowModified = d->baseWidget->isWindowModified();
2294 }
2295 if (!this->isWindowModified() && isWindowModified && windowTitle().contains(s: "[*]"_L1))
2296 setWindowModified(isWindowModified);
2297 d->lastChildWindowTitle = d->baseWidget->windowTitle();
2298 d->ignoreWindowTitleChange = false;
2299
2300 if (windowIcon().isNull() && !d->baseWidget->windowIcon().isNull())
2301 setWindowIcon(d->baseWidget->windowIcon());
2302
2303 d->updateGeometryConstraints();
2304 if (!wasResized && testAttribute(attribute: Qt::WA_Resized))
2305 setAttribute(Qt::WA_Resized, on: false);
2306}
2307
2308/*!
2309 Returns the current internal widget.
2310
2311 \sa setWidget()
2312*/
2313QWidget *QMdiSubWindow::widget() const
2314{
2315 return d_func()->baseWidget;
2316}
2317
2318
2319/*!
2320 \internal
2321*/
2322QWidget *QMdiSubWindow::maximizedButtonsWidget() const
2323{
2324 Q_D(const QMdiSubWindow);
2325 if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized()
2326 && !isChildOfTabbedQMdiArea(child: this)) {
2327 return d->controlContainer->controllerWidget();
2328 }
2329 return nullptr;
2330}
2331
2332/*!
2333 \internal
2334*/
2335QWidget *QMdiSubWindow::maximizedSystemMenuIconWidget() const
2336{
2337 Q_D(const QMdiSubWindow);
2338 if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized()
2339 && !isChildOfTabbedQMdiArea(child: this)) {
2340 return d->controlContainer->systemMenuLabel();
2341 }
2342 return nullptr;
2343}
2344
2345/*!
2346 Returns \c true if this window is shaded; otherwise returns \c false.
2347
2348 A window is shaded if it is collapsed so that only the title bar is
2349 visible.
2350*/
2351bool QMdiSubWindow::isShaded() const
2352{
2353 return d_func()->isShadeMode;
2354}
2355
2356/*!
2357 If \a on is true, \a option is enabled on the subwindow; otherwise it is
2358 disabled. See SubWindowOption for the effect of each option.
2359
2360 \sa SubWindowOption, testOption()
2361*/
2362void QMdiSubWindow::setOption(SubWindowOption option, bool on)
2363{
2364 Q_D(QMdiSubWindow);
2365 d->options.setFlag(flag: option, on);
2366
2367#if QT_CONFIG(rubberband)
2368 if ((option & (RubberBandResize | RubberBandMove)) && !on && d->isInRubberBandMode)
2369 d->leaveRubberBandMode();
2370#endif
2371}
2372
2373/*!
2374 Returns \c true if \a option is enabled; otherwise returns \c false.
2375
2376 \sa SubWindowOption, setOption()
2377*/
2378bool QMdiSubWindow::testOption(SubWindowOption option) const
2379{
2380 return d_func()->options & option;
2381}
2382
2383/*!
2384 \property QMdiSubWindow::keyboardSingleStep
2385 \brief sets how far a widget should move or resize when using the
2386 keyboard arrow keys.
2387
2388 When in keyboard-interactive mode, you can use the arrow and page keys to
2389 either move or resize the window. This property controls the arrow keys.
2390 The common way to enter keyboard interactive mode is to enter the
2391 subwindow menu, and select either "resize" or "move".
2392
2393 The default keyboard single step value is 5 pixels.
2394
2395 \sa keyboardPageStep
2396*/
2397int QMdiSubWindow::keyboardSingleStep() const
2398{
2399 return d_func()->keyboardSingleStep;
2400}
2401
2402void QMdiSubWindow::setKeyboardSingleStep(int step)
2403{
2404 // Haven't done any boundary check here since negative step only
2405 // means inverted behavior, which is OK if the user want it.
2406 // A step equal to zero means "do nothing".
2407 d_func()->keyboardSingleStep = step;
2408}
2409
2410/*!
2411 \property QMdiSubWindow::keyboardPageStep
2412 \brief sets how far a widget should move or resize when using the
2413 keyboard page keys.
2414
2415 When in keyboard-interactive mode, you can use the arrow and page keys to
2416 either move or resize the window. This property controls the page
2417 keys. The common way to enter keyboard interactive mode is to enter the
2418 subwindow menu, and select either "resize" or "move".
2419
2420 The default keyboard page step value is 20 pixels.
2421
2422 \sa keyboardSingleStep
2423*/
2424int QMdiSubWindow::keyboardPageStep() const
2425{
2426 return d_func()->keyboardPageStep;
2427}
2428
2429void QMdiSubWindow::setKeyboardPageStep(int step)
2430{
2431 // Haven't done any boundary check here since negative step only
2432 // means inverted behavior, which is OK if the user want it.
2433 // A step equal to zero means "do nothing".
2434 d_func()->keyboardPageStep = step;
2435}
2436
2437#if QT_CONFIG(menu)
2438/*!
2439 Sets \a systemMenu as the current system menu for this subwindow.
2440
2441 By default, each QMdiSubWindow has a standard system menu.
2442
2443 QActions for the system menu created by QMdiSubWindow will
2444 automatically be updated depending on the current window state;
2445 e.g., the minimize action will be disabled after the window is
2446 minimized.
2447
2448 QActions added by the user are not updated by QMdiSubWindow.
2449
2450 QMdiSubWindow takes ownership of \a systemMenu; you do not have to
2451 delete it. Any existing menus will be deleted.
2452
2453 \sa systemMenu(), showSystemMenu()
2454*/
2455void QMdiSubWindow::setSystemMenu(QMenu *systemMenu)
2456{
2457 Q_D(QMdiSubWindow);
2458 if (Q_UNLIKELY(systemMenu && systemMenu == d->systemMenu)) {
2459 qWarning(msg: "QMdiSubWindow::setSystemMenu: system menu is already set");
2460 return;
2461 }
2462
2463 if (d->systemMenu) {
2464 delete d->systemMenu;
2465 d->systemMenu = nullptr;
2466 }
2467
2468 if (!systemMenu)
2469 return;
2470
2471 if (systemMenu->parent() != this)
2472 systemMenu->setParent(this);
2473 d->systemMenu = systemMenu;
2474}
2475
2476/*!
2477 Returns a pointer to the current system menu, or zero if no system
2478 menu is set. QMdiSubWindow provides a default system menu, but you can
2479 also set the menu with setSystemMenu().
2480
2481 \sa setSystemMenu(), showSystemMenu()
2482*/
2483QMenu *QMdiSubWindow::systemMenu() const
2484{
2485 return d_func()->systemMenu;
2486}
2487
2488/*!
2489 Shows the system menu below the system menu icon in the title bar.
2490
2491 \sa setSystemMenu(), systemMenu()
2492*/
2493void QMdiSubWindow::showSystemMenu()
2494{
2495 Q_D(QMdiSubWindow);
2496 if (!d->systemMenu)
2497 return;
2498
2499 QPoint globalPopupPos;
2500 if (QWidget *icon = maximizedSystemMenuIconWidget()) {
2501 if (isLeftToRight())
2502 globalPopupPos = icon->mapToGlobal(QPoint(0, icon->y() + icon->height()));
2503 else
2504 globalPopupPos = icon->mapToGlobal(QPoint(icon->width(), icon->y() + icon->height()));
2505 } else {
2506 if (isLeftToRight())
2507 globalPopupPos = mapToGlobal(contentsRect().topLeft());
2508 else // + QPoint(1, 0) because topRight() == QPoint(left() + width() -1, top())
2509 globalPopupPos = mapToGlobal(contentsRect().topRight()) + QPoint(1, 0);
2510 }
2511
2512 // Adjust x() with -menuwidth in reverse mode.
2513 if (isRightToLeft())
2514 globalPopupPos -= QPoint(d->systemMenu->sizeHint().width(), 0);
2515 d->systemMenu->popup(pos: globalPopupPos);
2516}
2517#endif // QT_CONFIG(menu)
2518
2519/*!
2520 \since 4.4
2521
2522 Returns the area containing this sub-window, or \nullptr if there
2523 is none.
2524
2525 \sa QMdiArea::addSubWindow()
2526*/
2527QMdiArea *QMdiSubWindow::mdiArea() const
2528{
2529 QWidget *parent = parentWidget();
2530 while (parent) {
2531 if (QMdiArea *area = qobject_cast<QMdiArea *>(object: parent)) {
2532 if (area->viewport() == parentWidget())
2533 return area;
2534 }
2535 parent = parent->parentWidget();
2536 }
2537 return nullptr;
2538}
2539
2540/*!
2541 Calling this function makes the subwindow enter the shaded mode.
2542 When the subwindow is shaded, only the title bar is visible.
2543
2544 Although shading is not supported by all styles, this function will
2545 still show the subwindow as shaded, regardless of whether support
2546 for shading is available. However, when used with styles without
2547 shading support, the user will be unable to return from shaded mode
2548 through the user interface (e.g., through a shade button in the title
2549 bar).
2550
2551 \sa isShaded()
2552*/
2553void QMdiSubWindow::showShaded()
2554{
2555 if (!parent())
2556 return;
2557
2558 Q_D(QMdiSubWindow);
2559 // setMinimizeMode uses this function.
2560 if (!d->isShadeRequestFromMinimizeMode && isShaded())
2561 return;
2562
2563 d->isMaximizeMode = false;
2564
2565 d->storeFocusWidget();
2566
2567 if (!d->isShadeRequestFromMinimizeMode) {
2568 d->isShadeMode = true;
2569 d->ensureWindowState(state: Qt::WindowMinimized);
2570 }
2571
2572#if QT_CONFIG(menubar)
2573 d->removeButtonsFromMenuBar();
2574#endif
2575
2576 // showMinimized() will reset Qt::WindowActive, which makes sense
2577 // for top level widgets, but in MDI it makes sense to have an
2578 // active window which is minimized.
2579 if (hasFocus() || isAncestorOf(child: QApplication::focusWidget()))
2580 d->ensureWindowState(state: Qt::WindowActive);
2581
2582#if QT_CONFIG(sizegrip)
2583 d->setSizeGripVisible(false);
2584#endif
2585
2586 if (!d->restoreSize.isValid() || d->isShadeMode) {
2587 d->oldGeometry = geometry();
2588 d->restoreSize.setWidth(d->oldGeometry.width());
2589 d->restoreSize.setHeight(d->oldGeometry.height());
2590 }
2591
2592 // Hide the window before we change the geometry to avoid multiple resize
2593 // events and wrong window state.
2594 const bool wasVisible = isVisible();
2595 if (wasVisible)
2596 setVisible(false);
2597
2598 d->updateGeometryConstraints();
2599 // Update minimum size to internalMinimumSize if set by user.
2600 if (!minimumSize().isNull()) {
2601 d->userMinimumSize = minimumSize();
2602 setMinimumSize(d->internalMinimumSize);
2603 }
2604 resize(d->internalMinimumSize);
2605
2606 // Hide the internal widget if not already hidden by the user.
2607 if (d->baseWidget && !d->baseWidget->isHidden() && !(windowFlags() & Qt::FramelessWindowHint)) {
2608 d->baseWidget->hide();
2609 d->isWidgetHiddenByUs = true;
2610 }
2611
2612 if (wasVisible)
2613 setVisible(true);
2614
2615 d->setFocusWidget();
2616 d->resizeEnabled = false;
2617 d->moveEnabled = true;
2618 d->updateDirtyRegions();
2619 d->updateMask();
2620
2621#ifndef QT_NO_ACTION
2622 d->setEnabled(action: QMdiSubWindowPrivate::MinimizeAction, enable: false);
2623 d->setEnabled(action: QMdiSubWindowPrivate::ResizeAction, enable: d->resizeEnabled);
2624 d->setEnabled(action: QMdiSubWindowPrivate::MaximizeAction, enable: true);
2625 d->setEnabled(action: QMdiSubWindowPrivate::RestoreAction, enable: true);
2626 d->setEnabled(action: QMdiSubWindowPrivate::MoveAction, enable: d->moveEnabled);
2627#endif
2628}
2629
2630/*!
2631 \reimp
2632*/
2633bool QMdiSubWindow::eventFilter(QObject *object, QEvent *event)
2634{
2635 Q_D(QMdiSubWindow);
2636 if (!object)
2637 return QWidget::eventFilter(watched: object, event);
2638
2639#if QT_CONFIG(menu)
2640 // System menu events.
2641 if (d->systemMenu && d->systemMenu == object) {
2642 if (event->type() == QEvent::MouseButtonDblClick) {
2643 const QMouseEvent *mouseEvent = static_cast<const QMouseEvent *>(event);
2644 const QAction *action = d->systemMenu->actionAt(mouseEvent->position().toPoint());
2645 if (!action || action->isEnabled())
2646 close();
2647 } else if (event->type() == QEvent::MouseMove) {
2648 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
2649 d->hoveredSubControl = d->getSubControl(pos: mapFromGlobal(mouseEvent->globalPosition().toPoint()));
2650 } else if (event->type() == QEvent::Hide) {
2651 d->activeSubControl = QStyle::SC_None;
2652 update(QRegion(0, 0, width(), d->titleBarHeight()));
2653 }
2654 return QWidget::eventFilter(watched: object, event);
2655 }
2656#endif
2657
2658#if QT_CONFIG(sizegrip)
2659 if (object != d->baseWidget && parent() && qobject_cast<QSizeGrip *>(object)) {
2660 if (event->type() != QEvent::MouseButtonPress || !testOption(option: QMdiSubWindow::RubberBandResize))
2661 return QWidget::eventFilter(watched: object, event);
2662 const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
2663 d->mousePressPosition = parentWidget()->mapFromGlobal(mouseEvent->globalPosition().toPoint());
2664 d->oldGeometry = geometry();
2665 d->currentOperation = isLeftToRight() ? QMdiSubWindowPrivate::BottomRightResize
2666 : QMdiSubWindowPrivate::BottomLeftResize;
2667#if QT_CONFIG(rubberband)
2668 d->enterRubberBandMode();
2669#endif
2670 return true;
2671 }
2672#endif
2673
2674 if (object != d->baseWidget && event->type() != QEvent::WindowTitleChange)
2675 return QWidget::eventFilter(watched: object, event);
2676
2677 switch (event->type()) {
2678 case QEvent::Show:
2679 d->setActive(activate: true);
2680 break;
2681 case QEvent::ShowToParent:
2682 if (!d->isWidgetHiddenByUs)
2683 show();
2684 break;
2685 case QEvent::WindowStateChange: {
2686 QWindowStateChangeEvent *changeEvent = static_cast<QWindowStateChangeEvent*>(event);
2687 if (changeEvent->isOverride())
2688 break;
2689 Qt::WindowStates oldState = changeEvent->oldState();
2690 Qt::WindowStates newState = d->baseWidget->windowState();
2691 if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized))
2692 showMinimized();
2693 else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized))
2694 showMaximized();
2695 else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen)))
2696 showNormal();
2697 break;
2698 }
2699 case QEvent::Enter:
2700 d->currentOperation = QMdiSubWindowPrivate::None;
2701 d->updateCursor();
2702 break;
2703 case QEvent::LayoutRequest:
2704 d->updateGeometryConstraints();
2705 break;
2706 case QEvent::WindowTitleChange:
2707 if (d->ignoreWindowTitleChange)
2708 break;
2709 if (object == d->baseWidget) {
2710 d->updateWindowTitle(isRequestFromChild: true);
2711 d->lastChildWindowTitle = d->baseWidget->windowTitle();
2712#if QT_CONFIG(menubar)
2713 } else if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar()
2714 ->cornerWidget(corner: Qt::TopRightCorner) == maximizedButtonsWidget()) {
2715 d->originalTitle.clear();
2716 if (d->baseWidget && d->baseWidget->windowTitle() == windowTitle())
2717 d->updateWindowTitle(isRequestFromChild: true);
2718 else
2719 d->updateWindowTitle(isRequestFromChild: false);
2720#endif
2721 }
2722 break;
2723 case QEvent::ModifiedChange: {
2724 if (object != d->baseWidget)
2725 break;
2726 bool windowModified = d->baseWidget->isWindowModified();
2727 if (!windowModified && d->baseWidget->windowTitle() != windowTitle())
2728 break;
2729 if (windowTitle().contains(s: "[*]"_L1))
2730 setWindowModified(windowModified);
2731 break;
2732 }
2733 default:
2734 break;
2735 }
2736 return QWidget::eventFilter(watched: object, event);
2737}
2738
2739/*!
2740 \reimp
2741*/
2742bool QMdiSubWindow::event(QEvent *event)
2743{
2744 Q_D(QMdiSubWindow);
2745 switch (event->type()) {
2746 case QEvent::StyleChange: {
2747 bool wasShaded = isShaded();
2748 bool wasMinimized = isMinimized();
2749 bool wasMaximized = isMaximized();
2750 // Don't emit subWindowActivated, the app doesn't have to know about our hacks
2751 const QScopedValueRollback<bool> activationEnabledSaver(d->activationEnabled);
2752 d->activationEnabled = false;
2753
2754 ensurePolished();
2755 setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
2756 if (wasMinimized || wasMaximized || wasShaded)
2757 showNormal();
2758 d->updateGeometryConstraints();
2759 resize(d->internalMinimumSize.expandedTo(otherSize: size()));
2760 d->updateMask();
2761 d->updateDirtyRegions();
2762 if (wasShaded)
2763 showShaded();
2764 else if (wasMinimized)
2765 showMinimized();
2766 else if (wasMaximized)
2767 showMaximized();
2768 break;
2769 }
2770 case QEvent::ParentAboutToChange:
2771 d->setActive(activate: false);
2772 break;
2773 case QEvent::ParentChange: {
2774 bool wasResized = testAttribute(attribute: Qt::WA_Resized);
2775#if QT_CONFIG(menubar)
2776 d->removeButtonsFromMenuBar();
2777#endif
2778 d->currentOperation = QMdiSubWindowPrivate::None;
2779 d->activeSubControl = QStyle::SC_None;
2780 d->hoveredSubControl = QStyle::SC_None;
2781#if QT_CONFIG(rubberband)
2782 if (d->isInRubberBandMode)
2783 d->leaveRubberBandMode();
2784#endif
2785 d->isShadeMode = false;
2786 d->isMaximizeMode = false;
2787 d->isWidgetHiddenByUs = false;
2788 if (!parent()) {
2789#if QT_CONFIG(sizegrip)
2790 if (isMacStyle(style: style()))
2791 delete d->sizeGrip;
2792#endif
2793 setOption(option: RubberBandResize, on: false);
2794 setOption(option: RubberBandMove, on: false);
2795 } else {
2796 d->setWindowFlags(windowFlags());
2797 }
2798 setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
2799 d->updateGeometryConstraints();
2800 d->updateCursor();
2801 d->updateMask();
2802 d->updateDirtyRegions();
2803 d->updateActions();
2804 if (!wasResized && testAttribute(attribute: Qt::WA_Resized))
2805 setAttribute(Qt::WA_Resized, on: false);
2806 break;
2807 }
2808 case QEvent::WindowActivate:
2809 if (d->ignoreNextActivationEvent) {
2810 d->ignoreNextActivationEvent = false;
2811 break;
2812 }
2813 d->isExplicitlyDeactivated = false;
2814 d->setActive(activate: true);
2815 break;
2816 case QEvent::WindowDeactivate:
2817 if (d->ignoreNextActivationEvent) {
2818 d->ignoreNextActivationEvent = false;
2819 break;
2820 }
2821 d->isExplicitlyDeactivated = true;
2822 d->setActive(activate: false);
2823 break;
2824 case QEvent::WindowTitleChange:
2825 if (!d->ignoreWindowTitleChange)
2826 d->updateWindowTitle(isRequestFromChild: false);
2827 d->updateInternalWindowTitle();
2828 break;
2829 case QEvent::ModifiedChange:
2830 if (!windowTitle().contains(s: "[*]"_L1))
2831 break;
2832#if QT_CONFIG(menubar)
2833 if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar()
2834 ->cornerWidget(corner: Qt::TopRightCorner) == maximizedButtonsWidget()) {
2835 window()->setWindowModified(isWindowModified());
2836 }
2837#endif // QT_CONFIG(menubar)
2838 d->updateInternalWindowTitle();
2839 break;
2840 case QEvent::LayoutDirectionChange:
2841 d->updateDirtyRegions();
2842 break;
2843 case QEvent::LayoutRequest:
2844 d->updateGeometryConstraints();
2845 break;
2846 case QEvent::WindowIconChange:
2847 d->menuIcon = windowIcon();
2848 if (d->menuIcon.isNull())
2849 d->menuIcon = style()->standardIcon(standardIcon: QStyle::SP_TitleBarMenuButton, option: nullptr, widget: this);
2850 if (d->controlContainer)
2851 d->controlContainer->updateWindowIcon(windowIcon: d->menuIcon);
2852 if (!maximizedSystemMenuIconWidget())
2853 update(ax: 0, ay: 0, aw: width(), ah: d->titleBarHeight());
2854 break;
2855 case QEvent::PaletteChange:
2856 d->titleBarPalette = d->desktopPalette();
2857 break;
2858 case QEvent::FontChange:
2859 d->font = font();
2860 break;
2861#if QT_CONFIG(tooltip)
2862 case QEvent::ToolTip:
2863 showToolTip(helpEvent: static_cast<QHelpEvent *>(event), widget: this, opt: d->titleBarOptions(),
2864 complexControl: QStyle::CC_TitleBar, subControl: d->hoveredSubControl);
2865 break;
2866#endif
2867#ifndef QT_NO_ACTION
2868 case QEvent::ActionAdded:
2869 case QEvent::ActionChanged:
2870 case QEvent::ActionRemoved:
2871 update();
2872 break;
2873#endif
2874 default:
2875 break;
2876 }
2877 return QWidget::event(event);
2878}
2879
2880/*!
2881 \reimp
2882*/
2883void QMdiSubWindow::showEvent(QShowEvent *showEvent)
2884{
2885 Q_D(QMdiSubWindow);
2886 if (!parent()) {
2887 QWidget::showEvent(event: showEvent);
2888 return;
2889 }
2890
2891#if QT_CONFIG(sizegrip)
2892 if (isMacStyle(style: style()) && !d->sizeGrip
2893 && !(windowFlags() & Qt::FramelessWindowHint)) {
2894 d->setSizeGrip(new QSizeGrip(this));
2895 Q_ASSERT(d->sizeGrip);
2896 if (isMinimized())
2897 d->setSizeGripVisible(false);
2898 else
2899 d->setSizeGripVisible(true);
2900 resize(size().expandedTo(otherSize: d->internalMinimumSize));
2901 }
2902#endif
2903
2904 d->updateDirtyRegions();
2905 // Show buttons in the menu bar if they're already not there.
2906 // We want to do this when QMdiSubWindow becomes visible after being hidden.
2907#if QT_CONFIG(menubar)
2908 if (d->controlContainer) {
2909 if (QMenuBar *menuBar = d->menuBar()) {
2910 if (menuBar->cornerWidget(corner: Qt::TopRightCorner) != maximizedButtonsWidget())
2911 d->showButtonsInMenuBar(menuBar);
2912 }
2913 }
2914#endif
2915 d->setActive(activate: true);
2916}
2917
2918/*!
2919 \reimp
2920*/
2921void QMdiSubWindow::hideEvent(QHideEvent * /*hideEvent*/)
2922{
2923#if QT_CONFIG(menubar)
2924 d_func()->removeButtonsFromMenuBar();
2925#endif
2926}
2927
2928/*!
2929 \reimp
2930*/
2931void QMdiSubWindow::changeEvent(QEvent *changeEvent)
2932{
2933 if (!parent()) {
2934 QWidget::changeEvent(changeEvent);
2935 return;
2936 }
2937
2938 if (changeEvent->type() != QEvent::WindowStateChange) {
2939 QWidget::changeEvent(changeEvent);
2940 return;
2941 }
2942
2943 QWindowStateChangeEvent *event = static_cast<QWindowStateChangeEvent *>(changeEvent);
2944 if (event->isOverride()) {
2945 event->ignore();
2946 return;
2947 }
2948
2949 Qt::WindowStates oldState = event->oldState();
2950 Qt::WindowStates newState = windowState();
2951 if (oldState == newState) {
2952 changeEvent->ignore();
2953 return;
2954 }
2955
2956 // QWidget ensures that the widget is visible _after_ setWindowState(),
2957 // but we need to ensure that the widget is visible _before_
2958 // setWindowState() returns.
2959 Q_D(QMdiSubWindow);
2960 if (!isVisible()) {
2961 d->ensureWindowState(state: Qt::WindowNoState);
2962 setVisible(true);
2963 }
2964
2965 if (!d->oldGeometry.isValid())
2966 d->oldGeometry = geometry();
2967
2968 if ((oldState & Qt::WindowActive) && (newState & Qt::WindowActive))
2969 d->currentOperation = QMdiSubWindowPrivate::None;
2970
2971 if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized))
2972 d->setMinimizeMode();
2973 else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized))
2974 d->setMaximizeMode();
2975 else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen)))
2976 d->setNormalMode();
2977
2978 if (d->isActive)
2979 d->ensureWindowState(state: Qt::WindowActive);
2980 if (d->activationEnabled)
2981 emit windowStateChanged(oldState, newState: windowState());
2982}
2983
2984/*!
2985 \reimp
2986*/
2987void QMdiSubWindow::closeEvent(QCloseEvent *closeEvent)
2988{
2989 Q_D(QMdiSubWindow);
2990 bool acceptClose = true;
2991 if (d->baseWidget)
2992 acceptClose = d->baseWidget->close();
2993 if (!acceptClose) {
2994 closeEvent->ignore();
2995 return;
2996 }
2997#if QT_CONFIG(menubar)
2998 d->removeButtonsFromMenuBar();
2999#endif
3000 d->setActive(activate: false);
3001 if (parentWidget() && testAttribute(attribute: Qt::WA_DeleteOnClose)) {
3002 QChildEvent childRemoved(QEvent::ChildRemoved, this);
3003 QCoreApplication::sendEvent(receiver: parentWidget(), event: &childRemoved);
3004 }
3005 closeEvent->accept();
3006}
3007
3008/*!
3009 \reimp
3010*/
3011void QMdiSubWindow::leaveEvent(QEvent * /*leaveEvent*/)
3012{
3013 Q_D(QMdiSubWindow);
3014 if (d->hoveredSubControl != QStyle::SC_None) {
3015 d->hoveredSubControl = QStyle::SC_None;
3016 update(QRegion(0, 0, width(), d->titleBarHeight()));
3017 }
3018}
3019
3020/*!
3021 \reimp
3022
3023 \warning When maximizing or restoring a subwindow, the resulting call to this function
3024 may have an invalid QResizeEvent::oldSize().
3025*/
3026void QMdiSubWindow::resizeEvent(QResizeEvent *resizeEvent)
3027{
3028 Q_D(QMdiSubWindow);
3029#if QT_CONFIG(sizegrip)
3030 if (d->sizeGrip) {
3031 d->sizeGrip->move(ax: isLeftToRight() ? width() - d->sizeGrip->width() : 0,
3032 ay: height() - d->sizeGrip->height());
3033 }
3034#endif
3035
3036 if (!parent()) {
3037 QWidget::resizeEvent(event: resizeEvent);
3038 return;
3039 }
3040
3041 if (d->isMaximizeMode)
3042 d->ensureWindowState(state: Qt::WindowMaximized);
3043
3044 d->updateMask();
3045 if (!isVisible())
3046 return;
3047
3048 if (!d->resizeTimer.isActive())
3049 d->cachedStyleOptions = d->titleBarOptions();
3050 d->resizeTimer.start(duration: 200ms, obj: this);
3051}
3052
3053/*!
3054 \reimp
3055*/
3056void QMdiSubWindow::timerEvent(QTimerEvent *timerEvent)
3057{
3058 Q_D(QMdiSubWindow);
3059 if (timerEvent->id() == d->resizeTimer.id()) {
3060 d->resizeTimer.stop();
3061 d->updateDirtyRegions();
3062 }
3063}
3064
3065/*!
3066 \reimp
3067*/
3068void QMdiSubWindow::moveEvent(QMoveEvent *moveEvent)
3069{
3070 if (!parent()) {
3071 QWidget::moveEvent(event: moveEvent);
3072 return;
3073 }
3074
3075 Q_D(QMdiSubWindow);
3076 if (d->isMaximizeMode)
3077 d->ensureWindowState(state: Qt::WindowMaximized);
3078}
3079
3080/*!
3081 \reimp
3082*/
3083void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent)
3084{
3085 if (!parent() || (windowFlags() & Qt::FramelessWindowHint)) {
3086 QWidget::paintEvent(event: paintEvent);
3087 return;
3088 }
3089
3090 Q_D(QMdiSubWindow);
3091
3092 if (d->resizeTimer.isActive()) {
3093 // Only update the style option rect and the window title.
3094 int border = d->hasBorder(options: d->cachedStyleOptions) ? 4 : 0;
3095 int titleBarHeight = d->titleBarHeight(options: d->cachedStyleOptions);
3096 titleBarHeight -= isMinimized() ? 2 * border : border;
3097 d->cachedStyleOptions.rect = QRect(border, border, width() - 2 * border, titleBarHeight);
3098 if (!d->windowTitle.isEmpty()) {
3099 int width = style()->subControlRect(cc: QStyle::CC_TitleBar, opt: &d->cachedStyleOptions,
3100 sc: QStyle::SC_TitleBarLabel, widget: this).width();
3101 d->cachedStyleOptions.text = d->cachedStyleOptions.fontMetrics
3102 .elidedText(text: d->windowTitle, mode: Qt::ElideRight, width);
3103 }
3104 } else {
3105 // Force full update.
3106 d->cachedStyleOptions = d->titleBarOptions();
3107 }
3108
3109 QStylePainter painter(this);
3110 QStyleOptionFrame frameOptions;
3111 frameOptions.initFrom(w: this);
3112 frameOptions.state.setFlag(flag: QStyle::State_Active, on: d->isActive);
3113 if (isMaximized() && !d->drawTitleBarWhenMaximized()) {
3114 if (!autoFillBackground() && (!widget() || !qt_widget_private(widget: widget())->isOpaque)) {
3115 // make sure we paint all pixels of a maximized QMdiSubWindow if no-one else does
3116 painter.drawPrimitive(pe: QStyle::PE_FrameWindow, opt: frameOptions);
3117 }
3118 return;
3119 }
3120
3121 if (!d->windowTitle.isEmpty())
3122 painter.setFont(d->font);
3123 painter.drawComplexControl(cc: QStyle::CC_TitleBar, opt: d->cachedStyleOptions);
3124
3125 if (isMinimized() && !d->hasBorder(options: d->cachedStyleOptions))
3126 return;
3127
3128 frameOptions.lineWidth = style()->pixelMetric(metric: QStyle::PM_MdiSubWindowFrameWidth, option: nullptr, widget: this);
3129
3130 // ### Ensure that we do not require setting the cliprect for 4.4
3131 if (!isMinimized() && !d->hasBorder(options: d->cachedStyleOptions))
3132 painter.setClipRect(rect().adjusted(xp1: 0, yp1: d->titleBarHeight(options: d->cachedStyleOptions), xp2: 0, yp2: 0));
3133 if (!isMinimized() || d->hasBorder(options: d->cachedStyleOptions))
3134 painter.drawPrimitive(pe: QStyle::PE_FrameWindow, opt: frameOptions);
3135}
3136
3137/*!
3138 \reimp
3139*/
3140void QMdiSubWindow::mousePressEvent(QMouseEvent *mouseEvent)
3141{
3142 if (!parent()) {
3143 QWidget::mousePressEvent(event: mouseEvent);
3144 return;
3145 }
3146
3147 Q_D(QMdiSubWindow);
3148 if (d->isInInteractiveMode)
3149 d->leaveInteractiveMode();
3150#if QT_CONFIG(rubberband)
3151 if (d->isInRubberBandMode)
3152 d->leaveRubberBandMode();
3153#endif
3154
3155 if (mouseEvent->button() != Qt::LeftButton) {
3156 mouseEvent->ignore();
3157 return;
3158 }
3159
3160 if (d->currentOperation != QMdiSubWindowPrivate::None) {
3161 d->updateCursor();
3162 d->mousePressPosition = mapToParent(mouseEvent->position().toPoint());
3163 if (d->resizeEnabled || d->moveEnabled)
3164 d->oldGeometry = geometry();
3165#if QT_CONFIG(rubberband)
3166 if ((testOption(option: QMdiSubWindow::RubberBandResize) && d->isResizeOperation())
3167 || (testOption(option: QMdiSubWindow::RubberBandMove) && d->isMoveOperation())) {
3168 d->enterRubberBandMode();
3169 }
3170#endif
3171 return;
3172 }
3173
3174 d->activeSubControl = d->hoveredSubControl;
3175#if QT_CONFIG(menu)
3176 if (d->activeSubControl == QStyle::SC_TitleBarSysMenu)
3177 showSystemMenu();
3178 else
3179#endif
3180 update(QRegion(0, 0, width(), d->titleBarHeight()));
3181}
3182
3183/*!
3184 \reimp
3185*/
3186void QMdiSubWindow::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
3187{
3188 if (!parent()) {
3189 QWidget::mouseDoubleClickEvent(event: mouseEvent);
3190 return;
3191 }
3192
3193 if (mouseEvent->button() != Qt::LeftButton) {
3194 mouseEvent->ignore();
3195 return;
3196 }
3197
3198 Q_D(QMdiSubWindow);
3199 if (!d->isMoveOperation()) {
3200#if QT_CONFIG(menu)
3201 if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu)
3202 close();
3203#endif
3204 return;
3205 }
3206
3207 Qt::WindowFlags flags = windowFlags();
3208 if (isMinimized()) {
3209 if ((isShaded() && (flags & Qt::WindowShadeButtonHint))
3210 || (flags & Qt::WindowMinimizeButtonHint)) {
3211 showNormal();
3212 }
3213 return;
3214 }
3215
3216 if (isMaximized()) {
3217 if (flags & Qt::WindowMaximizeButtonHint)
3218 showNormal();
3219 return;
3220 }
3221
3222 if (flags & Qt::WindowShadeButtonHint)
3223 showShaded();
3224 else if (flags & Qt::WindowMaximizeButtonHint)
3225 showMaximized();
3226}
3227
3228/*!
3229 \reimp
3230*/
3231void QMdiSubWindow::mouseReleaseEvent(QMouseEvent *mouseEvent)
3232{
3233 if (!parent()) {
3234 QWidget::mouseReleaseEvent(event: mouseEvent);
3235 return;
3236 }
3237
3238 if (mouseEvent->button() != Qt::LeftButton) {
3239 mouseEvent->ignore();
3240 return;
3241 }
3242
3243 Q_D(QMdiSubWindow);
3244 if (d->currentOperation != QMdiSubWindowPrivate::None) {
3245#if QT_CONFIG(rubberband)
3246 if (d->isInRubberBandMode && !d->isInInteractiveMode)
3247 d->leaveRubberBandMode();
3248#endif
3249 if (d->resizeEnabled || d->moveEnabled)
3250 d->oldGeometry = geometry();
3251 }
3252
3253 d->currentOperation = d->getOperation(pos: mouseEvent->position().toPoint());
3254 d->updateCursor();
3255
3256 d->hoveredSubControl = d->getSubControl(pos: mouseEvent->position().toPoint());
3257 if (d->activeSubControl != QStyle::SC_None
3258 && d->activeSubControl == d->hoveredSubControl) {
3259 d->processClickedSubControl();
3260 }
3261 d->activeSubControl = QStyle::SC_None;
3262 update(QRegion(0, 0, width(), d->titleBarHeight()));
3263}
3264
3265/*!
3266 \reimp
3267*/
3268void QMdiSubWindow::mouseMoveEvent(QMouseEvent *mouseEvent)
3269{
3270 if (!parent()) {
3271 QWidget::mouseMoveEvent(event: mouseEvent);
3272 return;
3273 }
3274
3275 Q_D(QMdiSubWindow);
3276 // No update needed if we're in a move/resize operation.
3277 if (!d->isMoveOperation() && !d->isResizeOperation()) {
3278 // Find previous and current hover region.
3279 const QStyleOptionTitleBar options = d->titleBarOptions();
3280 QStyle::SubControl oldHover = d->hoveredSubControl;
3281 d->hoveredSubControl = d->getSubControl(pos: mouseEvent->position().toPoint());
3282 QRegion hoverRegion;
3283 if (isHoverControl(control: oldHover) && oldHover != d->hoveredSubControl)
3284 hoverRegion += style()->subControlRect(cc: QStyle::CC_TitleBar, opt: &options, sc: oldHover, widget: this);
3285 if (isHoverControl(control: d->hoveredSubControl) && d->hoveredSubControl != oldHover) {
3286 hoverRegion += style()->subControlRect(cc: QStyle::CC_TitleBar, opt: &options,
3287 sc: d->hoveredSubControl, widget: this);
3288 }
3289
3290 if (isMacStyle(style: style()) && !hoverRegion.isEmpty())
3291 hoverRegion += QRegion(0, 0, width(), d->titleBarHeight(options));
3292
3293 if (!hoverRegion.isEmpty())
3294 update(hoverRegion);
3295 }
3296
3297 if ((mouseEvent->buttons() & Qt::LeftButton) || d->isInInteractiveMode) {
3298 if ((d->isResizeOperation() && d->resizeEnabled) || (d->isMoveOperation() && d->moveEnabled)) {
3299 // As setNewGeometry moves the window, it invalidates the pos() value of any mouse move events that are
3300 // currently queued in the event loop. Map to parent using globalPos() instead.
3301 d->setNewGeometry(parentWidget()->mapFromGlobal(mouseEvent->globalPosition().toPoint()));
3302 }
3303 return;
3304 }
3305
3306 // Do not resize/move if not allowed.
3307 d->currentOperation = d->getOperation(pos: mouseEvent->position().toPoint());
3308 if ((d->isResizeOperation() && !d->resizeEnabled) || (d->isMoveOperation() && !d->moveEnabled))
3309 d->currentOperation = QMdiSubWindowPrivate::None;
3310 d->updateCursor();
3311}
3312
3313/*!
3314 \reimp
3315*/
3316void QMdiSubWindow::keyPressEvent(QKeyEvent *keyEvent)
3317{
3318 Q_D(QMdiSubWindow);
3319 if (!d->isInInteractiveMode || !parent()) {
3320 keyEvent->ignore();
3321 return;
3322 }
3323
3324 QPoint delta;
3325 switch (keyEvent->key()) {
3326 case Qt::Key_Right:
3327 if (keyEvent->modifiers() & Qt::ShiftModifier)
3328 delta = QPoint(d->keyboardPageStep, 0);
3329 else
3330 delta = QPoint(d->keyboardSingleStep, 0);
3331 break;
3332 case Qt::Key_Up:
3333 if (keyEvent->modifiers() & Qt::ShiftModifier)
3334 delta = QPoint(0, -d->keyboardPageStep);
3335 else
3336 delta = QPoint(0, -d->keyboardSingleStep);
3337 break;
3338 case Qt::Key_Left:
3339 if (keyEvent->modifiers() & Qt::ShiftModifier)
3340 delta = QPoint(-d->keyboardPageStep, 0);
3341 else
3342 delta = QPoint(-d->keyboardSingleStep, 0);
3343 break;
3344 case Qt::Key_Down:
3345 if (keyEvent->modifiers() & Qt::ShiftModifier)
3346 delta = QPoint(0, d->keyboardPageStep);
3347 else
3348 delta = QPoint(0, d->keyboardSingleStep);
3349 break;
3350 case Qt::Key_Escape:
3351 case Qt::Key_Return:
3352 case Qt::Key_Enter:
3353 d->leaveInteractiveMode();
3354 return;
3355 default:
3356 keyEvent->ignore();
3357 return;
3358 }
3359
3360#ifndef QT_NO_CURSOR
3361 QPoint newPosition = parentWidget()->mapFromGlobal(cursor().pos() + delta);
3362 QRect oldGeometry =
3363#if QT_CONFIG(rubberband)
3364 d->isInRubberBandMode ? d->rubberBand->geometry() :
3365#endif
3366 geometry();
3367 d->setNewGeometry(newPosition);
3368 QRect currentGeometry =
3369#if QT_CONFIG(rubberband)
3370 d->isInRubberBandMode ? d->rubberBand->geometry() :
3371#endif
3372 geometry();
3373 if (currentGeometry == oldGeometry)
3374 return;
3375
3376 // Update cursor position
3377
3378 QPoint actualDelta;
3379 if (d->isMoveOperation()) {
3380 actualDelta = QPoint(currentGeometry.x() - oldGeometry.x(),
3381 currentGeometry.y() - oldGeometry.y());
3382 } else {
3383 int dx = isLeftToRight() ? currentGeometry.width() - oldGeometry.width()
3384 : currentGeometry.x() - oldGeometry.x();
3385 actualDelta = QPoint(dx, currentGeometry.height() - oldGeometry.height());
3386 }
3387
3388 // Adjust in case we weren't able to move as long as wanted.
3389 if (actualDelta != delta)
3390 newPosition += (actualDelta - delta);
3391 cursor().setPos(parentWidget()->mapToGlobal(newPosition));
3392#endif
3393}
3394
3395#ifndef QT_NO_CONTEXTMENU
3396/*!
3397 \reimp
3398*/
3399void QMdiSubWindow::contextMenuEvent(QContextMenuEvent *contextMenuEvent)
3400{
3401 Q_D(QMdiSubWindow);
3402 if (!d->systemMenu) {
3403 contextMenuEvent->ignore();
3404 return;
3405 }
3406
3407 if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu
3408 || d->getRegion(operation: QMdiSubWindowPrivate::Move).contains(p: contextMenuEvent->pos())) {
3409 d->systemMenu->exec(pos: contextMenuEvent->globalPos());
3410 } else {
3411 contextMenuEvent->ignore();
3412 }
3413}
3414#endif // QT_NO_CONTEXTMENU
3415
3416/*!
3417 \reimp
3418*/
3419void QMdiSubWindow::focusInEvent(QFocusEvent *focusInEvent)
3420{
3421 d_func()->focusInReason = focusInEvent->reason();
3422}
3423
3424/*!
3425 \reimp
3426*/
3427void QMdiSubWindow::focusOutEvent(QFocusEvent * /*focusOutEvent*/)
3428{
3429 // To avoid update() in QWidget::focusOutEvent.
3430}
3431
3432/*!
3433 \reimp
3434*/
3435void QMdiSubWindow::childEvent(QChildEvent *childEvent)
3436{
3437 if (childEvent->type() != QEvent::ChildPolished)
3438 return;
3439#if QT_CONFIG(sizegrip)
3440 if (QSizeGrip *sizeGrip = qobject_cast<QSizeGrip *>(object: childEvent->child()))
3441 d_func()->setSizeGrip(sizeGrip);
3442#endif
3443}
3444
3445/*!
3446 \reimp
3447*/
3448QSize QMdiSubWindow::sizeHint() const
3449{
3450 Q_D(const QMdiSubWindow);
3451 int margin, minWidth;
3452 d->sizeParameters(margin: &margin, minWidth: &minWidth);
3453 QSize size(2 * margin, d->titleBarHeight() + margin);
3454 if (d->baseWidget && d->baseWidget->sizeHint().isValid())
3455 size += d->baseWidget->sizeHint();
3456 return size.expandedTo(otherSize: minimumSizeHint());
3457}
3458
3459/*!
3460 \reimp
3461*/
3462QSize QMdiSubWindow::minimumSizeHint() const
3463{
3464 Q_D(const QMdiSubWindow);
3465 if (isVisible())
3466 ensurePolished();
3467
3468 // Minimized window.
3469 if (parent() && isMinimized() && !isShaded())
3470 return d->iconSize();
3471
3472 // Calculate window decoration.
3473 int margin, minWidth;
3474 d->sizeParameters(margin: &margin, minWidth: &minWidth);
3475 int decorationHeight = margin + d->titleBarHeight();
3476 int minHeight = decorationHeight;
3477
3478 // Shaded window.
3479 if (parent() && isShaded())
3480 return QSize(qMax(a: minWidth, b: width()), d->titleBarHeight());
3481
3482 // Content
3483 if (layout()) {
3484 QSize minLayoutSize = layout()->minimumSize();
3485 if (minLayoutSize.isValid()) {
3486 minWidth = qMax(a: minWidth, b: minLayoutSize.width() + 2 * margin);
3487 minHeight += minLayoutSize.height();
3488 }
3489 } else if (d->baseWidget && d->baseWidget->isVisible()) {
3490 QSize minBaseWidgetSize = d->baseWidget->minimumSizeHint();
3491 if (minBaseWidgetSize.isValid()) {
3492 minWidth = qMax(a: minWidth, b: minBaseWidgetSize.width() + 2 * margin);
3493 minHeight += minBaseWidgetSize.height();
3494 }
3495 }
3496
3497#if QT_CONFIG(sizegrip)
3498 // SizeGrip
3499 int sizeGripHeight = 0;
3500 if (d->sizeGrip && d->sizeGrip->isVisibleTo(const_cast<QMdiSubWindow *>(this)))
3501 sizeGripHeight = d->sizeGrip->height();
3502 else if (parent() && isMacStyle(style: style()) && !d->sizeGrip)
3503 sizeGripHeight = style()->pixelMetric(metric: QStyle::PM_SizeGripSize, option: nullptr, widget: this);
3504 minHeight = qMax(a: minHeight, b: decorationHeight + sizeGripHeight);
3505#endif
3506
3507 return QSize(minWidth, minHeight);
3508}
3509
3510QT_END_NAMESPACE
3511
3512#include "moc_qmdisubwindow.cpp"
3513#include "qmdisubwindow.moc"
3514

source code of qtbase/src/widgets/widgets/qmdisubwindow.cpp