floatingdragpreview.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2020 Uwe Kindler
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of Qt Creator.
  7. **
  8. ** Commercial License Usage
  9. ** Licensees holding valid commercial Qt licenses may use this file in
  10. ** accordance with the commercial license agreement provided with the
  11. ** Software or, alternatively, in accordance with the terms contained in
  12. ** a written agreement between you and The Qt Company. For licensing terms
  13. ** and conditions see https://www.qt.io/terms-conditions. For further
  14. ** information use the contact form at https://www.qt.io/contact-us.
  15. **
  16. ** GNU Lesser General Public License Usage
  17. ** Alternatively, this file may be used under the terms of the GNU Lesser
  18. ** General Public License version 2.1 or (at your option) any later version.
  19. ** The licenses are as published by the Free Software Foundation
  20. ** and appearing in the file LICENSE.LGPLv21 included in the packaging
  21. ** of this file. Please review the following information to ensure
  22. ** the GNU Lesser General Public License version 2.1 requirements
  23. ** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 3 or (at your option) any later version
  28. ** approved by the KDE Free Qt Foundation. The licenses are as published by
  29. ** the Free Software Foundation and appearing in the file LICENSE.GPL3
  30. ** included in the packaging of this file. Please review the following
  31. ** information to ensure the GNU General Public License requirements will
  32. ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
  33. **
  34. ****************************************************************************/
  35. #include "floatingdragpreview.h"
  36. #include "dockareawidget.h"
  37. #include "dockcontainerwidget.h"
  38. #include "dockmanager.h"
  39. #include "dockoverlay.h"
  40. #include "dockwidget.h"
  41. #include "utils/hostosinfo.h"
  42. #include <QApplication>
  43. #include <QEvent>
  44. #include <QKeyEvent>
  45. #include <QLoggingCategory>
  46. #include <QPainter>
  47. #include <iostream>
  48. static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
  49. namespace ADS
  50. {
  51. /**
  52. * Private data class (pimpl)
  53. */
  54. struct FloatingDragPreviewPrivate
  55. {
  56. FloatingDragPreview *q;
  57. QWidget *m_content;
  58. DockAreaWidget *m_contentSourceArea = nullptr;
  59. DockContainerWidget *m_contenSourceContainer = nullptr;
  60. QPoint m_dragStartMousePosition;
  61. DockManager *m_dockManager;
  62. DockContainerWidget *m_dropContainer = nullptr;
  63. qreal m_windowOpacity;
  64. bool m_hidden = false;
  65. QPixmap m_contentPreviewPixmap;
  66. /**
  67. * Private data constructor
  68. */
  69. FloatingDragPreviewPrivate(FloatingDragPreview *parent);
  70. void updateDropOverlays(const QPoint &globalPosition);
  71. void setHidden(bool value)
  72. {
  73. m_hidden = value;
  74. q->update();
  75. }
  76. /**
  77. * Cancel dragging and emit the draggingCanceled event
  78. */
  79. void cancelDragging()
  80. {
  81. emit q->draggingCanceled();
  82. m_dockManager->containerOverlay()->hideOverlay();
  83. m_dockManager->dockAreaOverlay()->hideOverlay();
  84. q->close();
  85. }
  86. };
  87. // struct FloatingDragPreviewPrivate
  88. void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition)
  89. {
  90. if (!q->isVisible() || !m_dockManager) {
  91. return;
  92. }
  93. auto containers = m_dockManager->dockContainers();
  94. DockContainerWidget *topContainer = nullptr;
  95. for (auto containerWidget : containers) {
  96. if (!containerWidget->isVisible()) {
  97. continue;
  98. }
  99. QPoint mappedPosition = containerWidget->mapFromGlobal(globalPosition);
  100. if (containerWidget->rect().contains(mappedPosition)) {
  101. if (!topContainer || containerWidget->isInFrontOf(topContainer)) {
  102. topContainer = containerWidget;
  103. }
  104. }
  105. }
  106. m_dropContainer = topContainer;
  107. auto containerOverlay = m_dockManager->containerOverlay();
  108. auto dockAreaOverlay = m_dockManager->dockAreaOverlay();
  109. auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor();
  110. auto containerDropArea = containerOverlay->dropAreaUnderCursor();
  111. if (!topContainer) {
  112. containerOverlay->hideOverlay();
  113. dockAreaOverlay->hideOverlay();
  114. if (DockManager::configFlags().testFlag(DockManager::DragPreviewIsDynamic)) {
  115. setHidden(false);
  116. }
  117. return;
  118. }
  119. int visibleDockAreas = topContainer->visibleDockAreaCount();
  120. containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas);
  121. DockWidgetArea containerArea = containerOverlay->showOverlay(topContainer);
  122. containerOverlay->enableDropPreview(containerArea != InvalidDockWidgetArea);
  123. auto dockArea = topContainer->dockAreaAt(globalPosition);
  124. if (dockArea && dockArea->isVisible() && visibleDockAreas > 0
  125. && dockArea != m_contentSourceArea) {
  126. dockAreaOverlay->enableDropPreview(true);
  127. dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea
  128. : dockArea->allowedAreas());
  129. DockWidgetArea area = dockAreaOverlay->showOverlay(dockArea);
  130. // A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in the
  131. // title bar. If the ContainerArea is valid then we ignore the dock area of the
  132. // dockAreaOverlay() and disable the drop preview
  133. if ((area == CenterDockWidgetArea) && (containerArea != InvalidDockWidgetArea)) {
  134. dockAreaOverlay->enableDropPreview(false);
  135. containerOverlay->enableDropPreview(true);
  136. } else {
  137. containerOverlay->enableDropPreview(InvalidDockWidgetArea == area);
  138. }
  139. } else {
  140. dockAreaOverlay->hideOverlay();
  141. if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea) {
  142. m_dropContainer = nullptr;
  143. }
  144. }
  145. if (DockManager::configFlags().testFlag(DockManager::DragPreviewIsDynamic)) {
  146. setHidden(dockDropArea != InvalidDockWidgetArea
  147. || containerDropArea != InvalidDockWidgetArea);
  148. }
  149. }
  150. FloatingDragPreviewPrivate::FloatingDragPreviewPrivate(FloatingDragPreview *parent)
  151. : q(parent)
  152. {}
  153. FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent)
  154. : QWidget(parent)
  155. , d(new FloatingDragPreviewPrivate(this))
  156. {
  157. d->m_content = content;
  158. setAttribute(Qt::WA_DeleteOnClose);
  159. if (DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) {
  160. setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
  161. } else {
  162. setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
  163. setAttribute(Qt::WA_NoSystemBackground);
  164. setAttribute(Qt::WA_TranslucentBackground);
  165. }
  166. if (Utils::HostOsInfo::isLinuxHost()) {
  167. auto flags = windowFlags();
  168. flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint;
  169. setWindowFlags(flags);
  170. }
  171. setWindowOpacity(0.6);
  172. // Create a static image of the widget that should get undocked
  173. // This is like some kind preview image like it is uses in drag and drop operations
  174. if (DockManager::configFlags().testFlag(DockManager::DragPreviewShowsContentPixmap)) {
  175. d->m_contentPreviewPixmap = QPixmap(content->size());
  176. content->render(&d->m_contentPreviewPixmap);
  177. }
  178. connect(qApp,
  179. &QApplication::applicationStateChanged,
  180. this,
  181. &FloatingDragPreview::onApplicationStateChanged); // TODO
  182. }
  183. FloatingDragPreview::FloatingDragPreview(DockWidget *content)
  184. : FloatingDragPreview(static_cast<QWidget *>(content),
  185. content->dockManager()) // TODO static_cast?
  186. {
  187. d->m_dockManager = content->dockManager();
  188. if (content->dockAreaWidget()->openDockWidgetsCount() == 1) {
  189. d->m_contentSourceArea = content->dockAreaWidget();
  190. d->m_contenSourceContainer = content->dockContainer();
  191. }
  192. setWindowTitle(content->windowTitle());
  193. // We need to install an event filter for the given content
  194. // widget to receive the escape key press
  195. content->dockAreaWidget()->installEventFilter(this);
  196. }
  197. FloatingDragPreview::FloatingDragPreview(DockAreaWidget *content)
  198. : FloatingDragPreview(static_cast<QWidget *>(content),
  199. content->dockManager()) // TODO static_cast?
  200. {
  201. d->m_dockManager = content->dockManager();
  202. d->m_contentSourceArea = content;
  203. d->m_contenSourceContainer = content->dockContainer();
  204. setWindowTitle(content->currentDockWidget()->windowTitle());
  205. // We need to install an event filter for the given Content
  206. // widget to receive the escape key press
  207. content->installEventFilter(this);
  208. }
  209. FloatingDragPreview::~FloatingDragPreview() { delete d; }
  210. void FloatingDragPreview::moveFloating()
  211. {
  212. int borderSize = (frameSize().width() - size().width()) / 2;
  213. const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition
  214. - QPoint(borderSize, 0);
  215. move(moveToPos);
  216. }
  217. void FloatingDragPreview::startFloating(const QPoint &dragStartMousePos,
  218. const QSize &size,
  219. eDragState dragState,
  220. QWidget *mouseEventHandler)
  221. {
  222. Q_UNUSED(mouseEventHandler)
  223. Q_UNUSED(dragState)
  224. resize(size);
  225. d->m_dragStartMousePosition = dragStartMousePos;
  226. moveFloating();
  227. show();
  228. }
  229. void FloatingDragPreview::moveEvent(QMoveEvent *event)
  230. {
  231. QWidget::moveEvent(event);
  232. d->updateDropOverlays(QCursor::pos());
  233. }
  234. void FloatingDragPreview::finishDragging()
  235. {
  236. qCInfo(adsLog) << Q_FUNC_INFO;
  237. auto dockDropArea = d->m_dockManager->dockAreaOverlay()->dropAreaUnderCursor();
  238. auto containerDropArea = d->m_dockManager->containerOverlay()->dropAreaUnderCursor();
  239. bool dropPossible = (dockDropArea != InvalidDockWidgetArea)
  240. || (containerDropArea != InvalidDockWidgetArea);
  241. if (d->m_dropContainer && dropPossible) {
  242. d->m_dropContainer->dropWidget(d->m_content, QCursor::pos());
  243. } else {
  244. DockWidget *dockWidget = qobject_cast<DockWidget *>(d->m_content);
  245. FloatingDockContainer *floatingWidget = nullptr;
  246. if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) {
  247. floatingWidget = new FloatingDockContainer(dockWidget);
  248. } else {
  249. DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(d->m_content);
  250. if (dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) {
  251. floatingWidget = new FloatingDockContainer(dockArea);
  252. }
  253. }
  254. if (floatingWidget) {
  255. floatingWidget->setGeometry(this->geometry());
  256. floatingWidget->show();
  257. if (!DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) {
  258. QApplication::processEvents();
  259. int frameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height();
  260. QRect fixedGeometry = this->geometry();
  261. fixedGeometry.adjust(0, frameHeight, 0, 0);
  262. floatingWidget->setGeometry(fixedGeometry);
  263. }
  264. }
  265. }
  266. this->close();
  267. d->m_dockManager->containerOverlay()->hideOverlay();
  268. d->m_dockManager->dockAreaOverlay()->hideOverlay();
  269. }
  270. void FloatingDragPreview::paintEvent(QPaintEvent *event)
  271. {
  272. Q_UNUSED(event)
  273. if (d->m_hidden) {
  274. return;
  275. }
  276. QPainter painter(this);
  277. if (DockManager::configFlags().testFlag(DockManager::DragPreviewShowsContentPixmap)) {
  278. painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap);
  279. }
  280. // If we do not have a window frame then we paint a QRubberBand like frameless window
  281. if (!DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) {
  282. QColor color = palette().color(QPalette::Active, QPalette::Highlight);
  283. QPen pen = painter.pen();
  284. pen.setColor(color.darker(120));
  285. pen.setStyle(Qt::SolidLine);
  286. pen.setWidth(1);
  287. pen.setCosmetic(true);
  288. painter.setPen(pen);
  289. color = color.lighter(130);
  290. color.setAlpha(64);
  291. painter.setBrush(color);
  292. painter.drawRect(rect().adjusted(0, 0, -1, -1));
  293. }
  294. }
  295. void FloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state)
  296. {
  297. if (state != Qt::ApplicationActive) {
  298. disconnect(qApp,
  299. &QApplication::applicationStateChanged,
  300. this,
  301. &FloatingDragPreview::onApplicationStateChanged);
  302. d->cancelDragging();
  303. }
  304. }
  305. bool FloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
  306. {
  307. if (event->type() == QEvent::KeyPress) {
  308. QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
  309. if (keyEvent->key() == Qt::Key_Escape) {
  310. watched->removeEventFilter(this);
  311. d->cancelDragging();
  312. }
  313. }
  314. return false;
  315. }
  316. } // namespace ADS