QtCanpool 3.3.0
载入中...
搜索中...
未找到
dockfloatingpreview.cpp
浏览该文件的文档.
1
7#include "dockpanel.h"
8#include "dockwindow.h"
9#include "dockwidget.h"
10#include "dockmanager.h"
11#include "dockoverlay.h"
12#include "dockcontainer.h"
15#include "dockutils.h"
16
17#include <QApplication>
18#include <QKeyEvent>
19#include <QPainter>
20
22
23class DockFloatingPreviewPrivate
24{
25public:
26 QX_DECLARE_PUBLIC(DockFloatingPreview)
27public:
28 DockFloatingPreviewPrivate();
29
30 void updateDropOverlays(const QPoint &globalPos);
31 void setHidden(bool Value);
32
33 void cancelDragging();
34 void createFloatingWidget();
35 bool isContentFloatable() const;
36 bool isContentPinnable() const;
37
38 DockWidget::DockWidgetFeatures contentFeatures() const;
39public:
40 QWidget *m_content = nullptr;
41 DockPanel *m_contentSourcePanel = nullptr;
42 QPoint m_dragStartMousePosition;
43 DockWindow *m_window = nullptr;
44 DockContainer *m_dropContainer = nullptr;
45 DockWidget::DockWidgetFeatures m_contentFeatures;
46 QPixmap m_contentPreviewPixmap;
47 bool m_canceled = false;
48 bool m_hidden = false;
49};
50
51DockFloatingPreviewPrivate::DockFloatingPreviewPrivate()
52{
53}
54
55void DockFloatingPreviewPrivate::updateDropOverlays(const QPoint &globalPos)
56{
57 Q_Q(DockFloatingPreview);
58 if (!q->isVisible() || !m_window) {
59 return;
60 }
61
62 auto containers = m_window->dockContainers();
63 DockContainer *topContainer = nullptr;
64 for (auto container : containers) {
65 if (!container->isVisible()) {
66 continue;
67 }
68
69 QPoint mappedPos = container->mapFromGlobal(globalPos);
70 if (container->rect().contains(mappedPos)) {
71 if (!topContainer || container->isInFrontOf(topContainer)) {
72 topContainer = container;
73 }
74 }
75 }
76
77 m_dropContainer = topContainer;
78 auto containerOverlay = m_window->containerOverlay();
79 auto panelOverlay = m_window->panelOverlay();
80
81 if (!topContainer) {
82 containerOverlay->hideOverlay();
83 panelOverlay->hideOverlay();
84 if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic)) {
85 setHidden(false);
86 }
87 return;
88 }
89
90 auto panelDropArea = panelOverlay->dropAreaUnderCursor();
91 auto containerDropArea = containerOverlay->dropAreaUnderCursor();
92
93 int visibleDockpanels = topContainer->visibleDockPanelCount();
94
95 // Include the overlay widget we're dragging as a visible widget
96 auto panel = qobject_cast<DockPanel *>(m_content);
97 if (panel && panel->isAutoHide()) {
98 visibleDockpanels++;
99 }
100
101 Qx::DockWidgetAreas allowedContainerAreas = (visibleDockpanels > 1) ? Qx::OuterDockAreas : Qx::AllDockAreas;
102 auto topPanel = topContainer->dockPanelAt(globalPos);
103 // If the dock container contains only one single panel, then we need
104 // to respect the allowed areas - only the center area is relevant here because
105 // all other allowed areas are from the container
106 if (visibleDockpanels == 1 && topPanel) {
107 allowedContainerAreas.setFlag(Qx::CenterDockWidgetArea,
108 topPanel->allowedAreas().testFlag(Qx::CenterDockWidgetArea));
109 }
110
111 if (isContentPinnable()) {
112 allowedContainerAreas |= Qx::AutoHideDockAreas;
113 }
114
115 containerOverlay->setAllowedAreas(allowedContainerAreas);
116 containerOverlay->enableDropPreview(containerDropArea != Qx::InvalidDockWidgetArea);
117 if (topPanel && topPanel->isVisible() && visibleDockpanels >= 0 && topPanel != m_contentSourcePanel) {
118 panelOverlay->enableDropPreview(true);
119 panelOverlay->setAllowedAreas((visibleDockpanels == 1) ? Qx::NoDockWidgetArea : topPanel->allowedAreas());
120 Qx::DockWidgetArea area = panelOverlay->showOverlay(topPanel);
121
122 // A CenterDockWidgetArea for the panelOverlay() indicates that
123 // the mouse is in the title bar. If the ContainerArea is valid
124 // then we ignore the dock area of the panelOverlay() and disable
125 // the drop preview
126 if ((area == Qx::CenterDockWidgetArea) && (containerDropArea != Qx::InvalidDockWidgetArea)) {
127 panelOverlay->enableDropPreview(false);
128 containerOverlay->enableDropPreview(true);
129 } else {
130 containerOverlay->enableDropPreview(Qx::InvalidDockWidgetArea == area);
131 }
132 containerOverlay->showOverlay(topContainer);
133 } else {
134 panelOverlay->hideOverlay();
135 // If there is only one single visible dock area in a container, then
136 // it does not make sense to show a dock overlay because the dock area
137 // would be removed and inserted at the same position. Only auto hide
138 // area is allowed
139 if (visibleDockpanels == 1) {
140 containerOverlay->setAllowedAreas(Qx::AutoHideDockAreas);
141 }
142 containerOverlay->showOverlay(topContainer);
143
144 if (topPanel == m_contentSourcePanel && Qx::InvalidDockWidgetArea == containerDropArea) {
145 m_dropContainer = nullptr;
146 }
147 }
148
149 if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic)) {
150 setHidden(panelDropArea != Qx::InvalidDockWidgetArea || containerDropArea != Qx::InvalidDockWidgetArea);
151 }
152}
153
154void DockFloatingPreviewPrivate::setHidden(bool Value)
155{
156 Q_Q(DockFloatingPreview);
157 m_hidden = Value;
158 q->update();
159}
160
161void DockFloatingPreviewPrivate::cancelDragging()
162{
163 Q_Q(DockFloatingPreview);
164 m_canceled = true;
165 Q_EMIT q->draggingCanceled();
166 m_window->containerOverlay()->hideOverlay();
167 m_window->panelOverlay()->hideOverlay();
168 q->close();
169}
170
171void DockFloatingPreviewPrivate::createFloatingWidget()
172{
173 Q_Q(DockFloatingPreview);
174 DockWidget *widget = qobject_cast<DockWidget *>(m_content);
175 DockPanel *panel = qobject_cast<DockPanel *>(m_content);
176
177 DockFloatingContainer *floatingWidget = nullptr;
178
179 if (widget && widget->features().testFlag(DockWidget::DockWidgetFloatable)) {
180 floatingWidget = new DockFloatingContainer(widget);
181 } else if (panel && panel->features().testFlag(DockWidget::DockWidgetFloatable)) {
182 floatingWidget = new DockFloatingContainer(panel);
183 }
184
185 if (floatingWidget) {
186 floatingWidget->setGeometry(q->geometry());
187 floatingWidget->show();
188 if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
189 QApplication::processEvents();
190 int FrameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height();
191 QRect FixedGeometry = q->geometry();
192 FixedGeometry.adjust(0, FrameHeight, 0, 0);
193 floatingWidget->setGeometry(FixedGeometry);
194 }
195 }
196}
197
198bool DockFloatingPreviewPrivate::isContentFloatable() const
199{
200 return m_contentFeatures.testFlag(DockWidget::DockWidgetFloatable);
201}
202
203bool DockFloatingPreviewPrivate::isContentPinnable() const
204{
205 return m_contentFeatures.testFlag(DockWidget::DockWidgetPinnable);
206}
207
208DockWidget::DockWidgetFeatures DockFloatingPreviewPrivate::contentFeatures() const
209{
210 DockWidget *widget = qobject_cast<DockWidget *>(m_content);
211 if (widget) {
212 return widget->features();
213 }
214 DockPanel *panel = qobject_cast<DockPanel *>(m_content);
215 if (panel) {
216 return panel->features();
217 }
218
219 return DockWidget::DockWidgetFeatures();
220}
221
222DockFloatingPreview::DockFloatingPreview(DockPanel *content)
223 : DockFloatingPreview(content, content->dockWindow())
224{
226 d->m_window = content->dockWindow();
227 d->m_contentSourcePanel = content;
228 setWindowTitle(content->currentDockWidget()->windowTitle());
229}
230
232 : DockFloatingPreview(content, content->dockWindow())
233{
235 d->m_window = content->dockWindow();
236 if (content->dockPanel()->openDockWidgetsCount() == 1) {
237 d->m_contentSourcePanel = content->dockPanel();
238 }
239 setWindowTitle(content->windowTitle());
240}
241
245DockFloatingPreview::DockFloatingPreview(QWidget *content, QWidget *parent)
246 : QWidget(parent)
247{
249
251 d->m_content = content;
252 d->m_contentFeatures = d->contentFeatures();
253
254 setAttribute(Qt::WA_DeleteOnClose);
256 setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
257 } else {
258 setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
259 setAttribute(Qt::WA_NoSystemBackground);
260 setAttribute(Qt::WA_TranslucentBackground);
261 }
262
263#if QX_DOCK_X11
264 auto flags = windowFlags();
265 flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint;
266 setWindowFlags(flags);
267#endif
268
269 // Create a static image of the widget that should get undocked
270 // This is like some kind preview image like it is uses in drag and drop
271 // operations
273 d->m_contentPreviewPixmap = QPixmap(content->size());
274 content->render(&d->m_contentPreviewPixmap);
275 }
276
277 connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
278 SLOT(onApplicationStateChanged(Qt::ApplicationState)));
279
280 // The only safe way to receive escape key presses is to install an event
281 // filter for the application object
282 qApp->installEventFilter(this);
283}
284
289
290void DockFloatingPreview::startFloating(const QPoint &dragStartMousePos, const QSize &size, Qx::DockDragState dragState,
291 QWidget *mouseEventHandler)
292{
293 Q_UNUSED(mouseEventHandler)
294 Q_UNUSED(dragState)
296 resize(size);
297 d->m_dragStartMousePosition = dragStartMousePos;
298 moveFloating();
299 show();
300}
301
303{
305 auto panelDropArea = d->m_window->panelOverlay()->visibleDropAreaUnderCursor();
306 auto containerDropArea = d->m_window->containerOverlay()->visibleDropAreaUnderCursor();
307
308 bool validDropArea =
309 (panelDropArea != Qx::InvalidDockWidgetArea) || (containerDropArea != Qx::InvalidDockWidgetArea);
310
311 // Non floatable auto hide widgets should stay in its current auto hide
312 // state if they are dragged into a floating window
313 if (validDropArea || d->isContentFloatable()) {
314 cleanupAutoHideContainerWidget(containerDropArea);
315 }
316
317 if (!d->m_dropContainer) {
318 d->createFloatingWidget();
319 } else if (panelDropArea != Qx::InvalidDockWidgetArea) {
320 d->m_dropContainer->dropWidget(d->m_content, panelDropArea, d->m_dropContainer->dockPanelAt(QCursor::pos()),
321 d->m_window->panelOverlay()->tabIndexUnderCursor());
322 } else if (containerDropArea != Qx::InvalidDockWidgetArea) {
323 DockPanel *dropPanel = nullptr;
324 // If there is only one single dock area, and we drop into the center
325 // then we tabify the dropped widget into the only visible dock area
326 if (d->m_dropContainer->visibleDockPanelCount() <= 1 && Qx::CenterDockWidgetArea == containerDropArea) {
327 dropPanel = d->m_dropContainer->dockPanelAt(QCursor::pos());
328 }
329
330 d->m_dropContainer->dropWidget(d->m_content, containerDropArea, dropPanel,
331 d->m_window->containerOverlay()->tabIndexUnderCursor());
332 } else {
333 d->createFloatingWidget();
334 }
335
336 this->close();
337 d->m_window->containerOverlay()->hideOverlay();
338 d->m_window->panelOverlay()->hideOverlay();
339}
340
342{
344 int borderSize = (frameSize().width() - size().width()) / 2;
345 const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition - QPoint(borderSize, 0);
346 move(moveToPos);
347 d->updateDropOverlays(QCursor::pos());
348}
349
351{
353 auto droppedDockWidget = qobject_cast<DockWidget *>(d->m_content);
354 auto droppedPanel = qobject_cast<DockPanel *>(d->m_content);
355 auto autoHideContainer =
356 droppedDockWidget ? droppedDockWidget->autoHideContainer() : droppedPanel->autoHideContainer();
357
358 if (!autoHideContainer) {
359 return;
360 }
361
362 // If the dropped widget is already an auto hide widget and if it is moved
363 // to a new side bar location in the same container, then we do not need
364 // to cleanup
365 if (internal::isSideBarArea(containerDropArea) && (d->m_dropContainer == autoHideContainer->dockContainer())) {
366 return;
367 }
368
369 autoHideContainer->cleanupAndDelete();
370}
371
376{
378 if (state != Qt::ApplicationActive) {
379 disconnect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this,
380 SLOT(onApplicationStateChanged(Qt::ApplicationState)));
381 d->cancelDragging();
382 }
383}
384
385bool DockFloatingPreview::eventFilter(QObject *watched, QEvent *event)
386{
388 Q_UNUSED(watched);
389 if (!d->m_canceled && event->type() == QEvent::KeyPress) {
390 QKeyEvent *e = static_cast<QKeyEvent *>(event);
391 if (e->key() == Qt::Key_Escape) {
392 watched->removeEventFilter(this);
393 d->cancelDragging();
394 }
395 }
396
397 return false;
398}
399
401{
402 Q_UNUSED(e);
404 if (d->m_hidden) {
405 return;
406 }
407
408 QPainter painter(this);
409 painter.setOpacity(0.6);
411 painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap);
412 }
413
414 // If we do not have a window frame then we paint a QRubberBand like
415 // frameless window
417 QColor color = palette().color(QPalette::Active, QPalette::Highlight);
418 QPen pen = painter.pen();
419 pen.setColor(color.darker(120));
420 pen.setStyle(Qt::SolidLine);
421 pen.setWidth(1);
422 pen.setCosmetic(true);
423 painter.setPen(pen);
424 color = color.lighter(130);
425 color.setAlpha(64);
426 painter.setBrush(color);
427 painter.drawRect(rect().adjusted(0, 0, -1, -1));
428 }
429}
430
A floating overlay is a temporary floating widget that is just used to indicate the floating widget m...
virtual bool eventFilter(QObject *watched, QEvent *event) override
virtual void finishDragging() override
Tells the widget that to finish dragging if the mouse is released.
void moveFloating() override
Moves the widget to a new position relative to the position given when startFloating() was called.
virtual void paintEvent(QPaintEvent *e) override
void onApplicationStateChanged(Qt::ApplicationState state)
Cancel non opaque undocking if application becomes inactive
virtual void startFloating(const QPoint &dragStartMousePos, const QSize &size, Qx::DockDragState dragState, QWidget *mouseEventHandler) override
Starts floating.
void cleanupAutoHideContainerWidget(Qx::DockWidgetArea containerDropArea)
DockFloatingPreview(DockPanel *content)
static bool testConfigFlag(ConfigFlag flag)
DockPanel manages multiple instances of DockWidgets.
Definition dockpanel.h:31
DockWidget * currentDockWidget() const
DockWindow * dockWindow() const
int openDockWidgetsCount() const
The DockWidget class provides a widget that can be docked inside a DockWindow or floated as a top-lev...
Definition dockwidget.h:31
DockPanel * dockPanel() const
Returns the dock panel this dock widget belongs to or 0 if this dock widget has not been docked yet
DockWindow * dockWindow() const
Returns the dock window that manages the dock widget or 0 if the widget has not been assigned to any ...
bool isSideBarArea(Qx::DockWidgetArea area)
DockWidgetArea
@ OuterDockAreas
@ NoDockWidgetArea
@ AllDockAreas
@ InvalidDockWidgetArea
@ CenterDockWidgetArea
@ AutoHideDockAreas
DockDragState
#define QX_INIT_PRIVATE(Class)
#define QX_DECLARE_PUBLIC(Class)
#define QX_FINI_PRIVATE()
#define QX_DOCK_END_NAMESPACE
#define QX_DOCK_BEGIN_NAMESPACE