Plasma-framework

dialog.cpp
1/*
2 SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
3 SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
4 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
5 SPDX-FileCopyrightText: 2014 Vishesh Handa <vhanda@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "dialog.h"
11#include "../declarativeimports/core/config-x11.h"
12#include "appletquickitem.h"
13#include "config-plasma.h"
14#include "configview.h"
15#include "dialogshadows_p.h"
16#include "private/dialogbackground_p.h"
17#include "sharedqmlengine.h"
18
19#include <QLayout>
20#include <QMenu>
21#include <QPlatformSurfaceEvent>
22#include <QPointer>
23#include <QQuickItem>
24#include <QScreen>
25
26#include <KWindowInfo>
27#include <KWindowSystem>
28#include <KX11Extras>
29
30#include <KWindowEffects>
31#include <Plasma/Corona>
32
33#include <QDebug>
34#include <optional>
35
36#if HAVE_X11
37#include <qpa/qplatformwindow_p.h>
38#endif
39
40#include "plasmashellwaylandintegration.h"
41
42// Unfortunately QWINDOWSIZE_MAX is not exported
43#define DIALOGSIZE_MAX ((1 << 24) - 1)
44
45namespace PlasmaQuick
46{
47class DialogPrivate
48{
49public:
50 DialogPrivate(Dialog *dialog)
51 : q(dialog)
52 , location(Plasma::Types::BottomEdge)
53 , dialogBackground(new DialogBackground(q->contentItem()))
54 , hasMask(false)
55 , type(Dialog::Normal)
56 , hideOnWindowDeactivate(false)
57 , outputOnly(false)
58 , visible(false)
59 , resizableEdges({})
60 , floating(0)
61 , overridingCursor(false)
62 , appletInterface(nullptr)
63 , componentComplete(dialog->parent() == nullptr)
64 , needsSetupNextExpose(true)
65 , backgroundHints(Dialog::StandardBackground)
66 {
67 }
68
69 // SLOTS
70 /**
71 * Sync Borders updates the enabled borders of the dialogBackground depending
72 * on the geometry of the window.
73 *
74 * \param windowGeometry The window geometry which should be taken into
75 * consideration when activating/deactivating certain borders
76 */
77 void syncBorders(const QRect &windowGeometry);
78
79 /**
80 * This function sets the blurBehind, background contrast and shadows. It
81 * does so wrt the dialogBackground. So make sure the dialogBackground is the
82 * correct size before calling this function.
83 */
84 void updateTheme();
85 void updateVisibility(bool visible);
86
87 void updateMinimumWidth();
88 void updateMinimumHeight();
89 void updateMaximumWidth();
90 void updateMaximumHeight();
91 void updateResizableEdges();
92 void updateSizeFromAppletInterface();
93
94 /**
95 * Gets the maximum and minimum size hints for the window based on the contents. it doesn't actually resize anything
96 */
97 void getSizeHints(QSize &min, QSize &max) const;
98
99 /**
100 * This function is an optimized version of updateMaximumHeight,
101 * updateMaximumWidth,updateMinimumWidth and updateMinimumHeight.
102 * It should be called when you need to call all 4 of these functions
103 * AND you have called syncToMainItemSize before.
104 */
105 void updateLayoutParameters();
106
107 QRect availableScreenGeometryForPosition(const QPoint &pos) const;
108
109 /**
110 * This function checks the current position of the dialog and repositions
111 * it so that no part of it is not on the screen
112 */
113 void repositionIfOffScreen();
114
115 void slotMainItemSizeChanged();
116 void slotWindowPositionChanged();
117
118 void syncToMainItemSize();
119
120 bool mainItemContainsPosition(const QPointF &point) const;
121 QPointF positionAdjustedForMainItem(const QPointF &point) const;
122
123 void applyType();
124
125 bool updateMouseCursor(const QPointF &globalMousePos);
126 Qt::Edges hitTest(const QPointF &pos);
127 bool hitTestLeft(const QPointF &pos);
128 bool hitTestRight(const QPointF &pos);
129 bool hitTestTop(const QPointF &pos);
130 bool hitTestBottom(const QPointF &pos);
131
132 Dialog *q;
134 DialogBackground *dialogBackground;
135 QPointer<QQuickItem> mainItem;
136 QPointer<QQuickItem> visualParent;
137
138 QRect cachedGeometry;
139 bool hasMask;
140 Dialog::WindowType type;
141 bool hideOnWindowDeactivate;
142 bool outputOnly;
143 bool visible;
144 Qt::Edges resizableEdges;
145 int floating;
146 bool overridingCursor;
147 AppletQuickItem *appletInterface;
148 Plasma::Theme theme;
149 bool componentComplete;
150 bool needsSetupNextExpose;
151 Dialog::BackgroundHints backgroundHints;
152
153 // Attached Layout property of mainItem, if any
154 QPointer<QObject> mainItemLayout;
155};
156
157static bool isRunningInKWin()
158{
159 static bool check = QGuiApplication::platformName() == QLatin1String("wayland-org.kde.kwin.qpa");
160 return check;
161}
162
163QRect DialogPrivate::availableScreenGeometryForPosition(const QPoint &pos) const
164{
165 // FIXME: QWindow::screen() never ever changes if the window is moved across
166 // virtual screens (normal two screens with X), this seems to be intentional
167 // as it's explicitly mentioned in the docs. Until that's changed or some
168 // more proper way of howto get the current QScreen for given QWindow is found,
169 // we simply iterate over the virtual screens and pick the one our QWindow
170 // says it's at.
171 QRect avail;
172 const auto screens = QGuiApplication::screens();
173 for (QScreen *screen : screens) {
174 // we check geometry() but then take availableGeometry()
175 // to reliably check in which screen a position is, we need the full
176 // geometry, including areas for panels
177 if (screen->geometry().contains(pos)) {
178 avail = screen->availableGeometry();
179 break;
180 }
181 }
182
183 /*
184 * if the heuristic fails (because the topleft of the dialog is offscreen)
185 * use at least our screen()
186 * the screen should be correctly updated now on Qt 5.3+ so should be
187 * more reliable anyways (could be tried to remove the whole for loop
188 * above at this point)
189 *
190 * important: screen can be a nullptr... see bug 345173
191 */
192 if (avail.isEmpty() && q->screen()) {
193 avail = q->screen()->availableGeometry();
194 }
195
196 return avail;
197}
198
199void DialogPrivate::syncBorders(const QRect &geom)
200{
201 QRect avail = availableScreenGeometryForPosition(geom.topLeft());
202 int borders = KSvg::FrameSvg::AllBorders;
203
204 // Tooltips always have all the borders
205 // floating windows have all borders
206 if (!q->flags().testFlag(Qt::ToolTip) && location != Plasma::Types::Floating && floating == 0) {
207 if (geom.x() <= avail.x() || location == Plasma::Types::LeftEdge) {
208 borders = borders & ~KSvg::FrameSvg::LeftBorder;
209 }
210 if (geom.y() <= avail.y() || location == Plasma::Types::TopEdge) {
211 borders = borders & ~KSvg::FrameSvg::TopBorder;
212 }
213 if (avail.right() <= geom.x() + geom.width() || location == Plasma::Types::RightEdge) {
214 borders = borders & ~KSvg::FrameSvg::RightBorder;
215 }
216 if (avail.bottom() <= geom.y() + geom.height() || location == Plasma::Types::BottomEdge) {
217 borders = borders & ~KSvg::FrameSvg::BottomBorder;
218 }
219 }
220
221 if (dialogBackground->enabledBorders() != (KSvg::FrameSvg::EnabledBorder)borders) {
222 dialogBackground->setEnabledBorders((KSvg::FrameSvg::EnabledBorder)borders);
223 }
224}
225
226void DialogPrivate::updateTheme()
227{
228 if (backgroundHints == Dialog::NoBackground) {
229 dialogBackground->setImagePath(QString());
232 q->setMask(QRegion());
233 DialogShadows::instance()->removeWindow(q);
234 } else {
235 auto prefix = QStringLiteral("");
236 if ((backgroundHints & Dialog::SolidBackground) == Dialog::SolidBackground) {
237 prefix = QStringLiteral("solid/");
238 }
239 if (type == Dialog::Tooltip) {
240 dialogBackground->setImagePath(prefix + QStringLiteral("widgets/tooltip"));
241 } else {
242 dialogBackground->setImagePath(prefix + QStringLiteral("dialogs/background"));
243 }
244
245 const QRegion mask = dialogBackground->mask();
247
250 theme.backgroundContrast(),
251 theme.backgroundIntensity(),
252 theme.backgroundSaturation(),
253 mask);
254
256 if (hasMask) {
257 hasMask = false;
258 q->setMask(QRegion());
259 }
260 } else {
261 hasMask = true;
262 q->setMask(dialogBackground->mask());
263 }
264 if (q->isVisible()) {
265 DialogShadows::instance()->addWindow(q, dialogBackground->enabledBorders());
266 }
267 }
268}
269
270void DialogPrivate::updateVisibility(bool visible)
271{
272 if (visible) {
273 if (visualParent && visualParent->window()) {
274 q->setTransientParent(visualParent->window());
275 }
276
278 dialogBackground->setEnabledBorders(KSvg::FrameSvg::NoBorder);
279
280 // We cache the original size of the item, to retrieve it
281 // when the dialog is switched back from fullscreen.
282 if (q->geometry() != q->screen()->availableGeometry()) {
283 cachedGeometry = q->geometry();
284 }
286 } else {
287 if (!cachedGeometry.isNull()) {
288 q->resize(cachedGeometry.size());
289 slotWindowPositionChanged();
290 if (visualParent) {
291 q->setPosition(q->popupPosition(visualParent, q->size()));
292 }
293 cachedGeometry = QRect();
294 }
295
296 if (mainItem) {
297 syncToMainItemSize();
298 }
299 if (mainItemLayout) {
300 updateLayoutParameters();
301 }
302
303 // if is a wayland window that was hidden, we need
304 // to set its position again as there won't be any move event to sync QWindow::position and shellsurface::position
305 if (type != Dialog::OnScreenDisplay) {
306 PlasmaShellWaylandIntegration::get(q)->setPosition(q->position());
307 }
308 }
309 }
310
311 if (!q->flags().testFlag(Qt::ToolTip) && type != Dialog::Notification && type != Dialog::CriticalNotification) {
312 KWindowEffects::SlideFromLocation slideLocation = KWindowEffects::NoEdge;
313
314 switch (location) {
316 slideLocation = KWindowEffects::TopEdge;
317 break;
319 slideLocation = KWindowEffects::LeftEdge;
320 break;
322 slideLocation = KWindowEffects::RightEdge;
323 break;
325 slideLocation = KWindowEffects::BottomEdge;
326 break;
327 // no edge, no slide
328 default:
329 break;
330 }
331
332 KWindowEffects::slideWindow(q, slideLocation, -1);
333 }
334
335 if (visible) {
336 q->raise();
337
338 applyType();
339 }
340}
341
342void DialogPrivate::updateMinimumWidth()
343{
344 Q_ASSERT(mainItem);
345 Q_ASSERT(mainItemLayout);
346
347 if (!componentComplete) {
348 return;
349 }
350
351 q->setMinimumWidth(0);
352
353 // this is to try to get the internal item resized a tad before, but
354 // the flicker almost always happen anyways, so is *probably* useless
355 // this other kind of flicker is the view not being always focused exactly
356 // on the scene
357 int minimumWidth = mainItemLayout->property("minimumWidth").toInt() + dialogBackground->leftMargin() + dialogBackground->rightMargin();
358 if (q->screen()) {
359 minimumWidth = qMin(q->screen()->availableGeometry().width(), minimumWidth);
360 }
361 q->contentItem()->setWidth(qMax(q->width(), minimumWidth));
362 q->setWidth(qMax(q->width(), minimumWidth));
363
364 updateLayoutParameters();
365}
366
367void DialogPrivate::updateMinimumHeight()
368{
369 Q_ASSERT(mainItem);
370 Q_ASSERT(mainItemLayout);
371
372 if (!componentComplete) {
373 return;
374 }
375
376 q->setMinimumHeight(0);
377
378 // this is to try to get the internal item resized a tad before, but
379 // the flicker almost always happen anyways, so is *probably* useless
380 // this other kind of flicker is the view not being always focused exactly
381 // on the scene
382 int minimumHeight = mainItemLayout->property("minimumHeight").toInt() + dialogBackground->topMargin() + dialogBackground->bottomMargin();
383 if (q->screen()) {
384 minimumHeight = qMin(q->screen()->availableGeometry().height(), minimumHeight);
385 }
386 q->contentItem()->setHeight(qMax(q->height(), minimumHeight));
387 q->setHeight(qMax(q->height(), minimumHeight));
388
389 updateLayoutParameters();
390}
391
392void DialogPrivate::updateMaximumWidth()
393{
394 Q_ASSERT(mainItem);
395 Q_ASSERT(mainItemLayout);
396
397 if (!componentComplete) {
398 return;
399 }
400
401 q->setMaximumWidth(DIALOGSIZE_MAX);
402
403 int maximumWidth = mainItemLayout->property("maximumWidth").toInt() + dialogBackground->leftMargin() + dialogBackground->rightMargin();
404 if (q->screen()) {
405 maximumWidth = qMin(q->screen()->availableGeometry().width(), maximumWidth);
406 }
407 q->contentItem()->setWidth(qMin(q->width(), maximumWidth));
408 q->setWidth(qMin(q->width(), maximumWidth));
409
410 updateLayoutParameters();
411}
412
413void DialogPrivate::updateMaximumHeight()
414{
415 Q_ASSERT(mainItem);
416 Q_ASSERT(mainItemLayout);
417
418 if (!componentComplete) {
419 return;
420 }
421
422 q->setMaximumHeight(DIALOGSIZE_MAX);
423
424 int maximumHeight = mainItemLayout->property("maximumHeight").toInt() + dialogBackground->topMargin() + dialogBackground->bottomMargin();
425 if (q->screen()) {
426 maximumHeight = qMin(q->screen()->availableGeometry().height(), maximumHeight);
427 }
428 q->contentItem()->setHeight(qMin(q->height(), maximumHeight));
429 q->setHeight(qMin(q->height(), maximumHeight));
430
431 updateLayoutParameters();
432}
433
434void DialogPrivate::updateResizableEdges()
435{
436 if (!appletInterface) {
437 resizableEdges = {};
438 return;
439 }
440
441 QSize min;
442 QSize max(DIALOGSIZE_MAX, DIALOGSIZE_MAX);
443 getSizeHints(min, max);
444 if (min == max) {
445 resizableEdges = {};
446 return;
447 }
448
449 switch (q->location()) {
451 resizableEdges = Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge;
452 break;
454 resizableEdges = Qt::LeftEdge | Qt::BottomEdge | Qt::RightEdge;
455 break;
457 resizableEdges = Qt::TopEdge | Qt::BottomEdge | Qt::RightEdge;
458 break;
460 resizableEdges = Qt::LeftEdge | Qt::TopEdge | Qt::BottomEdge;
461 break;
465 resizableEdges = {};
466 break;
467 }
468}
469
470void DialogPrivate::updateSizeFromAppletInterface()
471{
472 if (!appletInterface) {
473 return;
474 }
475 if (!mainItem) {
476 return;
477 }
478 if (!mainItemLayout) {
479 return;
480 }
481
482 QSize min;
483 QSize max(DIALOGSIZE_MAX, DIALOGSIZE_MAX);
484 getSizeHints(min, max);
485 if (min == max) {
486 return;
487 }
488
489 QVariant prefHeight = mainItemLayout->property("preferredHeight");
490 QVariant prefWidth = mainItemLayout->property("preferredWidth");
491 int defHeight = prefHeight.isNull() ? min.height() : prefHeight.toInt();
492 int defWidth = prefWidth.isNull() ? min.width() : prefWidth.toInt();
493
494 KConfigGroup config = appletInterface->applet()->config();
495 qreal popupWidth = config.readEntry("popupWidth", static_cast<qreal>(defWidth));
496 qreal popupHeight = config.readEntry("popupHeight", static_cast<qreal>(defHeight));
497 mainItemLayout->setProperty("preferredWidth", popupWidth);
498 mainItemLayout->setProperty("preferredHeight", popupHeight);
499 mainItem->setWidth(popupWidth);
500 mainItem->setHeight(popupHeight);
501 updateLayoutParameters();
502}
503
504void DialogPrivate::getSizeHints(QSize &min, QSize &max) const
505{
506 if (!componentComplete || !mainItem || !mainItemLayout) {
507 return;
508 }
509 Q_ASSERT(mainItem);
510 Q_ASSERT(mainItemLayout);
511
512 int minimumHeight = mainItemLayout->property("minimumHeight").toInt();
513 int maximumHeight = mainItemLayout->property("maximumHeight").toInt();
514 maximumHeight = maximumHeight > 0 ? qMax(minimumHeight, maximumHeight) : DIALOGSIZE_MAX;
515
516 int minimumWidth = mainItemLayout->property("minimumWidth").toInt();
517 int maximumWidth = mainItemLayout->property("maximumWidth").toInt();
518 maximumWidth = maximumWidth > 0 ? qMax(minimumWidth, maximumWidth) : DIALOGSIZE_MAX;
519
520 minimumHeight += dialogBackground->topMargin() + dialogBackground->bottomMargin();
521 maximumHeight += dialogBackground->topMargin() + dialogBackground->bottomMargin();
522 minimumWidth += dialogBackground->leftMargin() + dialogBackground->rightMargin();
523 maximumWidth += dialogBackground->leftMargin() + dialogBackground->rightMargin();
524
525 if (q->screen()) {
526 minimumWidth = qMin(q->screen()->availableGeometry().width(), minimumWidth);
527 minimumHeight = qMin(q->screen()->availableGeometry().height(), minimumHeight);
528 maximumWidth = qMin(q->screen()->availableGeometry().width(), maximumWidth);
529 maximumHeight = qMin(q->screen()->availableGeometry().height(), maximumHeight);
530 }
531
532 // Make sure that we never return min that would be larger than max
533 min = QSize(qMin(minimumWidth, maximumWidth), qMin(minimumHeight, maximumHeight));
534 max = QSize(maximumWidth, maximumHeight);
535}
536
537void DialogPrivate::updateLayoutParameters()
538{
539 if (!componentComplete || !mainItem || !mainItemLayout || q->visibility() == QWindow::Hidden) {
540 return;
541 }
542
543 mainItem->disconnect(q);
544
545 QSize min;
546 QSize max(DIALOGSIZE_MAX, DIALOGSIZE_MAX);
547 getSizeHints(min, max);
548
549 const QSize finalSize(qBound(min.width(), q->width(), std::max(max.width(), min.width())),
550 qBound(min.height(), q->height(), std::max(max.height(), min.height())));
551
552 if (visualParent) {
553 // it's important here that we're using re->size() as size, we don't want to do recursive resizeEvents
554 const QRect geom(q->popupPosition(visualParent, finalSize), finalSize);
555 q->adjustGeometry(geom);
556 } else {
557 q->resize(finalSize);
558 }
559
560 mainItem->setPosition(QPointF(dialogBackground->leftMargin(), dialogBackground->topMargin()));
561 mainItem->setSize(QSizeF(q->width() - dialogBackground->leftMargin() - dialogBackground->rightMargin(),
562 q->height() - dialogBackground->topMargin() - dialogBackground->bottomMargin()));
563
564 dialogBackground->setSize(QSizeF(q->width(), q->height()));
565
566 if (!needsSetupNextExpose && visible) {
567 // Only reposition after successful setup; otherwise repositionIfOffScreen will override the default position set by kwin under wayland
568 repositionIfOffScreen();
569 }
570 updateTheme();
571
572 // setting the minimum or maximum size will resize the window instantly and min <= max is enforced
573 // so we have to set maximum first in that case, but also care about the new maximum being smaller
574 // than the current minimum
575 // QTBUG-113233
577 q->setMinimumSize(min);
578 q->setMaximumSize(max);
579
580 QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged()));
581 QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged()));
582}
583
584void DialogPrivate::repositionIfOffScreen()
585{
586 if (!componentComplete) {
587 return;
588 }
589 const QRect avail = availableScreenGeometryForPosition(q->position());
590
591 int x = q->x();
592 int y = q->y();
593
594 if (x < avail.left()) {
595 x = avail.left();
596 } else if (x + q->width() > avail.right()) {
597 x = avail.right() - q->width() + 1;
598 }
599
600 if (y < avail.top()) {
601 y = avail.top();
602 } else if (y + q->height() > avail.bottom()) {
603 y = avail.bottom() - q->height() + 1;
604 }
605
606 q->setX(x);
607 q->setY(y);
608}
609
610void DialogPrivate::syncToMainItemSize()
611{
612 Q_ASSERT(mainItem);
613
614 if (!componentComplete || q->visibility() == QWindow::Hidden) {
615 return;
616 }
617 if (mainItem->width() <= 0 || mainItem->height() <= 0) {
618 qWarning() << "trying to show an empty dialog";
619 }
620
621 updateTheme();
622 if (visualParent) {
623 const QSize fullSize = QSize(mainItem->width(), mainItem->height())
624 + QSize(dialogBackground->leftMargin() + dialogBackground->rightMargin(), dialogBackground->topMargin() + dialogBackground->bottomMargin());
625
626 // We get the popup position with the fullsize as we need the popup
627 // position in order to determine our actual size, as the position
628 // determines which borders will be shown.
629 const QRect geom(q->popupPosition(visualParent, fullSize), fullSize);
630
631 // We're then moving the window to where we think we would be with all
632 // the borders. This way when syncBorders is called, it has a geometry
633 // to work with.
634 syncBorders(geom);
635 } else {
636 syncBorders(q->geometry());
637 }
638
639 QSize s = QSize(mainItem->width(), mainItem->height())
640 + QSize(dialogBackground->leftMargin() + dialogBackground->rightMargin(), dialogBackground->topMargin() + dialogBackground->bottomMargin());
641
642 QSize min;
643 QSize max(DIALOGSIZE_MAX, DIALOGSIZE_MAX);
644 getSizeHints(min, max);
645 s = QSize(qBound(min.width(), s.width(), max.width()), qBound(min.height(), s.height(), max.height()));
646
647 q->contentItem()->setSize(s);
648
649 dialogBackground->setSize(s);
650
651 if (visualParent) {
652 const QRect geom(q->popupPosition(visualParent, s), s);
653
654 if (geom == q->geometry()) {
655 return;
656 }
657
658 q->adjustGeometry(geom);
659 // The borders will instantly be updated but the geometry might take a
660 // while as sub-classes can reimplement adjustGeometry and animate it.
661 syncBorders(geom);
662
663 } else {
664 q->resize(s);
665 }
666
667 mainItem->setPosition(QPointF(dialogBackground->leftMargin(), dialogBackground->topMargin()));
668
669 updateTheme();
670}
671
672void DialogPrivate::slotWindowPositionChanged()
673{
674 // Tooltips always have all the borders
675 // floating windows have all borders
676 if (!q->isVisible() || q->flags().testFlag(Qt::ToolTip) || location == Plasma::Types::Floating || floating > 0) {
677 return;
678 }
679
680 syncBorders(q->geometry());
681 updateTheme();
682
683 if (mainItem) {
684 mainItem->setPosition(QPoint(dialogBackground->leftMargin(), dialogBackground->topMargin()));
685 mainItem->setSize(QSize(q->width() - dialogBackground->leftMargin() - dialogBackground->rightMargin(),
686 q->height() - dialogBackground->topMargin() - dialogBackground->bottomMargin()));
687 }
688}
689
690bool DialogPrivate::mainItemContainsPosition(const QPointF &point) const
691{
692 if (!mainItem) {
693 return false;
694 }
695
696 return QRectF(mainItem->mapToScene(QPoint(0, 0)), QSizeF(mainItem->width(), mainItem->height())).contains(point);
697}
698
699QPointF DialogPrivate::positionAdjustedForMainItem(const QPointF &point) const
700{
701 if (!mainItem) {
702 return point;
703 }
704
705 QRectF itemRect(mainItem->mapToScene(QPoint(0, 0)), QSizeF(mainItem->width(), mainItem->height()));
706
707 return QPointF(qBound(itemRect.left(), point.x(), itemRect.right()), qBound(itemRect.top(), point.y(), itemRect.bottom()));
708}
709
710void DialogPrivate::applyType()
711{
712 /*QXcbWindowFunctions::WmWindowType*/ int wmType = 0;
713
714#if HAVE_X11
716 switch (type) {
717 case Dialog::Normal:
719 break;
720 case Dialog::Dock:
721 wmType = QNativeInterface::Private::QXcbWindow::Dock;
722 break;
723 case Dialog::DialogWindow:
724 wmType = QNativeInterface::Private::QXcbWindow::Dialog;
725 break;
726 case Dialog::PopupMenu:
727 wmType = QNativeInterface::Private::QXcbWindow::PopupMenu;
728 break;
729 case Dialog::Tooltip:
730 wmType = QNativeInterface::Private::QXcbWindow::Tooltip;
731 break;
732 case Dialog::Notification:
733 wmType = QNativeInterface::Private::QXcbWindow::Notification;
734 break;
735 case Dialog::OnScreenDisplay:
736 case Dialog::CriticalNotification:
737 case Dialog::AppletPopup:
738 // Not supported by Qt
739 break;
740 }
741
742 if (wmType) {
743 // QXcbWindow isn't installed and thus inaccessible to us, but it does read this magic property from the window...
744 q->setProperty("_q_xcb_wm_window_type", wmType);
745 }
746 }
747#endif
748
749 if (!wmType && type != Dialog::Normal && KWindowSystem::isPlatformX11()) {
750 KX11Extras::setType(q->winId(), static_cast<NET::WindowType>(type));
751 }
752 if (q->flags() & Qt::WindowStaysOnTopHint) {
753 // If the AppletPopup type is not explicitly requested, then use the Dock type in this case
754 // to avoid bug #454635.
755 if (type != Dialog::AppletPopup && type != Dialog::Tooltip) {
756 type = Dialog::Dock;
757 PlasmaShellWaylandIntegration::get(q)->setPanelBehavior(QtWayland::org_kde_plasma_surface::panel_behavior_windows_go_below);
758 } else {
759 PlasmaShellWaylandIntegration::get(q)->setPanelBehavior(QtWayland::org_kde_plasma_surface::panel_behavior_always_visible);
760 }
761 }
762 switch (type) {
763 case Dialog::Dock:
764 PlasmaShellWaylandIntegration::get(q)->setRole(QtWayland::org_kde_plasma_surface::role_panel);
765 break;
766 case Dialog::Tooltip:
767 PlasmaShellWaylandIntegration::get(q)->setRole(QtWayland::org_kde_plasma_surface::role_tooltip);
768 break;
769 case Dialog::Notification:
770 PlasmaShellWaylandIntegration::get(q)->setRole(QtWayland::org_kde_plasma_surface::role_notification);
771 break;
772 case Dialog::OnScreenDisplay:
773 PlasmaShellWaylandIntegration::get(q)->setRole(QtWayland::org_kde_plasma_surface::role_onscreendisplay);
774 break;
775 case Dialog::CriticalNotification:
776 PlasmaShellWaylandIntegration::get(q)->setRole(QtWayland::org_kde_plasma_surface::role_criticalnotification);
777 break;
778 case Dialog::Normal:
779 PlasmaShellWaylandIntegration::get(q)->setRole(QtWayland::org_kde_plasma_surface::role_normal);
780 break;
781 case Dialog::AppletPopup:
782 PlasmaShellWaylandIntegration::get(q)->setRole(QtWayland::org_kde_plasma_surface::role_appletpopup);
783 break;
784 default:
785 break;
786 }
787
788 // an OSD can't be a Dialog, as qt xcb would attempt to set a transient parent for it
789 // see bug 370433
790 if (type == Dialog::OnScreenDisplay) {
791 Qt::WindowFlags flags = (q->flags() & ~Qt::Dialog) | Qt::Window;
792 if (outputOnly) {
794 } else {
795 flags &= ~Qt::WindowTransparentForInput;
796 }
797 q->setFlags(flags);
798 }
799
800 if (backgroundHints == Dialog::NoBackground) {
801 dialogBackground->setImagePath(QString());
802 } else {
803 auto prefix = QStringLiteral("");
804 if ((backgroundHints & Dialog::SolidBackground) == Dialog::SolidBackground) {
805 prefix = QStringLiteral("solid/");
806 }
807 if (type == Dialog::Tooltip) {
808 dialogBackground->setImagePath(prefix + QStringLiteral("widgets/tooltip"));
809 } else {
810 dialogBackground->setImagePath(prefix + QStringLiteral("dialogs/background"));
811 }
812 }
813
815 if (type == Dialog::Dock || type == Dialog::Notification || type == Dialog::OnScreenDisplay || type == Dialog::CriticalNotification) {
817 } else {
819 }
820 }
821
823}
824
825bool DialogPrivate::updateMouseCursor(const QPointF &globalMousePos)
826{
827 Qt::Edges sides = hitTest(globalMousePos) & resizableEdges;
828 if (!sides) {
829 if (overridingCursor) {
830 q->unsetCursor();
831 overridingCursor = false;
832 }
833 return false;
834 }
835
836 if (sides == Qt::Edges(Qt::LeftEdge | Qt::TopEdge)) {
838 } else if (sides == Qt::Edges(Qt::RightEdge | Qt::TopEdge)) {
840 } else if (sides == Qt::Edges(Qt::LeftEdge | Qt::BottomEdge)) {
842 } else if (sides == Qt::Edges(Qt::RightEdge | Qt::BottomEdge)) {
844 } else if (sides.testFlag(Qt::TopEdge)) {
846 } else if (sides.testFlag(Qt::LeftEdge)) {
848 } else if (sides.testFlag(Qt::RightEdge)) {
850 } else {
852 }
853
854 overridingCursor = true;
855 return true;
856}
857
858Qt::Edges DialogPrivate::hitTest(const QPointF &pos)
859{
860 bool left = hitTestLeft(pos);
861 bool right = hitTestRight(pos);
862 bool top = hitTestTop(pos);
863 bool bottom = hitTestBottom(pos);
864 Qt::Edges edges;
865 if (left) {
866 edges.setFlag(Qt::LeftEdge);
867 }
868 if (right) {
869 edges.setFlag(Qt::RightEdge);
870 }
871 if (bottom) {
872 edges.setFlag(Qt::BottomEdge);
873 }
874 if (top) {
875 edges.setFlag(Qt::TopEdge);
876 }
877
878 return edges;
879}
880
881bool DialogPrivate::hitTestLeft(const QPointF &pos)
882{
883 const QRect geometry = q->geometry();
884 const QRectF rect(geometry.x(), geometry.y(), dialogBackground->leftMargin(), geometry.height());
885 return rect.contains(pos);
886}
887
888bool DialogPrivate::hitTestRight(const QPointF &pos)
889{
890 const QRect geometry = q->geometry();
891 const QRectF rect(geometry.x() + geometry.width() - dialogBackground->rightMargin(), geometry.y(), dialogBackground->rightMargin(), geometry.height());
892 return rect.contains(pos);
893}
894
895bool DialogPrivate::hitTestTop(const QPointF &pos)
896{
897 const QRect geometry = q->geometry();
898 const QRectF rect(geometry.x(), geometry.y(), geometry.width(), dialogBackground->topMargin());
899 return rect.contains(pos);
900}
901
902bool DialogPrivate::hitTestBottom(const QPointF &pos)
903{
904 const QRect geometry = q->geometry();
905 const QRectF rect(geometry.x(), geometry.y() + geometry.height() - dialogBackground->bottomMargin(), geometry.width(), dialogBackground->bottomMargin());
906 return rect.contains(pos);
907}
908
909Dialog::Dialog(QQuickItem *parent)
910 : QQuickWindow(parent ? parent->window() : nullptr)
911 , d(new DialogPrivate(this))
912{
913 setColor(QColor(Qt::transparent));
915
916 connect(this, &QWindow::xChanged, [this]() {
917 d->slotWindowPositionChanged();
918 });
919 connect(this, &QWindow::yChanged, [this]() {
920 d->slotWindowPositionChanged();
921 });
922 connect(this, &Dialog::locationChanged, this, [&] {
923 d->updateResizableEdges();
924 });
925
926 // Given dialogs are skip task bar and don't have a decoration
927 // minimizing them using e.g. "minimize all" should just close them
928 connect(this, &QWindow::windowStateChanged, this, [this](Qt::WindowState newState) {
929 if (newState == Qt::WindowMinimized) {
930 setVisible(false);
931 }
932 });
933
934 connect(this, &QWindow::visibleChanged, this, &Dialog::visibleChangedProxy);
935
936 // HACK: this property is invoked due to the initialization that gets done to contentItem() in the getter
937 property("data");
938
939 // This is needed as a transition thing for KWayland
940 // FIXME: is this valid anymore?
941 // setProperty("__plasma_frameSvg", QVariant::fromValue(d->dialogBackground->frameSvg()));
942
943 connect(&d->theme, SIGNAL(themeChanged()), this, SLOT(updateTheme()));
944}
945
946Dialog::~Dialog()
947{
948 // Prevent signals from super-class destructor invoking our now-destroyed slots
949 disconnect(this, nullptr, this, nullptr);
950}
951
952QQuickItem *Dialog::mainItem() const
953{
954 return d->mainItem;
955}
956
957void Dialog::setMainItem(QQuickItem *mainItem)
958{
959 if (d->mainItem != mainItem) {
960 if (d->mainItem) {
961 disconnect(d->mainItem, nullptr, this, nullptr);
962 d->mainItem->setParentItem(nullptr);
963 }
964
965 if (d->mainItemLayout) {
966 disconnect(d->mainItemLayout, nullptr, this, nullptr);
967 }
968
969 d->mainItem = mainItem;
970
971 if (mainItem) {
972 mainItem->setParentItem(contentItem());
973
974 connect(mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged()));
975 connect(mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged()));
976 d->slotMainItemSizeChanged();
977
978 // Extract the representation's Layout, if any
979 QObject *layout = nullptr;
980 setMinimumSize(QSize(0, 0));
981 setMaximumSize(QSize(DIALOGSIZE_MAX, DIALOGSIZE_MAX));
982
983 // Search a child that has the needed Layout properties
984 // HACK: here we are not type safe, but is the only way to access to a pointer of Layout
985 const auto lstChild = mainItem->children();
986 for (QObject *child : lstChild) {
987 // find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight
988 if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid()
989 && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid()
990 && child->property("fillWidth").isValid() && child->property("fillHeight").isValid()) {
991 layout = child;
992 break;
993 }
994 }
995
996 d->mainItemLayout = layout;
997
998 if (layout) {
999 // These connections are direct. They run on the GUI thread.
1000 // If the underlying QQuickItem is sane, these properties should be updated atomically in one cycle
1001 // of the GUI thread event loop, denying the chance for the event loop to run a QQuickItem::update() call in between.
1002 // So we avoid rendering a frame in between with inconsistent geometry properties which would cause flickering issues.
1003 connect(layout, SIGNAL(minimumWidthChanged()), this, SLOT(updateMinimumWidth()));
1004 connect(layout, SIGNAL(minimumHeightChanged()), this, SLOT(updateMinimumHeight()));
1005 connect(layout, SIGNAL(maximumWidthChanged()), this, SLOT(updateMaximumWidth()));
1006 connect(layout, SIGNAL(maximumHeightChanged()), this, SLOT(updateMaximumHeight()));
1007
1008 d->updateLayoutParameters();
1009 }
1010 }
1011
1012 // if this is called in Component.onCompleted we have to wait a loop the item is added to a scene
1013 Q_EMIT mainItemChanged();
1014 }
1015}
1016
1017void DialogPrivate::slotMainItemSizeChanged()
1018{
1019 syncToMainItemSize();
1020}
1021
1022QQuickItem *Dialog::visualParent() const
1023{
1024 return d->visualParent;
1025}
1026
1027void Dialog::setVisualParent(QQuickItem *visualParent)
1028{
1029 if (d->visualParent == visualParent) {
1030 return;
1031 }
1032
1033 d->visualParent = visualParent;
1034 Q_EMIT visualParentChanged();
1035 if (visualParent) {
1036 if (visualParent->window()) {
1037 setTransientParent(visualParent->window());
1038 }
1039 if (d->mainItem) {
1040 d->syncToMainItemSize();
1041 }
1042 }
1043}
1044
1045QPoint Dialog::popupPosition(QQuickItem *item, const QSize &size)
1046{
1047 if (!item) {
1048 // If no item was specified try to align at the center of the parent view
1049 QQuickItem *parentItem = qobject_cast<QQuickItem *>(parent());
1050 if (parentItem) {
1051 QScreen *screen = parentItem->window()->screen();
1052
1053 switch (d->location) {
1055 return QPoint(screen->availableGeometry().center().x() - size.width() / 2, screen->availableGeometry().y());
1057 return QPoint(screen->availableGeometry().x(), screen->availableGeometry().center().y() - size.height() / 2);
1059 return QPoint(screen->availableGeometry().right() - size.width(), screen->availableGeometry().center().y() - size.height() / 2);
1061 return QPoint(screen->availableGeometry().center().x() - size.width() / 2, screen->availableGeometry().bottom() - size.height());
1062 // Default center in the screen
1063 default:
1064 return screen->geometry().center() - QPoint(size.width() / 2, size.height() / 2);
1065 }
1066 } else {
1067 return QPoint();
1068 }
1069 }
1070
1071 QPointF pos = item->mapToScene(QPointF(0, 0));
1072
1073 if (item->window()) {
1074 pos = item->window()->mapToGlobal(pos.toPoint());
1075 } else {
1076 return QPoint();
1077 }
1078
1079 // if the item is in a dock or in a window that ignores WM we want to position the popups outside of the dock
1080 const KWindowInfo winInfo(item->window()->winId(), NET::WMWindowType);
1081 const bool outsideParentWindow =
1082 ((winInfo.windowType(NET::AllTypesMask) == NET::Dock) || (item->window()->flags() & Qt::X11BypassWindowManagerHint)) && item->window()->mask().isNull();
1083
1084 QRect parentGeometryBounds;
1085 if (outsideParentWindow) {
1086 parentGeometryBounds = item->window()->geometry();
1087 } else {
1088 parentGeometryBounds = item->mapRectToScene(item->boundingRect()).toRect();
1089 if (item->window()) {
1090 parentGeometryBounds.moveTopLeft(item->window()->mapToGlobal(parentGeometryBounds.topLeft()));
1091 pos = parentGeometryBounds.topLeft();
1092 }
1093 }
1094
1095 const QRectF itemSceneBoundingRect = item->mapRectToScene(item->boundingRect());
1096 const QPoint centerPoint(pos.x() + (itemSceneBoundingRect.width() - size.width()) / 2, //
1097 pos.y() + (itemSceneBoundingRect.height() - size.height()) / 2);
1098
1099 const QPoint topPoint(centerPoint.x(), parentGeometryBounds.top() - size.height());
1100 const QPoint bottomPoint(centerPoint.x(), parentGeometryBounds.bottom());
1101
1102 const QPoint leftPoint(parentGeometryBounds.left() - size.width(), centerPoint.y());
1103 const QPoint rightPoint(parentGeometryBounds.right(), centerPoint.y());
1104
1105 QPoint dialogPos;
1106 if (d->location == Plasma::Types::TopEdge) {
1107 dialogPos = bottomPoint;
1108 } else if (d->location == Plasma::Types::LeftEdge) {
1109 dialogPos = rightPoint;
1110 } else if (d->location == Plasma::Types::RightEdge) {
1111 dialogPos = leftPoint;
1112 } else { // Types::BottomEdge
1113 dialogPos = topPoint;
1114 }
1115
1116 // find the correct screen for the item
1117 // we do not rely on item->window()->screen() because
1118 // QWindow::screen() is always only the screen where the window gets first created
1119 // not actually the current window. See QWindow::screen() documentation
1120 QRect avail = item->window()->screen()->availableGeometry();
1121 avail.adjust(d->floating, d->floating, -d->floating, -d->floating);
1122
1123 if (outsideParentWindow && d->dialogBackground->enabledBorders() != KSvg::FrameSvg::AllBorders) {
1124 // make the panel look it's inside the panel, in order to not make it look cut
1125 switch (d->location) {
1128 avail.setTop(qMax(avail.top(), parentGeometryBounds.top()));
1129 avail.setBottom(qMin(avail.bottom(), parentGeometryBounds.bottom()));
1130 break;
1131 default:
1132 avail.setLeft(qMax(avail.left(), parentGeometryBounds.left()));
1133 avail.setRight(qMin(avail.right(), parentGeometryBounds.right()));
1134 break;
1135 }
1136 }
1137
1138 // If the dialog is from opening an applet in the panel and it's close enough to the center that
1139 // it would still cover the original applet in the panel if it was centered, then we manually center it.
1140 if (d->type == Dialog::AppletPopup) {
1141 QRectF parentRect = item->mapRectToScene(item->boundingRect());
1142 switch (d->location) {
1145 if (qAbs(dialogPos.x() + size.width() / 2 - avail.center().x()) < size.width() / 2 - parentRect.width() / 3) {
1146 dialogPos.setX(avail.center().x() - size.width() / 2);
1147 }
1148 break;
1151 if (qAbs(dialogPos.y() + size.height() / 2 - avail.center().y()) < size.height() / 2 - parentRect.height() / 3) {
1152 dialogPos.setY(avail.center().y() - size.height() / 2);
1153 }
1154 break;
1155 default:
1156 break;
1157 }
1158 }
1159
1160 // For top & bottom the inner conditions are intentionally different from thouse for left & right,
1161 // because we want floating popups to flip vertically, but only push them in bounds horizontally.
1162
1163 // If popup goes out of bounds...
1164 // ...at the left edge
1165 if (dialogPos.x() < avail.left()) {
1166 if (d->location != Plasma::Types::LeftEdge) {
1167 // move it in bounds
1168 // Note: floating popup goes here.
1169 dialogPos.setX(avail.left());
1170 } else {
1171 // flip it around
1172 dialogPos.setX(rightPoint.x());
1173 }
1174 }
1175 // ...at the right edge
1176 if (dialogPos.x() + size.width() > avail.right()) {
1177 if (d->location != Plasma::Types::RightEdge) {
1178 // move it in bounds
1179 // Note: floating popup goes here.
1180 dialogPos.setX(qMax(avail.left(), (avail.right() - size.width() + 1)));
1181 } else {
1182 // flip it around
1183 dialogPos.setX(leftPoint.x());
1184 }
1185 }
1186 // ...at the top edge
1187 if (dialogPos.y() < avail.top()) {
1188 if (d->location == Plasma::Types::LeftEdge || d->location == Plasma::Types::RightEdge) {
1189 // move it in bounds
1190 dialogPos.setY(avail.top());
1191 } else {
1192 // flip it around
1193 // Note: floating popup goes here.
1194 dialogPos.setY(bottomPoint.y());
1195 }
1196 }
1197 // ...at the bottom edge
1198 if (dialogPos.y() + size.height() > avail.bottom()) {
1199 if (d->location == Plasma::Types::LeftEdge || d->location == Plasma::Types::RightEdge) {
1200 // move it in bounds
1201 dialogPos.setY(qMax(avail.top(), (avail.bottom() - size.height() + 1)));
1202 } else {
1203 // flip it around
1204 // Note: floating popup goes here.
1205 dialogPos.setY(topPoint.y());
1206 }
1207 }
1208
1209 return dialogPos;
1210}
1211
1212Plasma::Types::Location Dialog::location() const
1213{
1214 return d->location;
1215}
1216
1217void Dialog::setLocation(Plasma::Types::Location location)
1218{
1219 if (d->location == location) {
1220 return;
1221 }
1222 d->location = location;
1223 Q_EMIT locationChanged();
1224
1225 if (d->mainItem) {
1226 d->syncToMainItemSize();
1227 }
1228}
1229
1230QObject *Dialog::margins() const
1231{
1232 return d->dialogBackground->fixedMargins();
1233}
1234
1235QObject *Dialog::inset() const
1236{
1237 return d->dialogBackground->inset();
1238}
1239
1240void Dialog::setFramelessFlags(Qt::WindowFlags flags)
1241{
1242 if (d->type == Dialog::Normal) {
1243 flags |= Qt::Dialog;
1244 }
1246 d->applyType();
1247 Q_EMIT flagsChanged();
1248}
1249
1250void Dialog::adjustGeometry(const QRect &geom)
1251{
1252 setGeometry(geom);
1253}
1254
1255void Dialog::resizeEvent(QResizeEvent *re)
1256{
1258
1259 if (d->resizableEdges) {
1260 d->updateMouseCursor(QCursor::pos());
1261 }
1262
1263 // it's a spontaneous event generated in qguiapplication.cpp QGuiApplicationPrivate::processWindowScreenChangedEvent
1264 // QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window), QRect());
1265 // This happens before the first show event when there is more than one screen,
1266 // right after the window has been created, the window is still 0x0,
1267 // but the resize event gets delivered with 0x0 again and executed with all the bad side effects
1268 // this seems to happen for every window when there are multiple screens, so something we have probably to watch out for in the future
1269 if (re->size().isEmpty() || re->size() == re->oldSize()) {
1270 return;
1271 }
1272
1273 // A dialog can be resized even if no mainItem has ever been set
1274 if (!d->mainItem || !isExposed()) {
1275 return;
1276 }
1277
1278 d->mainItem->disconnect(this);
1279
1280 d->dialogBackground->setSize(QSizeF(re->size().width(), re->size().height()));
1281 d->mainItem->setPosition(QPointF(d->dialogBackground->leftMargin(), d->dialogBackground->topMargin()));
1282
1283 d->mainItem->setSize(QSize(re->size().width() - d->dialogBackground->leftMargin() - d->dialogBackground->rightMargin(),
1284 re->size().height() - d->dialogBackground->topMargin() - d->dialogBackground->bottomMargin()));
1285
1286 d->updateTheme();
1287
1288 QObject::connect(d->mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged()));
1289 QObject::connect(d->mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged()));
1290}
1291
1292void Dialog::setType(WindowType type)
1293{
1294 if (type == d->type) {
1295 return;
1296 }
1297
1298 d->type = type;
1299 d->applyType();
1300 Q_EMIT typeChanged();
1301}
1302
1303Dialog::WindowType Dialog::type() const
1304{
1305 return d->type;
1306}
1307
1308void Dialog::focusInEvent(QFocusEvent *ev)
1309{
1311}
1312
1313void Dialog::focusOutEvent(QFocusEvent *ev)
1314{
1315 if (d->hideOnWindowDeactivate) {
1316 bool parentHasFocus = false;
1317
1318 QWindow *parentWindow = transientParent();
1319
1320 while (parentWindow) {
1321 if (parentWindow->isActive() && !(parentWindow->flags() & Qt::WindowDoesNotAcceptFocus)) {
1322 parentHasFocus = true;
1323
1324 break;
1325 }
1326
1327 parentWindow = parentWindow->transientParent();
1328 }
1329
1330 const QWindow *focusWindow = QGuiApplication::focusWindow();
1331 bool childHasFocus = focusWindow && ((focusWindow->isActive() && isAncestorOf(focusWindow)) || (focusWindow->type() & Qt::Popup) == Qt::Popup);
1332
1333 const bool viewClicked = qobject_cast<const PlasmaQuick::SharedQmlEngine *>(focusWindow) || qobject_cast<const ConfigView *>(focusWindow);
1334
1335 if (viewClicked || (!parentHasFocus && !childHasFocus)) {
1336 setVisible(false);
1337 Q_EMIT windowDeactivated();
1338 }
1339 }
1340
1342}
1343
1344void Dialog::showEvent(QShowEvent *event)
1345{
1346 d->updateResizableEdges();
1347 d->updateSizeFromAppletInterface();
1348
1349 if (d->backgroundHints != Dialog::NoBackground) {
1350 DialogShadows::instance()->addWindow(this, d->dialogBackground->enabledBorders());
1351 }
1352
1355 }
1357}
1358
1359bool Dialog::event(QEvent *event)
1360{
1361 if (event->type() == QEvent::Expose) {
1362 if (!KWindowSystem::isPlatformWayland() || isRunningInKWin() || !isExposed()) {
1363 return QQuickWindow::event(event);
1364 }
1365
1366 /*
1367 * expose event is the only place where to correctly
1368 * register our wayland extensions, as showevent is a bit too
1369 * soon and the platform window isn't shown yet
1370 * (only the first expose event, guarded by needsSetupNextExpose bool)
1371 * and tear it down when the window gets hidden
1372 * see https://phabricator.kde.org/T6064
1373 */
1374 // sometimes non null regions arrive even for non visible windows
1375 // for which surface creation would fail
1376 if (d->needsSetupNextExpose && isVisible()) {
1377 d->updateVisibility(true);
1378 const bool ret = QQuickWindow::event(event);
1379 d->updateTheme();
1380 d->needsSetupNextExpose = false;
1381 return ret;
1382 }
1383 } else if (event->type() == QEvent::Show) {
1384 d->updateVisibility(true);
1385 } else if (event->type() == QEvent::Hide) {
1386 d->updateVisibility(false);
1387 d->needsSetupNextExpose = true;
1388 } else if (event->type() == QEvent::Move) {
1389 QMoveEvent *me = static_cast<QMoveEvent *>(event);
1390 PlasmaShellWaylandIntegration::get(this)->setPosition(me->pos());
1391 }
1392
1393 /*Fitt's law: if the containment has margins, and the mouse cursor clicked
1394 * on the mouse edge, forward the click in the containment boundaries
1395 */
1396 if (d->mainItem && !d->mainItem->size().isEmpty()) {
1397 switch (event->type()) {
1398 case QEvent::MouseMove:
1401 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1402 if (d->resizableEdges) {
1403 if (event->type() == QEvent::MouseMove && d->updateMouseCursor(me->globalPosition())) {
1404 return QQuickWindow::event(event);
1405 }
1406 if (event->type() == QEvent::MouseButtonPress) {
1407 const QPointF globalMousePos = me->globalPosition();
1408 const Qt::Edges sides = d->hitTest(globalMousePos) & d->resizableEdges;
1409 if (sides) {
1410 startSystemResize(sides);
1411 return true;
1412 }
1413 }
1414 }
1415
1416 // don't mess with position if the cursor is actually outside the view:
1417 // somebody is doing a click and drag that must not break when the cursor is outside
1418 if (geometry().contains(me->globalPosition().toPoint()) && !d->mainItemContainsPosition(me->scenePosition())) {
1419 QMouseEvent me2(me->type(),
1420 d->positionAdjustedForMainItem(me->scenePosition()),
1421 d->positionAdjustedForMainItem(me->scenePosition()),
1422 d->positionAdjustedForMainItem(me->scenePosition()) + position(),
1423 me->button(),
1424 me->buttons(),
1425 me->modifiers());
1426 me2.setTimestamp(me->timestamp());
1427
1428 if (isVisible()) {
1429 QCoreApplication::sendEvent(this, &me2);
1430 }
1431 return true;
1432 }
1433 break;
1434 }
1435
1436 case QEvent::Wheel: {
1437 QWheelEvent *we = static_cast<QWheelEvent *>(event);
1438
1439 const QPoint pos = we->position().toPoint();
1440
1441 if (!d->mainItemContainsPosition(pos)) {
1442 QWheelEvent we2(d->positionAdjustedForMainItem(pos),
1443 d->positionAdjustedForMainItem(pos) + position(),
1444 we->pixelDelta(),
1445 we->angleDelta(),
1446 we->buttons(),
1447 we->modifiers(),
1448 we->phase(),
1449 false /*inverted*/);
1450 we2.setTimestamp(we->timestamp());
1451
1452 if (isVisible()) {
1453 QCoreApplication::sendEvent(this, &we2);
1454 }
1455 return true;
1456 }
1457 break;
1458 }
1459
1460 case QEvent::DragEnter: {
1461 QDragEnterEvent *de = static_cast<QDragEnterEvent *>(event);
1462 if (!d->mainItemContainsPosition(de->position())) {
1463 QDragEnterEvent de2(d->positionAdjustedForMainItem(de->position()).toPoint(),
1464 de->possibleActions(),
1465 de->mimeData(),
1466 de->buttons(),
1467 de->modifiers());
1468
1469 if (isVisible()) {
1470 QCoreApplication::sendEvent(this, &de2);
1471 }
1472 return true;
1473 }
1474 break;
1475 }
1476 // DragLeave just works
1477 case QEvent::DragLeave:
1478 break;
1479 case QEvent::DragMove: {
1480 QDragMoveEvent *de = static_cast<QDragMoveEvent *>(event);
1481 if (!d->mainItemContainsPosition(de->position())) {
1482 QDragMoveEvent de2(d->positionAdjustedForMainItem(de->position()).toPoint(),
1483 de->possibleActions(),
1484 de->mimeData(),
1485 de->buttons(),
1486 de->modifiers());
1487
1488 if (isVisible()) {
1489 QCoreApplication::sendEvent(this, &de2);
1490 }
1491 return true;
1492 }
1493 break;
1494 }
1495 case QEvent::Drop: {
1496 QDropEvent *de = static_cast<QDropEvent *>(event);
1497 if (!d->mainItemContainsPosition(de->position())) {
1498 QDropEvent de2(d->positionAdjustedForMainItem(de->position()).toPoint(), de->possibleActions(), de->mimeData(), de->buttons(), de->modifiers());
1499
1500 if (isVisible()) {
1501 QCoreApplication::sendEvent(this, &de2);
1502 }
1503 return true;
1504 }
1505 break;
1506 }
1507
1508 default:
1509 break;
1510 }
1511 }
1512
1513 return QQuickWindow::event(event);
1514}
1515
1516void Dialog::hideEvent(QHideEvent *event)
1517{
1518 // Persist the size if this contains an applet
1519 if (d->appletInterface && d->mainItem) {
1520 KConfigGroup config = d->appletInterface->applet()->config();
1521 qreal w = d->mainItem->width();
1522 qreal h = d->mainItem->height();
1523 config.writeEntry("popupWidth", w);
1524 config.writeEntry("popupHeight", h);
1525 config.sync();
1526 }
1527
1529}
1530
1531void Dialog::moveEvent(QMoveEvent *e)
1532{
1534 if (d->resizableEdges) {
1535 d->updateMouseCursor(QCursor::pos());
1536 }
1537}
1538
1539void Dialog::classBegin()
1540{
1541 d->componentComplete = false;
1542}
1543
1544void Dialog::componentComplete()
1545{
1546 d->componentComplete = true;
1547 QQuickWindow::setVisible(d->visible);
1548 d->updateTheme();
1549}
1550
1551bool Dialog::hideOnWindowDeactivate() const
1552{
1553 return d->hideOnWindowDeactivate;
1554}
1555
1556void Dialog::setHideOnWindowDeactivate(bool hide)
1557{
1558 if (d->hideOnWindowDeactivate == hide) {
1559 return;
1560 }
1561 d->hideOnWindowDeactivate = hide;
1562 Q_EMIT hideOnWindowDeactivateChanged();
1563}
1564
1565bool Dialog::isOutputOnly() const
1566{
1567 return d->outputOnly;
1568}
1569
1570void Dialog::setOutputOnly(bool outputOnly)
1571{
1572 if (d->outputOnly == outputOnly) {
1573 return;
1574 }
1575 d->outputOnly = outputOnly;
1576 d->applyType();
1577 Q_EMIT outputOnlyChanged();
1578}
1579
1580void Dialog::setFloating(int floating)
1581{
1582 d->floating = floating;
1583 Q_EMIT floatingChanged();
1584}
1585
1586int Dialog::floating() const
1587{
1588 return d->floating;
1589}
1590
1591void Dialog::setVisible(bool visible)
1592{
1593 // only update real visibility when we have finished component completion
1594 // and all flags have been set
1595
1596 d->visible = visible;
1597 if (d->componentComplete) {
1598 if (visible && d->visualParent) {
1599 setPosition(popupPosition(d->visualParent, size()));
1600 }
1601
1602 // Bug 381242: Qt remembers minimize state and re-applies it when showing
1603 setWindowStates(windowStates() & ~Qt::WindowMinimized);
1604 QQuickWindow::setVisible(visible);
1605 // signal will be emitted and proxied from the QQuickWindow code
1606 } else {
1607 Q_EMIT visibleChangedProxy();
1608 }
1609}
1610
1611bool Dialog::isVisible() const
1612{
1613 if (d->componentComplete) {
1614 return QQuickWindow::isVisible();
1615 }
1616 return d->visible;
1617}
1618
1619void Dialog::setAppletInterface(QQuickItem *appletInterface)
1620{
1621 if (d->appletInterface == appletInterface) {
1622 return;
1623 }
1624 d->appletInterface = qobject_cast<AppletQuickItem *>(appletInterface);
1625 Q_EMIT appletInterfaceChanged();
1626}
1627
1628QQuickItem *Dialog::appletInterface() const
1629{
1630 return d->appletInterface;
1631}
1632
1633Dialog::BackgroundHints Dialog::backgroundHints() const
1634{
1635 return d->backgroundHints;
1636}
1637
1638void Dialog::setBackgroundHints(Dialog::BackgroundHints hints)
1639{
1640 if (d->backgroundHints == hints) {
1641 return;
1642 }
1643
1644 d->backgroundHints = hints;
1645 d->updateTheme();
1646 Q_EMIT backgroundHintsChanged();
1647}
1648
1649}
1650
1651#include "moc_dialog.cpp"
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
KConfig * config()
QString readEntry(const char *key, const char *aDefault=nullptr) const
bool sync() override
NET::WindowType windowType(NET::WindowTypes supported_types) const
static bool isPlatformX11()
static bool isPlatformWayland()
static void setOnAllDesktops(WId win, bool b)
static void setState(WId win, NET::States state)
bool compositingActive
static void setType(WId win, NET::WindowType windowType)
SkipTaskbar
SkipSwitcher
AllTypesMask
WindowType
virtual void adjustGeometry(const QRect &geom)
set the dialog position.
Definition dialog.cpp:1250
@ SolidBackground
The solid version of the background is preferred.
Definition dialog.h:178
@ NoBackground
Not drawing a background under the applet, the dialog has its own implementation.
Definition dialog.h:176
@ StandardBackground
The standard background from the theme is drawn.
Definition dialog.h:177
Plasma::Types::Location location
Plasma Location of the dialog window.
Definition dialog.h:102
virtual QPoint popupPosition(QQuickItem *item, const QSize &size)
Definition dialog.cpp:1045
Qt::WindowFlags flags
This property holds the window flags of the window.
Definition dialog.h:134
static PlasmaShellWaylandIntegration * get(QWindow *window)
Returns the relevant PlasmaWaylandShellIntegration instance for this window creating one if needed.
KConfigGroup config() const
Returns the KConfigGroup to access the applets configuration.
Definition applet.cpp:194
Interface to the Plasma theme.
Definition theme.h:40
qreal backgroundIntensity() const
This method allows Plasma to set a background contrast effect for a given theme, improving readabilit...
Definition theme.cpp:313
bool backgroundContrastEnabled() const
This method allows Plasma to enable and disable the background contrast effect for a given theme,...
Definition theme.cpp:288
bool blurBehindEnabled() const
This method allows Plasma to enable and disable the blurring of what is behind the background for a g...
Definition theme.cpp:333
qreal backgroundSaturation() const
This method allows Plasma to set a background contrast effect for a given theme, improving readabilit...
Definition theme.cpp:325
qreal backgroundContrast() const
This method allows Plasma to set a background contrast effect for a given theme, improving readabilit...
Definition theme.cpp:298
Location
The Location enumeration describes where on screen an element, such as an Applet or its managing cont...
Definition plasma.h:81
@ RightEdge
Along the right side of the screen.
Definition plasma.h:90
@ Floating
Free floating.
Definition plasma.h:82
@ FullScreen
Full screen.
Definition plasma.h:86
@ TopEdge
Along the top of the screen.
Definition plasma.h:87
@ Desktop
On the planar desktop layer, extending across the full screen from edge to edge.
Definition plasma.h:84
@ LeftEdge
Along the left side of the screen.
Definition plasma.h:89
@ BottomEdge
Along the bottom of the screen.
Definition plasma.h:88
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
Type type(const QSqlDatabase &db)
KCRASH_EXPORT void setFlags(KCrash::CrashFlags flags)
QVariant location(const QVariant &res)
KGUIADDONS_EXPORT QWindow * window(QObject *job)
void slideWindow(QWindow *window, SlideFromLocation location, int offset=-1)
void enableBlurBehind(QWindow *window, bool enable=true, const QRegion &region=QRegion())
void enableBackgroundContrast(QWindow *window, bool enable=true, qreal contrast=1, qreal intensity=1, qreal saturation=1, const QRegion &region=QRegion())
The EdgeEventForwarder class This class forwards edge events to be replayed within the given margin T...
Definition action.h:20
Namespace for everything in libplasma.
bool sendEvent(QObject *receiver, QEvent *event)
QPoint pos()
Qt::MouseButtons buttons() const const
const QMimeData * mimeData() const const
Qt::KeyboardModifiers modifiers() const const
QPointF position() const const
Qt::DropActions possibleActions() const const
Type type() const const
QWindow * focusWindow()
QList< QScreen * > screens()
Qt::KeyboardModifiers modifiers() const const
quint64 timestamp() const const
const QPoint & pos() const const
const QObjectList & children() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool setProperty(const char *name, QVariant &&value)
void setX(int x)
void setY(int y)
int x() const const
int y() const const
QPoint toPoint() const const
qreal x() const const
qreal y() const const
virtual QRectF boundingRect() const const
QRectF mapRectToScene(const QRectF &rect) const const
QPointF mapToScene(const QPointF &point) const const
void setParentItem(QQuickItem *parent)
QQuickWindow * window() const const
virtual bool event(QEvent *event) override
virtual void focusInEvent(QFocusEvent *ev) override
virtual void focusOutEvent(QFocusEvent *ev) override
virtual void hideEvent(QHideEvent *) override
virtual void resizeEvent(QResizeEvent *ev) override
virtual void showEvent(QShowEvent *) override
void adjust(int dx1, int dy1, int dx2, int dy2)
int bottom() const const
QPoint center() const const
int height() const const
bool isEmpty() const const
bool isNull() const const
int left() const const
void moveTopLeft(const QPoint &position)
int right() const const
void setBottom(int y)
void setLeft(int x)
void setRight(int x)
void setTop(int y)
QSize size() const const
int top() const const
QPoint topLeft() const const
int width() const const
int x() const const
int y() const const
bool contains(const QPointF &point) const const
qreal height() const const
qreal width() const const
bool isNull() const const
const QSize & oldSize() const const
const QSize & size() const const
Qt::MouseButton button() const const
Qt::MouseButtons buttons() const const
QPointF globalPosition() const const
QPointF position() const const
QPointF scenePosition() const const
QSize expandedTo(const QSize &otherSize) const const
int height() const const
bool isEmpty() const const
int width() const const
SizeFDiagCursor
BottomEdge
transparent
WindowState
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isNull() const const
int toInt(bool *ok) const const
QPoint angleDelta() const const
Qt::ScrollPhase phase() const const
QPoint pixelDelta() const const
void setFlags(Qt::WindowFlags flags)
QRect geometry() const const
bool isActive() const const
QPoint mapToGlobal(const QPoint &pos) const const
QRegion mask() const const
void setMaximumHeight(int h)
QSize maximumSize() const const
void setMaximumWidth(int w)
void setMinimumHeight(int h)
void setMinimumWidth(int w)
virtual void moveEvent(QMoveEvent *ev)
QPoint position() const const
void raise()
void resize(const QSize &newSize)
QScreen * screen() const const
void setCursor(const QCursor &cursor)
void setGeometry(const QRect &rect)
void setMask(const QRegion &region)
void setMaximumSize(const QSize &size)
void setMinimumSize(const QSize &size)
void setPosition(const QPoint &pt)
virtual QSize size() const const override
void setTransientParent(QWindow *parent)
Qt::WindowType type() const const
void unsetCursor()
void visibleChanged(bool arg)
WId winId() const const
void windowStateChanged(Qt::WindowState windowState)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:54:11 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.