KDDockWidgets API Documentation 1.7
Loading...
Searching...
No Matches
ClassicIndicatorsWindow.cpp
Go to the documentation of this file.
1/*
2 This file is part of KDDockWidgets.
3
4 SPDX-FileCopyrightText: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5 Author: SĂ©rgio Martins <sergio.martins@kdab.com>
6
7 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
8
9 Contact KDAB at <info@kdab.com> for commercial licensing options.
10*/
11
12#include "ClassicIndicatorsWindow_p.h"
13#include "ClassicIndicators_p.h"
14#include "../Utils_p.h"
15
16using namespace KDDockWidgets;
17
18namespace KDDockWidgets {
19
20static QString iconName(DropLocation loc, bool active)
21{
22 QString suffix = active ? QStringLiteral("_active")
23 : QString();
24
25 QString name;
26 switch (loc) {
28 name = QStringLiteral("center");
29 break;
31 name = QStringLiteral("inner_left");
32 break;
34 name = QStringLiteral("inner_right");
35 break;
37 name = QStringLiteral("inner_bottom");
38 break;
40 name = QStringLiteral("inner_top");
41 break;
43 name = QStringLiteral("outter_left");
44 break;
46 name = QStringLiteral("outter_bottom");
47 break;
49 name = QStringLiteral("outter_right");
50 break;
52 name = QStringLiteral("outter_top");
53 break;
59 return QString();
60 }
61
62 return name + suffix;
63}
64}
65
66#ifdef KDDOCKWIDGETS_QTWIDGETS
67
68#include <QPainter>
69
70#define INDICATOR_WIDTH 40
71#define OUTTER_INDICATOR_MARGIN 10
72
73void Indicator::paintEvent(QPaintEvent *)
74{
75 QPainter p(this);
76 if (m_hovered)
77 p.drawImage(rect(), m_imageActive, rect());
78 else
79 p.drawImage(rect(), m_image, rect());
80}
81
82void Indicator::setHovered(bool hovered)
83{
84 if (hovered != m_hovered) {
85 m_hovered = hovered;
86 update();
87 if (hovered) {
88 q->setDropLocation(m_dropLocation);
89 } else if (q->currentDropLocation() == m_dropLocation) {
90 q->setDropLocation(DropLocation_None);
91 }
92 }
93}
94
95QString Indicator::iconName(bool active) const
96{
97 return KDDockWidgets::iconName(m_dropLocation, active);
98}
99
100QString Indicator::iconFileName(bool active) const
101{
102 const QString name = iconName(active);
103 return KDDockWidgets::windowManagerHasTranslucency() ? QStringLiteral(":/img/classic_indicators/%1.png").arg(name)
104 : QStringLiteral(":/img/classic_indicators/opaque/%1.png").arg(name);
105}
106
107static QWidgetAdapter *parentForIndicatorWindow(ClassicIndicators *classicIndicators_)
108{
109 // On Wayland it can't be a top-level, as we have no way of positioning it
110
111 return isWayland() ? classicIndicators_
112 : nullptr;
113}
114
115static Qt::WindowFlags flagsForIndicatorWindow()
116{
117 return isWayland() ? Qt::Widget
119}
120
121IndicatorWindow::IndicatorWindow(ClassicIndicators *classicIndicators_)
122 : QWidget(parentForIndicatorWindow(classicIndicators_), flagsForIndicatorWindow())
123 , classicIndicators(classicIndicators_)
124 , m_center(new Indicator(classicIndicators, this, DropLocation_Center)) // Each indicator is not a top-level. Otherwise there's noticeable delay.
125 , m_left(new Indicator(classicIndicators, this, DropLocation_Left))
126 , m_right(new Indicator(classicIndicators, this, DropLocation_Right))
127 , m_bottom(new Indicator(classicIndicators, this, DropLocation_Bottom))
128 , m_top(new Indicator(classicIndicators, this, DropLocation_Top))
129 , m_outterLeft(new Indicator(classicIndicators, this, DropLocation_OutterLeft))
130 , m_outterRight(new Indicator(classicIndicators, this, DropLocation_OutterRight))
131 , m_outterBottom(new Indicator(classicIndicators, this, DropLocation_OutterBottom))
132 , m_outterTop(new Indicator(classicIndicators, this, DropLocation_OutterTop))
133{
134 setWindowFlag(Qt::FramelessWindowHint, true);
135
137 // Ensure the overlay window is on top
138 setWindowFlag(Qt::WindowStaysOnTopHint, true);
139 }
140
141 setAttribute(Qt::WA_TranslucentBackground);
142
143 connect(classicIndicators, &ClassicIndicators::indicatorsVisibleChanged,
144 this, &IndicatorWindow::updateIndicatorVisibility);
145 connect(classicIndicators, &ClassicIndicators::indicatorsVisibleChanged,
146 this, &IndicatorWindow::updateIndicatorVisibility);
147
148 m_indicators << m_center << m_left << m_right << m_top << m_bottom
149 << m_outterBottom << m_outterTop << m_outterLeft << m_outterRight;
150}
151
152Indicator *IndicatorWindow::indicatorForLocation(DropLocation loc) const
153{
154 switch (loc) {
156 return m_center;
158 return m_left;
160 return m_right;
162 return m_bottom;
163 case DropLocation_Top:
164 return m_top;
166 return m_outterLeft;
168 return m_outterBottom;
170 return m_outterRight;
172 return m_outterTop;
178 return nullptr;
179 }
180
181 return nullptr;
182}
183
184void IndicatorWindow::updateMask()
185{
186 QRegion region;
187
188 if (!KDDockWidgets::windowManagerHasTranslucency()) {
189 for (Indicator *indicator : qAsConst(m_indicators)) {
190 if (indicator->isVisible())
191 region = region.united(QRegion(indicator->geometry(), QRegion::Rectangle));
192 }
193 }
194
195 setMask(region);
196}
197
198void IndicatorWindow::resizeEvent(QResizeEvent *ev)
199{
201 updatePositions();
202}
203
204void IndicatorWindow::updateIndicatorVisibility()
205{
206 for (Indicator *indicator : { m_left, m_right, m_bottom, m_top,
207 m_outterTop, m_outterLeft, m_outterRight, m_outterBottom,
208 m_center })
209 indicator->setVisible(classicIndicators->dropIndicatorVisible(indicator->m_dropLocation));
210
211 updateMask();
212}
213
214QPoint IndicatorWindow::posForIndicator(DropLocation loc) const
215{
216 Indicator *indicator = indicatorForLocation(loc);
217 return indicator->mapToGlobal(indicator->rect().center());
218}
219
220DropLocation IndicatorWindow::hover(QPoint globalPos)
221{
223
224 for (Indicator *indicator : qAsConst(m_indicators)) {
225 if (indicator->isVisible()) {
226 const bool hovered = indicator->rect().contains(indicator->mapFromGlobal(globalPos));
227 indicator->setHovered(hovered);
228 if (hovered)
229 loc = indicator->m_dropLocation;
230 }
231 }
232
233 return loc;
234}
235
236void IndicatorWindow::updatePositions()
237{
238 QRect r = rect();
239 const int indicatorWidth = m_outterBottom->width();
240 const int halfIndicatorWidth = m_outterBottom->width() / 2;
241
242 m_outterLeft->move(r.x() + OUTTER_INDICATOR_MARGIN, r.center().y() - halfIndicatorWidth);
243 m_outterBottom->move(r.center().x() - halfIndicatorWidth, r.y() + height() - indicatorWidth - OUTTER_INDICATOR_MARGIN);
244 m_outterTop->move(r.center().x() - halfIndicatorWidth, r.y() + OUTTER_INDICATOR_MARGIN);
245 m_outterRight->move(r.x() + width() - indicatorWidth - OUTTER_INDICATOR_MARGIN, r.center().y() - halfIndicatorWidth);
246 Frame *hoveredFrame = classicIndicators->m_hoveredFrame;
247 if (hoveredFrame) {
248 QRect hoveredRect = hoveredFrame->QWidget::geometry();
249 m_center->move(r.topLeft() + hoveredRect.center() - QPoint(halfIndicatorWidth, halfIndicatorWidth));
250 m_top->move(m_center->pos() - QPoint(0, indicatorWidth + OUTTER_INDICATOR_MARGIN));
251 m_right->move(m_center->pos() + QPoint(indicatorWidth + OUTTER_INDICATOR_MARGIN, 0));
252 m_bottom->move(m_center->pos() + QPoint(0, indicatorWidth + OUTTER_INDICATOR_MARGIN));
253 m_left->move(m_center->pos() - QPoint(indicatorWidth + OUTTER_INDICATOR_MARGIN, 0));
254 }
255}
256
257Indicator::Indicator(ClassicIndicators *classicIndicators, IndicatorWindow *parent, DropLocation location)
258 : QWidget(parent)
259 , q(classicIndicators)
260 , m_dropLocation(location)
261{
262 m_image = QImage(iconFileName(/*active=*/false)).scaled(INDICATOR_WIDTH, INDICATOR_WIDTH);
263 m_imageActive = QImage(iconFileName(/*active=*/true)).scaled(INDICATOR_WIDTH, INDICATOR_WIDTH);
264 setFixedSize(m_image.size());
265 setVisible(true);
266}
267
268#else
269
270#include <QQmlContext>
271
272IndicatorWindow::IndicatorWindow(KDDockWidgets::ClassicIndicators *classicIndicators)
273 : QQuickView()
274 , m_classicIndicators(classicIndicators)
275{
277 setColor(Qt::transparent);
278
279 rootContext()->setContextProperty(QStringLiteral("_window"), QVariant::fromValue<QObject *>(this));
280 setSource(QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/ClassicIndicatorsOverlay.qml")));
281
282
283 // Two workarounds for two unrelated bugs:
284 if (KDDockWidgets::isOffscreen()) {
285 // 1. We need to create the window asap, otherwise, if a drag triggers the indicator window
286 // to show, that creates a QOffscreenWindow, which flushes events in the ctor, triggering
287 // more hover events which will trigger another QOffscreenWindow.
288 // We then end up with a QWindow with two QPlatformWindow, and only one is deleted in
289 // at shutdown, meaning some timers aren't unregistered, meaning we get a crash when
290 // the timer event is sent to the destroyed QWindow.
291 create();
292 } else {
293 // 2.
294 // Small hack to avoid flickering when we drag over a window the first time
295 // Not sure why a simply create() doesn't work instead
296 // Not if offscreen though, as that QPA is flaky with window activation/focus
297 resize(QSize(1, 1));
298 show();
299 hide();
300 }
301}
302
303DropLocation IndicatorWindow::hover(QPoint pt)
304{
305 QQuickItem *item = indicatorForPos(pt);
306 const DropLocation loc = item ? locationForIndicator(item)
308 classicIndicators()->setDropLocation(loc);
309 return loc;
310}
311
312QQuickItem *IndicatorWindow::indicatorForPos(QPoint pt) const
313{
314 const QVector<QQuickItem *> indicators = indicatorItems();
315 Q_ASSERT(indicators.size() == 9);
316
317 for (QQuickItem *item : indicators) {
318 if (item->isVisible()) {
319 QRect rect(0, 0, int(item->width()), int(item->height()));
320 rect.moveTopLeft(item->mapToGlobal(QPointF(0, 0)).toPoint());
321 if (rect.contains(pt)) {
322 return item;
323 }
324 }
325 }
326
327 return nullptr;
328}
329
330void IndicatorWindow::updatePositions()
331{
332 // Not needed to implement, the Indicators use QML anchors
333}
334
335QPoint IndicatorWindow::posForIndicator(KDDockWidgets::DropLocation loc) const
336{
337 QQuickItem *indicator = IndicatorWindow::indicatorForLocation(loc);
338 return indicator->mapToGlobal(indicator->boundingRect().center()).toPoint();
339}
340
341QString IndicatorWindow::iconName(int loc, bool active) const
342{
343 return KDDockWidgets::iconName(DropLocation(loc), active);
344}
345
346ClassicIndicators *IndicatorWindow::classicIndicators() const
347{
348 return m_classicIndicators;
349}
350
351QQuickItem *IndicatorWindow::indicatorForLocation(DropLocation loc) const
352{
353 const QVector<QQuickItem *> indicators = indicatorItems();
354 Q_ASSERT(indicators.size() == 9);
355
356 for (QQuickItem *item : indicators) {
357 if (locationForIndicator(item) == loc)
358 return item;
359 }
360
361 qWarning() << Q_FUNC_INFO << "Couldn't find indicator for location" << loc;
362 return nullptr;
363}
364
365DropLocation IndicatorWindow::locationForIndicator(const QQuickItem *item) const
366{
367 return DropLocation(item->property("indicatorType").toInt());
368}
369
370QVector<QQuickItem *> IndicatorWindow::indicatorItems() const
371{
372 QVector<QQuickItem *> indicators;
373 indicators.reserve(9);
374
375 QQuickItem *root = rootObject();
376 const QList<QQuickItem *> items = root->childItems();
377 for (QQuickItem *item : items) {
378 if (QString::fromLatin1(item->metaObject()->className()).startsWith(QLatin1String("ClassicIndicator_QMLTYPE"))) {
379 indicators.push_back(item);
380 } else if (item->objectName() == QLatin1String("innerIndicators")) {
381 const QList<QQuickItem *> innerIndicators = item->childItems();
382 for (QQuickItem *innerItem : innerIndicators) {
383 if (QString::fromLatin1(innerItem->metaObject()->className()).startsWith(QLatin1String("ClassicIndicator_QMLTYPE"))) {
384 indicators.push_back(innerItem);
385 }
386 }
387 }
388 }
389
390 return indicators;
391}
392
393#endif // QtQuick
static Config & self()
returns the singleton Config instance
Definition Config.cpp:84
@ Flag_KeepAboveIfNotUtilityWindow
Only meaningful if Flag_DontUseUtilityFloatingWindows is set. If floating windows are normal windows,...
Definition Config.h:105
DropLocation
Enum describing the different drop indicator types.
static QString iconName(DropLocation loc, bool active)
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
int x() const const
int y() const const
QPoint center() const const
QPoint topLeft() const const
int x() const const
int y() const const
QRegion united(const QRegion &r) const const
QString fromLatin1(const char *str, int size)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
transparent
WA_TranslucentBackground
typedef WindowFlags
void push_back(const T &value)
void reserve(int size)
int size() const const
virtual void resizeEvent(QResizeEvent *event)

© 2019-2023 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated on Wed Nov 1 2023 00:02:31 for KDDockWidgets API Documentation by doxygen 1.9.8