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

© 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 Mon Mar 7 2022 02:01:20 for KDDockWidgets API Documentation by doxygen 1.8.20