KDDockWidgets API Documentation  1.6
DropArea.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 "DropArea_p.h"
13 #include "Config.h"
14 #include "DockRegistry_p.h"
15 #include "DockWidgetBase.h"
16 #include "DockWidgetBase_p.h"
17 #include "Draggable_p.h"
18 #include "DropIndicatorOverlayInterface_p.h"
19 #include "FloatingWindow_p.h"
20 #include "Frame_p.h"
21 #include "FrameworkWidgetFactory.h"
22 #include "Logging_p.h"
23 #include "MainWindowBase.h"
24 #include "Utils_p.h"
25 #include "multisplitter/Item_p.h"
26 #include "WindowBeingDragged_p.h"
27 
28 using namespace KDDockWidgets;
29 
36 DropArea::DropArea(QWidgetOrQuick *parent, bool isMDIWrapper)
37  : MultiSplitter(parent)
38  , m_isMDIWrapper(isMDIWrapper)
39  , m_dropIndicatorOverlay(Config::self().frameworkWidgetFactory()->createDropIndicatorOverlay(this))
40 {
41  qCDebug(creation) << "DropArea";
42  if (isWayland()) {
43 #ifdef KDDOCKWIDGETS_QTWIDGETS
44  setAcceptDrops(true);
45 #else
46  qWarning() << "Dropping not implement for QtQuick on Wayland yet!";
47 #endif
48  }
49 
50  if (m_isMDIWrapper) {
51  connect(this, &MultiSplitter::visibleWidgetCountChanged, this, [this] {
52  auto dw = mdiDockWidgetWrapper();
53  if (!dw) {
54  qWarning() << Q_FUNC_INFO << "Unexpected null wrapper dock widget";
55  return;
56  }
57 
58  if (visibleCount() > 0) {
59  // The title of our MDI frame will need to change to the app name if we have more than 1 dock widget nested
60  Q_EMIT dw->titleChanged(dw->title());
61  } else {
62  // Our wrapeper isn't needed anymore
63  dw->deleteLater();
64  }
65  });
66  }
67 }
68 
69 DropArea::~DropArea()
70 {
71  m_inDestructor = true;
72  qCDebug(creation) << "~DropArea";
73 }
74 
75 Frame::List DropArea::frames() const
76 {
77  return findChildren<Frame *>(QString(), Qt::FindDirectChildrenOnly);
78 }
79 
80 Frame *DropArea::frameContainingPos(QPoint globalPos) const
81 {
82  const Layouting::Item::List &items = this->items();
83  for (Layouting::Item *item : items) {
84  auto frame = static_cast<Frame *>(item->guestAsQObject());
85  if (!frame || !frame->QWidgetAdapter::isVisible()) {
86  continue;
87  }
88 
89  if (frame->containsMouse(globalPos))
90  return frame;
91  }
92  return nullptr;
93 }
94 
95 void DropArea::updateFloatingActions()
96 {
97  const Frame::List frames = this->frames();
98  for (Frame *frame : frames)
99  frame->updateFloatingActions();
100 }
101 
102 Layouting::Item *DropArea::centralFrame() const
103 {
104  for (Layouting::Item *item : this->items()) {
105  if (auto f = static_cast<Frame *>(item->guestAsQObject())) {
106  if (f->isCentralFrame())
107  return item;
108  }
109  }
110  return nullptr;
111 }
112 
113 void DropArea::addDockWidget(DockWidgetBase *dw, Location location,
114  DockWidgetBase *relativeTo, InitialOption option)
115 {
116  if (!dw || dw == relativeTo || location == Location_None) {
117  qWarning() << Q_FUNC_INFO << "Invalid parameters" << dw << relativeTo << location;
118  return;
119  }
120 
121  if ((option.visibility == InitialVisibilityOption::StartHidden) && dw->d->frame() != nullptr) {
122  // StartHidden is just to be used at startup, not to moving stuff around
123  qWarning() << Q_FUNC_INFO << "Dock widget already exists in the layout";
124  return;
125  }
126 
127  if (!validateAffinity(dw))
128  return;
129 
130  Frame *frame = nullptr;
131  Frame *relativeToFrame = relativeTo ? relativeTo->d->frame() : nullptr;
132 
133  dw->d->saveLastFloatingGeometry();
134 
135  const bool hadSingleFloatingFrame = hasSingleFloatingFrame();
136 
137  // Check if the dock widget already exists in the layout
138  if (containsDockWidget(dw)) {
139  Frame *oldFrame = dw->d->frame();
140  if (oldFrame->hasSingleDockWidget()) {
141  Q_ASSERT(oldFrame->containsDockWidget(dw));
142  // The frame only has this dock widget, and the frame is already in the layout. So move the frame instead
143  frame = oldFrame;
144  } else {
145  frame = Config::self().frameworkWidgetFactory()->createFrame();
146  frame->addWidget(dw);
147  }
148  } else {
149  frame = Config::self().frameworkWidgetFactory()->createFrame();
150  frame->addWidget(dw);
151  }
152 
153  if (option.startsHidden()) {
154  addWidget(dw, location, relativeToFrame, option);
155  } else {
156  addWidget(frame, location, relativeToFrame, option);
157  }
158 
159  if (hadSingleFloatingFrame && !hasSingleFloatingFrame()) {
160  // The dock widgets that already existed in our layout need to have their floatAction() updated
161  // otherwise it's still checked. Only the dropped dock widget got updated
162  updateFloatingActions();
163  }
164 }
165 
166 bool DropArea::containsDockWidget(DockWidgetBase *dw) const
167 {
168  return dw->d->frame() && LayoutWidget::containsFrame(dw->d->frame());
169 }
170 
171 bool DropArea::hasSingleFloatingFrame() const
172 {
173  const Frame::List frames = this->frames();
174  return frames.size() == 1 && frames.first()->isFloating();
175 }
176 
177 bool DropArea::hasSingleFrame() const
178 {
179  return visibleCount() == 1;
180 }
181 
182 QStringList DropArea::affinities() const
183 {
184  if (auto mw = mainWindow()) {
185  return mw->affinities();
186  } else if (auto fw = floatingWindow()) {
187  return fw->affinities();
188  }
189 
190  return {};
191 }
192 
193 void DropArea::layoutParentContainerEqually(DockWidgetBase *dw)
194 {
195  Layouting::Item *item = itemForFrame(dw->d->frame());
196  if (!item) {
197  qWarning() << Q_FUNC_INFO << "Item not found for" << dw << dw->d->frame();
198  return;
199  }
200 
201  layoutEqually(item->parentBoxContainer());
202 }
203 
204 DropLocation DropArea::hover(WindowBeingDragged *draggedWindow, QPoint globalPos)
205 {
206  if (Config::self().dropIndicatorsInhibited() || !validateAffinity(draggedWindow))
207  return DropLocation_None;
208 
209  if (!m_dropIndicatorOverlay) {
210  qWarning() << Q_FUNC_INFO << "The frontend is missing a drop indicator overlay";
211  return DropLocation_None;
212  }
213 
214  Frame *frame = frameContainingPos(globalPos); // Frame is nullptr if MainWindowOption_HasCentralFrame isn't set
215  m_dropIndicatorOverlay->setWindowBeingDragged(true);
216  m_dropIndicatorOverlay->setHoveredFrame(frame);
217  return m_dropIndicatorOverlay->hover(globalPos);
218 }
219 
220 static bool isOutterLocation(DropLocation location)
221 {
222  switch (location) {
227  return true;
228  default:
229  return false;
230  }
231 }
232 
233 bool DropArea::drop(WindowBeingDragged *droppedWindow, QPoint globalPos)
234 {
235  FloatingWindow *floatingWindow = droppedWindow->floatingWindow();
236 
237  if (floatingWindow == window()) {
238  qWarning() << "Refusing to drop onto itself"; // Doesn't happen
239  return false;
240  }
241 
242  if (m_dropIndicatorOverlay->currentDropLocation() == DropLocation_None) {
243  qCDebug(hovering) << "DropArea::drop: bailing out, drop location = none";
244  return false;
245  }
246 
247  qCDebug(dropping) << "DropArea::drop:" << droppedWindow;
248 
249  hover(droppedWindow, globalPos);
250  auto droploc = m_dropIndicatorOverlay->currentDropLocation();
251  Frame *acceptingFrame = m_dropIndicatorOverlay->hoveredFrame();
252  if (!(acceptingFrame || isOutterLocation(droploc))) {
253  qWarning() << "DropArea::drop: asserted with frame=" << acceptingFrame
254  << "; Location=" << droploc;
255  return false;
256  }
257 
258  return drop(droppedWindow, acceptingFrame, droploc);
259 }
260 
261 bool DropArea::drop(WindowBeingDragged *draggedWindow, Frame *acceptingFrame,
262  DropLocation droploc)
263 {
264  FloatingWindow *droppedWindow = draggedWindow ? draggedWindow->floatingWindow()
265  : nullptr;
266 
267  if (isWayland() && !droppedWindow) {
268  // This is the Wayland special case.
269  // With other platforms, when detaching a tab or dock widget we create the FloatingWindow immediately.
270  // With Wayland we delay the floating window until we drop it.
271  // Ofc, we could just dock the dockwidget without the temporary FloatingWindow, but this way we reuse
272  // 99% of the rest of the code, without adding more wayland special cases
273  droppedWindow = draggedWindow ? draggedWindow->draggable()->makeWindow()->floatingWindow()
274  : nullptr;
275  if (!droppedWindow) {
276  // Doesn't happen
277  qWarning() << Q_FUNC_INFO << "Wayland: Expected window" << draggedWindow;
278  return false;
279  }
280  }
281 
282  bool result = true;
283  const bool needToFocusNewlyDroppedWidgets = Config::self().flags() & Config::Flag_TitleBarIsFocusable;
284  const DockWidgetBase::List droppedDockWidgets = needToFocusNewlyDroppedWidgets
285  ? droppedWindow->layoutWidget()->dockWidgets()
286  : DockWidgetBase::List(); // just so save some memory allocations for the case where this
287  // variable isn't used
288 
289  switch (droploc) {
290  case DropLocation_Left:
291  case DropLocation_Top:
292  case DropLocation_Bottom:
293  case DropLocation_Right:
294  result = drop(droppedWindow, DropIndicatorOverlayInterface::multisplitterLocationFor(droploc), acceptingFrame);
295  break;
300  result = drop(droppedWindow, DropIndicatorOverlayInterface::multisplitterLocationFor(droploc), nullptr);
301  break;
302  case DropLocation_Center:
303  qCDebug(hovering) << "Tabbing" << droppedWindow << "into" << acceptingFrame;
304  if (!validateAffinity(droppedWindow, acceptingFrame))
305  return false;
306  acceptingFrame->addWidget(droppedWindow);
307  break;
308 
309  default:
310  qWarning() << "DropArea::drop: Unexpected drop location" << m_dropIndicatorOverlay->currentDropLocation();
311  result = false;
312  break;
313  }
314 
315  if (result) {
316  // Window receiving the drop gets raised.
317  // Exception: Under EGLFS we don't raise the fullscreen main window, as then all floating windows would
318  // go behind. It's also unneeded to raise, as it's fullscreen.
319 
320  const bool isEGLFSRootWindow = isEGLFS() && (window()->isFullScreen() || window()->isMaximized());
321  if (!isEGLFSRootWindow)
322  raiseAndActivate();
323 
324  if (needToFocusNewlyDroppedWidgets) {
325  // Let's also focus the newly dropped dock widget
326  if (!droppedDockWidgets.isEmpty()) {
327  // If more than 1 was dropped, we only focus the first one
328  Frame *frame = droppedDockWidgets.first()->d->frame();
329  frame->FocusScope::focus(Qt::MouseFocusReason);
330  } else {
331  // Doesn't happen.
332  qWarning() << Q_FUNC_INFO << "Nothing was dropped?";
333  }
334  }
335  }
336 
337  return result;
338 }
339 
340 bool DropArea::drop(QWidgetOrQuick *droppedWindow, KDDockWidgets::Location location, Frame *relativeTo)
341 {
342  qCDebug(docking) << "DropArea::addFrame";
343 
344  if (auto dock = qobject_cast<DockWidgetBase *>(droppedWindow)) {
345  if (!validateAffinity(dock))
346  return false;
347 
348  auto frame = Config::self().frameworkWidgetFactory()->createFrame();
349  frame->addWidget(dock);
350  addWidget(frame, location, relativeTo, DefaultSizeMode::FairButFloor);
351  } else if (auto floatingWindow = qobject_cast<FloatingWindow *>(droppedWindow)) {
352  if (!validateAffinity(floatingWindow))
353  return false;
354 
355  const bool hadSingleFloatingFrame = hasSingleFloatingFrame();
356  addMultiSplitter(floatingWindow->dropArea(), location, relativeTo,
357  DefaultSizeMode::FairButFloor);
358  if (hadSingleFloatingFrame != hasSingleFloatingFrame())
359  updateFloatingActions();
360 
361  floatingWindow->scheduleDeleteLater();
362  return true;
363  } else {
364  qWarning() << "Unknown dropped widget" << droppedWindow;
365  return false;
366  }
367 
368  return true;
369 }
370 
371 void DropArea::removeHover()
372 {
373  m_dropIndicatorOverlay->removeHover();
374 }
375 
376 template<typename T>
377 bool DropArea::validateAffinity(T *window, Frame *acceptingFrame) const
378 {
379  if (!DockRegistry::self()->affinitiesMatch(window->affinities(), affinities())) {
380  return false;
381  }
382 
383  if (acceptingFrame) {
384  // We're dropping into another frame (as tabbed), so also check the affinity of the frame
385  // not only of the main window, which might be more forgiving
386  if (!DockRegistry::self()->affinitiesMatch(window->affinities(), acceptingFrame->affinities())) {
387  return false;
388  }
389  }
390 
391  return true;
392 }
393 
394 bool DropArea::isMDIWrapper() const
395 {
396  return m_isMDIWrapper;
397 }
398 
399 DockWidgetBase *DropArea::mdiDockWidgetWrapper() const
400 {
401  if (m_isMDIWrapper)
402  return qobject_cast<DockWidgetBase *>(QWidgetAdapter::parent());
403 
404  return nullptr;
405 }
DockWidgetBase.h
The DockWidget base-class that's shared between QtWidgets and QtQuick stack.
QVector::isEmpty
bool isEmpty() const const
KDDockWidgets::DropLocation_Top
@ DropLocation_Top
Definition: KDDockWidgets.h:232
KDDockWidgets::InitialOption
Struct describing the preferred dock widget size and visibility when adding it to a layout.
Definition: KDDockWidgets.h:101
KDDockWidgets::DropLocation_Center
@ DropLocation_Center
Definition: KDDockWidgets.h:235
QWidget
KDDockWidgets::Location
Location
Definition: KDDockWidgets.h:44
QVector::first
T & first()
KDDockWidgets::DropLocation_Right
@ DropLocation_Right
Definition: KDDockWidgets.h:233
KDDockWidgets::DropLocation_Bottom
@ DropLocation_Bottom
Definition: KDDockWidgets.h:234
MainWindowBase.h
The MainWindow base-class that's shared between QtWidgets and QtQuick stack.
Qt::MouseFocusReason
MouseFocusReason
QString
Qt::FindDirectChildrenOnly
FindDirectChildrenOnly
KDDockWidgets::DropLocation_OutterTop
@ DropLocation_OutterTop
Definition: KDDockWidgets.h:237
KDDockWidgets::Config
Singleton to allow to choose certain behaviours of the framework.
Definition: Config.h:75
Config.h
Application-wide config to tune certain behaviours of the framework.
isOutterLocation
static bool isOutterLocation(DropLocation location)
Definition: DropArea.cpp:220
KDDockWidgets::DropLocation_Left
@ DropLocation_Left
Definition: KDDockWidgets.h:231
KDDockWidgets::InitialOption::startsHidden
bool startsHidden() const
Definition: KDDockWidgets.h:129
KDDockWidgets::DockWidgetBase
The DockWidget base-class. DockWidget and DockWidgetBase are only split in two so we can share some c...
Definition: DockWidgetBase.h:61
KDDockWidgets::DropLocation_OutterLeft
@ DropLocation_OutterLeft
Definition: KDDockWidgets.h:236
KDDockWidgets
Definition: Config.cpp:37
KDDockWidgets::DropLocation_OutterRight
@ DropLocation_OutterRight
Definition: KDDockWidgets.h:238
KDDockWidgets::Location_None
@ Location_None
Definition: KDDockWidgets.h:45
KDDockWidgets::InitialOption::visibility
InitialVisibilityOption visibility
Allows a dock widget to be docked as hidden.
Definition: KDDockWidgets.h:156
KDDockWidgets::DropLocation
DropLocation
Enum describing the different drop indicator types.
Definition: KDDockWidgets.h:229
QVector
KDDockWidgets::DropLocation_None
@ DropLocation_None
Definition: KDDockWidgets.h:230
QPoint
FrameworkWidgetFactory.h
A factory class for allowing the user to customize some internal widgets.
QStringList
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:28 for KDDockWidgets API Documentation by doxygen 1.8.20