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

© 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