Plasma-framework

popupplasmawindow.cpp
1/*
2 SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "popupplasmawindow.h"
7
8#include <kwindoweffects.h>
9#include <kwindowsystem.h>
10
11#include "debug_p.h"
12#include <QGuiApplication>
13#include <QScreen>
14#include <qnamespace.h>
15#include <qtmetamacros.h>
16
17#include "plasmashellwaylandintegration.h"
18#include "private/utils.h"
19#include "transientplacementhint_p.h"
20
21namespace PlasmaQuick
22{
23
24class PopupPlasmaWindowPrivate
25{
26public:
27 PopupPlasmaWindowPrivate(PopupPlasmaWindow *_q);
28
29 void updateEffectivePopupDirection(const QRect &anchorRect, const QRect &relativePopupPosition);
30 void updateSlideEffect();
31 void updatePosition();
32 void updatePositionX11(const QPoint &position);
33 void updatePositionWayland(const QPoint &position);
34 void updateBorders(const QRect &globalPosition);
35 void updateVisualParentWindow();
36
37 PopupPlasmaWindow *q;
38 QPointer<QQuickItem> m_visualParent;
39 QPointer<QQuickWindow> m_visualParentWindow;
40 PopupPlasmaWindow::RemoveBorders m_removeBorderStrategy = PopupPlasmaWindow::Never;
41 bool m_needsReposition = false;
42 bool m_floating = false;
43 bool m_animated = false;
44 int m_margin = 0;
45 Qt::Edge m_popupDirection = Qt::TopEdge;
46 Qt::Edge m_effectivePopupDirection = Qt::TopEdge;
47};
48
49PopupPlasmaWindowPrivate::PopupPlasmaWindowPrivate(PopupPlasmaWindow *_q)
50 : q(_q)
51{
52}
53
54/**
55 * PopupPlasmaWindowPrivate::updateSlideEffect
56 * @param anchorRect - the rect around where the popup should be placed relative to the parent window
57 * @param relativePopupPosition - the final rect of the popup relative to the parent window
58 *
59 * This is based purely on position in prepartion for being called in a wayland configure event
60 */
61void PopupPlasmaWindowPrivate::updateEffectivePopupDirection(const QRect &anchorRect, const QRect &relativePopupPosition)
62{
63 Qt::Edge effectivePopupDirection = Qt::TopEdge;
64 if (m_popupDirection == Qt::TopEdge || m_popupDirection == Qt::BottomEdge) {
65 if (relativePopupPosition.center().y() >= anchorRect.center().y()) {
66 effectivePopupDirection = Qt::BottomEdge;
67 } else {
68 effectivePopupDirection = Qt::TopEdge;
69 }
70 }
71 if (m_popupDirection == Qt::LeftEdge || m_popupDirection == Qt::RightEdge) {
72 if (relativePopupPosition.center().x() >= anchorRect.center().x()) {
73 effectivePopupDirection = Qt::RightEdge;
74 } else {
75 effectivePopupDirection = Qt::LeftEdge;
76 }
77 }
78
79 if (effectivePopupDirection != m_effectivePopupDirection) {
80 Q_EMIT q->effectivePopupDirectionChanged();
81 m_effectivePopupDirection = effectivePopupDirection;
82 }
83}
84
85void PopupPlasmaWindowPrivate::updateSlideEffect()
86{
87 KWindowEffects::SlideFromLocation slideLocation = KWindowEffects::NoEdge;
88 if (!m_animated) {
89 KWindowEffects::slideWindow(q, slideLocation);
90 return;
91 }
92 switch (m_effectivePopupDirection) {
93 case Qt::TopEdge:
94 slideLocation = KWindowEffects::BottomEdge;
95 break;
96 case Qt::BottomEdge:
97 slideLocation = KWindowEffects::TopEdge;
98 break;
99 case Qt::LeftEdge:
100 slideLocation = KWindowEffects::RightEdge;
101 break;
102 case Qt::RightEdge:
103 slideLocation = KWindowEffects::LeftEdge;
104 break;
105 }
106 KWindowEffects::slideWindow(q, slideLocation);
107}
108
109void PopupPlasmaWindowPrivate::updatePosition()
110{
111 m_needsReposition = false;
112
113 if (!m_visualParent || !m_visualParent->window()) {
114 qCWarning(LOG_PLASMAQUICK) << "Exposed with no visual parent. Window positioning broken.";
115 return;
116 }
117 q->setTransientParent(m_visualParent->window());
118 TransientPlacementHint placementHint;
119 QRectF parentAnchorRect = QRectF(m_visualParent->mapToScene(QPointF(0, 0)), m_visualParent->size());
120
121 if (!m_floating) {
122 // pad parentAnchorRect to the window it's in, so that the popup appears outside the panel
123 // even if the tooltip area does not fill it
124 if (m_popupDirection == Qt::TopEdge || m_popupDirection == Qt::BottomEdge) {
125 parentAnchorRect.setTop(0);
126 parentAnchorRect.setBottom(m_visualParent->window()->height());
127 }
128 if (m_popupDirection == Qt::LeftEdge || m_popupDirection == Qt::RightEdge) {
129 parentAnchorRect.setLeft(0);
130 parentAnchorRect.setRight(m_visualParent->window()->width());
131 }
132 }
133
134 placementHint.setParentAnchorArea(parentAnchorRect.toRect());
135 placementHint.setParentAnchor(m_popupDirection);
136 placementHint.setPopupAnchor(PlasmaQuickPrivate::oppositeEdge(m_popupDirection));
137 placementHint.setFlipConstraintAdjustments(m_floating ? Qt::Vertical : Qt::Orientations());
138 placementHint.setMargin(m_margin);
139
140 const QRect popupPosition = TransientPlacementHelper::popupRect(q, placementHint);
141
142 QRect relativePopupPosition = popupPosition;
143 if (m_visualParent->window()) {
144 relativePopupPosition = relativePopupPosition.translated(-m_visualParent->window()->position());
145 }
146 updateEffectivePopupDirection(parentAnchorRect.toRect(), relativePopupPosition);
147 updateSlideEffect();
148
150 updatePositionX11(popupPosition.topLeft());
152 updatePositionWayland(popupPosition.topLeft());
153 }
154
155 updateBorders(popupPosition);
156}
157
158void PopupPlasmaWindowPrivate::updatePositionX11(const QPoint &position)
159{
160 q->setPosition(position);
161}
162
163void PopupPlasmaWindowPrivate::updatePositionWayland(const QPoint &position)
164{
165 // still update's Qt internal reference as it's used by the next dialog
166 // this can be dropped when we're using true semantic positioning in the backend
167 q->setPosition(position);
168
169 PlasmaShellWaylandIntegration::get(q)->setPosition(position);
170}
171
172void PopupPlasmaWindowPrivate::updateBorders(const QRect &globalPosition)
173{
174 // disables borders for the edges that are touching the screen edge
175
176 QScreen *screen = QGuiApplication::screenAt(globalPosition.center());
177 if (!screen) {
178 return;
179 }
180 const QRect screenGeometry = screen->geometry();
181
183
184 if (m_margin) {
185 q->setBorders(enabledBorders);
186 return;
187 }
188
189 if (m_removeBorderStrategy & PopupPlasmaWindow::AtScreenEdges) {
190 if (globalPosition.top() <= screenGeometry.top()) {
191 enabledBorders.setFlag(Qt::TopEdge, false);
192 }
193 if (globalPosition.bottom() >= screenGeometry.bottom()) {
194 enabledBorders.setFlag(Qt::BottomEdge, false);
195 }
196 if (globalPosition.left() <= screenGeometry.left()) {
197 enabledBorders.setFlag(Qt::LeftEdge, false);
198 }
199 if (globalPosition.right() >= screenGeometry.right()) {
200 enabledBorders.setFlag(Qt::RightEdge, false);
201 }
202 }
203 if (m_removeBorderStrategy & PopupPlasmaWindow::AtPanelEdges) {
204 switch (m_popupDirection) {
205 case Qt::LeftEdge:
206 enabledBorders.setFlag(Qt::RightEdge, false);
207 break;
208 case Qt::RightEdge:
209 enabledBorders.setFlag(Qt::LeftEdge, false);
210 break;
211 case Qt::BottomEdge:
212 enabledBorders.setFlag(Qt::TopEdge, false);
213 break;
214 case Qt::TopEdge:
215 default:
216 enabledBorders.setFlag(Qt::BottomEdge, false);
217 break;
218 }
219 }
220 q->setBorders(enabledBorders);
221}
222
223void PopupPlasmaWindowPrivate::updateVisualParentWindow()
224{
225 if (m_visualParentWindow) {
226 QObject::disconnect(m_visualParentWindow, &QQuickWindow::yChanged, q, &PopupPlasmaWindow::queuePositionUpdate);
227 QObject::disconnect(m_visualParentWindow, &QQuickWindow::xChanged, q, &PopupPlasmaWindow::queuePositionUpdate);
228 }
229
230 m_visualParentWindow = m_visualParent ? m_visualParent->window() : nullptr;
231
232 if (m_visualParentWindow) {
233 QObject::connect(m_visualParentWindow, &QQuickWindow::yChanged, q, &PopupPlasmaWindow::queuePositionUpdate);
234 QObject::connect(m_visualParentWindow, &QQuickWindow::xChanged, q, &PopupPlasmaWindow::queuePositionUpdate);
235 }
236}
237
238PopupPlasmaWindow::PopupPlasmaWindow(const QString &svgPrefix)
239 : PlasmaWindow(svgPrefix)
240 , d(new PopupPlasmaWindowPrivate(this))
241{
242}
243
244PopupPlasmaWindow::~PopupPlasmaWindow()
245{
246}
247
248void PopupPlasmaWindow::setVisualParent(QQuickItem *item)
249{
250 if (item == d->m_visualParent) {
251 return;
252 }
253
254 if (d->m_visualParent) {
255 disconnect(d->m_visualParent, SIGNAL(windowChanged(QQuickWindow *)), this, SLOT(updateVisualParentWindow()));
256 }
257
258 d->m_visualParent = item;
259 d->updateVisualParentWindow();
260
261 if (d->m_visualParent) {
262 connect(d->m_visualParent, SIGNAL(windowChanged(QQuickWindow *)), this, SLOT(updateVisualParentWindow()));
263 }
264
265 Q_EMIT visualParentChanged();
266 queuePositionUpdate();
267}
268
269QQuickItem *PopupPlasmaWindow::visualParent() const
270{
271 return d->m_visualParent;
272}
273
274Qt::Edge PopupPlasmaWindow::popupDirection() const
275{
276 return d->m_popupDirection;
277}
278
279void PopupPlasmaWindow::setPopupDirection(Qt::Edge popupDirection)
280{
281 if (popupDirection == d->m_popupDirection) {
282 return;
283 }
284 d->m_popupDirection = popupDirection;
285
286 if (isExposed()) {
287 qCWarning(LOG_PLASMAQUICK) << "location should be set before showing popup window";
288 }
289 queuePositionUpdate();
290
291 Q_EMIT popupDirectionChanged();
292}
293
294Qt::Edge PopupPlasmaWindow::effectivePopupDirection() const
295{
296 return d->m_effectivePopupDirection;
297}
298
299bool PopupPlasmaWindow::floating() const
300{
301 return d->m_floating;
302}
303
304void PopupPlasmaWindow::setFloating(bool floating)
305{
306 if (floating == d->m_floating) {
307 return;
308 }
309 d->m_floating = floating;
310 queuePositionUpdate();
311 Q_EMIT floatingChanged();
312}
313
314bool PopupPlasmaWindow::animated() const
315{
316 return d->m_animated;
317}
318
319void PopupPlasmaWindow::setAnimated(bool animated)
320{
321 d->m_animated = animated;
322 queuePositionUpdate();
323 Q_EMIT animatedChanged();
324}
325
326PopupPlasmaWindow::RemoveBorders PopupPlasmaWindow::removeBorderStrategy() const
327{
328 return d->m_removeBorderStrategy;
329}
330
331void PopupPlasmaWindow::setRemoveBorderStrategy(PopupPlasmaWindow::RemoveBorders strategy)
332{
333 if (d->m_removeBorderStrategy == strategy) {
334 return;
335 }
336
337 d->m_removeBorderStrategy = strategy;
338 queuePositionUpdate(); // This will update borders as well
339 Q_EMIT removeBorderStrategyChanged();
340}
341
342int PopupPlasmaWindow::margin() const
343{
344 return d->m_margin;
345}
346
347void PopupPlasmaWindow::setMargin(int margin)
348{
349 if (d->m_margin == margin) {
350 return;
351 }
352
353 d->m_margin = margin;
354 queuePositionUpdate();
355 Q_EMIT marginChanged();
356}
357
358bool PopupPlasmaWindow::event(QEvent *event)
359{
360 switch (event->type()) {
362 if (d->m_needsReposition) {
363 d->updatePosition();
364 }
365 break;
366 case QEvent::Show:
367 d->updatePosition();
368 break;
369 case QEvent::Resize:
370 d->updatePosition();
371 break;
372 default:
373 break;
374 }
376}
377
378void PopupPlasmaWindow::queuePositionUpdate()
379{
380 d->m_needsReposition = true;
381}
382}
383
384#include "moc_popupplasmawindow.cpp"
static bool isPlatformX11()
static bool isPlatformWayland()
static PlasmaShellWaylandIntegration * get(QWindow *window)
Returns the relevant PlasmaWaylandShellIntegration instance for this window creating one if needed.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void slideWindow(QWindow *window, SlideFromLocation location, int offset=-1)
The EdgeEventForwarder class This class forwards edge events to be replayed within the given margin T...
Definition action.h:20
QScreen * screenAt(const QPoint &point)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
int x() const const
int y() const const
int bottom() const const
QPoint center() const const
int left() const const
int right() const const
int top() const const
QPoint topLeft() const const
QRect translated(const QPoint &offset) const const
void setBottom(qreal y)
void setLeft(qreal x)
void setRight(qreal x)
void setTop(qreal y)
QRect toRect() const const
Vertical
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
virtual bool event(QEvent *ev) override
void xChanged(int arg)
void yChanged(int arg)
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.