KDDockWidgets API Documentation  1.6
ClassicIndicatorsWindow.cpp
Go to the documentation of this file.
1 /*
2  This file is part of KDDockWidgets.
3 
4  SPDX-FileCopyrightText: 2019-2022 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 
16 using namespace KDDockWidgets;
17 
18 namespace KDDockWidgets {
19 
20 static 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;
30  case DropLocation_Left:
31  name = QStringLiteral("inner_left");
32  break;
33  case DropLocation_Right:
34  name = QStringLiteral("inner_right");
35  break;
37  name = QStringLiteral("inner_bottom");
38  break;
39  case DropLocation_Top:
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;
54  case DropLocation_None:
55  case DropLocation_Inner:
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 
73 void 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 
82 void 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 
95 QString Indicator::iconName(bool active) const
96 {
97  return KDDockWidgets::iconName(m_dropLocation, active);
98 }
99 
100 QString 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 
107 static 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 
115 static Qt::WindowFlags flagsForIndicatorWindow()
116 {
117  return isWayland() ? Qt::Widget
119 }
120 
121 IndicatorWindow::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 
152 Indicator *IndicatorWindow::indicatorForLocation(DropLocation loc) const
153 {
154  switch (loc) {
155  case DropLocation_Center:
156  return m_center;
157  case DropLocation_Left:
158  return m_left;
159  case DropLocation_Right:
160  return m_right;
161  case DropLocation_Bottom:
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;
173  case DropLocation_None:
174  case DropLocation_Outter:
175  case DropLocation_Inner:
178  return nullptr;
179  }
180 
181  return nullptr;
182 }
183 
184 void 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 
198 void IndicatorWindow::resizeEvent(QResizeEvent *ev)
199 {
201  updatePositions();
202 }
203 
204 void 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 
214 QPoint IndicatorWindow::posForIndicator(DropLocation loc) const
215 {
216  Indicator *indicator = indicatorForLocation(loc);
217  return indicator->mapToGlobal(indicator->rect().center());
218 }
219 
220 DropLocation 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 
236 void 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 
257 Indicator::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 
272 IndicatorWindow::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 
303 DropLocation 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 
312 QQuickItem *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 
330 void IndicatorWindow::updatePositions()
331 {
332  // Not needed to implement, the Indicators use QML anchors
333 }
334 
335 QPoint IndicatorWindow::posForIndicator(KDDockWidgets::DropLocation loc) const
336 {
337  QQuickItem *indicator = IndicatorWindow::indicatorForLocation(loc);
338  return indicator->mapToGlobal(indicator->boundingRect().center()).toPoint();
339 }
340 
341 QString IndicatorWindow::iconName(int loc, bool active) const
342 {
343  return KDDockWidgets::iconName(DropLocation(loc), active);
344 }
345 
346 ClassicIndicators *IndicatorWindow::classicIndicators() const
347 {
348  return m_classicIndicators;
349 }
350 
351 QQuickItem *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 
365 DropLocation IndicatorWindow::locationForIndicator(const QQuickItem *item) const
366 {
367  return DropLocation(item->property("indicatorType").toInt());
368 }
369 
370 QVector<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
QRegion
KDDockWidgets::DropLocation_Top
@ DropLocation_Top
Definition: KDDockWidgets.h:232
QRect::topLeft
QPoint topLeft() const const
QUrl
QRect
KDDockWidgets::DropLocation_Vertical
@ DropLocation_Vertical
Definition: KDDockWidgets.h:243
KDDockWidgets::DropLocation_Center
@ DropLocation_Center
Definition: KDDockWidgets.h:235
QWidget::resizeEvent
virtual void resizeEvent(QResizeEvent *event)
KDDockWidgets::DropLocation_Inner
@ DropLocation_Inner
Definition: KDDockWidgets.h:240
QVector::push_back
void push_back(const T &value)
QWidget
KDDockWidgets::Config::Flag_KeepAboveIfNotUtilityWindow
@ Flag_KeepAboveIfNotUtilityWindow
Only meaningful if Flag_DontUseUtilityFloatingWindows is set. If floating windows are normal windows,...
Definition: Config.h:105
QImage::scaled
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QSize
QRect::x
int x() const const
QRect::y
int y() const const
QPoint::x
int x() const const
QPoint::y
int y() const const
QList
QPainter
QPointF
KDDockWidgets::iconName
static QString iconName(DropLocation loc, bool active)
Definition: ClassicIndicatorsWindow.cpp:20
KDDockWidgets::DropLocation_Right
@ DropLocation_Right
Definition: KDDockWidgets.h:233
KDDockWidgets::DropLocation_Bottom
@ DropLocation_Bottom
Definition: KDDockWidgets.h:234
Qt::WindowFlags
typedef WindowFlags
QRegion::Rectangle
Rectangle
QString
KDDockWidgets::DropLocation_OutterTop
@ DropLocation_OutterTop
Definition: KDDockWidgets.h:237
QRect::center
QPoint center() const const
QLatin1String
QVector::reserve
void reserve(int size)
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
KDDockWidgets::DropLocation_Left
@ DropLocation_Left
Definition: KDDockWidgets.h:231
QString::fromLatin1
QString fromLatin1(const char *str, int size)
QResizeEvent
QPaintEvent
KDDockWidgets::DropLocation_OutterLeft
@ DropLocation_OutterLeft
Definition: KDDockWidgets.h:236
KDDockWidgets
Definition: Config.cpp:37
KDDockWidgets::DropLocation_OutterRight
@ DropLocation_OutterRight
Definition: KDDockWidgets.h:238
QVector::size
int size() const const
QRegion::united
QRegion united(const QRegion &r) const const
KDDockWidgets::DropLocation_Horizontal
@ DropLocation_Horizontal
Definition: KDDockWidgets.h:242
KDDockWidgets::DropLocation
DropLocation
Enum describing the different drop indicator types.
Definition: KDDockWidgets.h:229
QVector
KDDockWidgets::DropLocation_None
@ DropLocation_None
Definition: KDDockWidgets.h:230
QImage
Qt::WA_TranslucentBackground
WA_TranslucentBackground
Qt::transparent
transparent
QPoint
KDDockWidgets::Config::self
static Config & self()
returns the singleton Config instance
Definition: Config.cpp:84
KDDockWidgets::DropLocation_Outter
@ DropLocation_Outter
Definition: KDDockWidgets.h:241
KDDockWidgets::DropLocation_OutterBottom
@ DropLocation_OutterBottom
Definition: KDDockWidgets.h:239

© 2019-2022 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 Thu Sep 15 2022 00:16:27 for KDDockWidgets API Documentation by doxygen 1.8.20