2017-04-12 05:26:33 +08:00
|
|
|
/*******************************************************************************
|
2017-06-10 04:04:02 +08:00
|
|
|
** Qt Advanced Docking System
|
2017-04-12 05:26:33 +08:00
|
|
|
** Copyright (C) 2017 Uwe Kindler
|
2017-06-10 04:04:02 +08:00
|
|
|
**
|
|
|
|
** This library is free software; you can redistribute it and/or
|
|
|
|
** modify it under the terms of the GNU Lesser General Public
|
|
|
|
** License as published by the Free Software Foundation; either
|
|
|
|
** version 2.1 of the License, or (at your option) any later version.
|
|
|
|
**
|
|
|
|
** This library is distributed in the hope that it will be useful,
|
2017-04-12 05:26:33 +08:00
|
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2017-06-10 04:04:02 +08:00
|
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
** Lesser General Public License for more details.
|
|
|
|
**
|
|
|
|
** You should have received a copy of the GNU Lesser General Public
|
|
|
|
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
|
2017-04-12 05:26:33 +08:00
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// INCLUDES
|
|
|
|
//============================================================================
|
|
|
|
#include "DockOverlay.h"
|
|
|
|
|
|
|
|
#include <QPointer>
|
|
|
|
#include <QPaintEvent>
|
|
|
|
#include <QResizeEvent>
|
|
|
|
#include <QMoveEvent>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QGridLayout>
|
|
|
|
#include <QCursor>
|
|
|
|
#include <QIcon>
|
|
|
|
#include <QLabel>
|
|
|
|
#include <QtGlobal>
|
|
|
|
#include <QDebug>
|
2018-08-28 19:25:44 +08:00
|
|
|
#include <QMap>
|
2018-11-05 21:12:34 +08:00
|
|
|
#include <QWindow>
|
2017-04-12 05:26:33 +08:00
|
|
|
|
|
|
|
#include "DockAreaWidget.h"
|
2020-04-27 21:27:34 +08:00
|
|
|
#include "DockAreaTitleBar.h"
|
2017-04-12 05:26:33 +08:00
|
|
|
|
2018-11-05 21:12:34 +08:00
|
|
|
#include <iostream>
|
2018-09-26 15:57:36 +08:00
|
|
|
|
2018-08-28 19:25:44 +08:00
|
|
|
namespace ads
|
2017-04-12 05:26:33 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private data class of CDockOverlay
|
|
|
|
*/
|
|
|
|
struct DockOverlayPrivate
|
|
|
|
{
|
|
|
|
CDockOverlay* _this;
|
|
|
|
DockWidgetAreas AllowedAreas = InvalidDockWidgetArea;
|
|
|
|
CDockOverlayCross* Cross;
|
|
|
|
QPointer<QWidget> TargetWidget;
|
|
|
|
DockWidgetArea LastLocation = InvalidDockWidgetArea;
|
|
|
|
bool DropPreviewEnabled = true;
|
|
|
|
CDockOverlay::eMode Mode = CDockOverlay::ModeDockAreaOverlay;
|
|
|
|
QRect DropAreaRect;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private data constructor
|
|
|
|
*/
|
|
|
|
DockOverlayPrivate(CDockOverlay* _public) : _this(_public) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private data of CDockOverlayCross class
|
|
|
|
*/
|
|
|
|
struct DockOverlayCrossPrivate
|
|
|
|
{
|
|
|
|
CDockOverlayCross* _this;
|
|
|
|
CDockOverlay::eMode Mode = CDockOverlay::ModeDockAreaOverlay;
|
|
|
|
CDockOverlay* DockOverlay;
|
|
|
|
QHash<DockWidgetArea, QWidget*> DropIndicatorWidgets;
|
|
|
|
QGridLayout* GridLayout;
|
2018-08-28 19:25:44 +08:00
|
|
|
QColor IconColors[5];
|
|
|
|
bool UpdateRequired = false;
|
2018-11-05 21:12:34 +08:00
|
|
|
double LastDevicePixelRatio = 0.1;
|
2017-04-12 05:26:33 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Private data constructor
|
|
|
|
*/
|
|
|
|
DockOverlayCrossPrivate(CDockOverlayCross* _public) : _this(_public) {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param area
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
QPoint areaGridPosition(const DockWidgetArea area);
|
2018-08-28 19:25:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Palette based default icon colors
|
|
|
|
*/
|
|
|
|
QColor defaultIconColor(CDockOverlayCross::eIconColor ColorIndex)
|
|
|
|
{
|
|
|
|
QPalette pal = _this->palette();
|
|
|
|
switch (ColorIndex)
|
|
|
|
{
|
|
|
|
case CDockOverlayCross::FrameColor: return pal.color(QPalette::Active, QPalette::Highlight);
|
|
|
|
case CDockOverlayCross::WindowBackgroundColor: return pal.color(QPalette::Active, QPalette::Base);
|
|
|
|
case CDockOverlayCross::OverlayColor:
|
|
|
|
{
|
|
|
|
QColor Color = pal.color(QPalette::Active, QPalette::Highlight);
|
|
|
|
Color.setAlpha(64);
|
|
|
|
return Color;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CDockOverlayCross::ArrowColor: return pal.color(QPalette::Active, QPalette::Base);
|
|
|
|
case CDockOverlayCross::ShadowColor: return QColor(0, 0, 0, 64);
|
|
|
|
default:
|
|
|
|
return QColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
return QColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stylehseet based icon colors
|
|
|
|
*/
|
|
|
|
QColor iconColor(CDockOverlayCross::eIconColor ColorIndex)
|
|
|
|
{
|
|
|
|
QColor Color = IconColors[ColorIndex];
|
|
|
|
if (!Color.isValid())
|
|
|
|
{
|
|
|
|
Color = defaultIconColor(ColorIndex);
|
|
|
|
IconColors[ColorIndex] = Color;
|
|
|
|
}
|
|
|
|
return Color;
|
|
|
|
}
|
|
|
|
|
2019-05-14 21:32:50 +08:00
|
|
|
//============================================================================
|
|
|
|
/**
|
|
|
|
* Helper function that returns the drop indicator width depending on the
|
|
|
|
* operating system
|
|
|
|
*/
|
2019-07-12 16:37:14 +08:00
|
|
|
qreal dropIndicatiorWidth(QLabel* l) const
|
2019-05-14 21:32:50 +08:00
|
|
|
{
|
|
|
|
#ifdef Q_OS_LINUX
|
2019-07-12 16:39:59 +08:00
|
|
|
Q_UNUSED(l)
|
2019-05-14 21:32:50 +08:00
|
|
|
return 40;
|
|
|
|
#else
|
|
|
|
return static_cast<qreal>(l->fontMetrics().height()) * 3.f;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-08-28 19:25:44 +08:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
QWidget* createDropIndicatorWidget(DockWidgetArea DockWidgetArea,
|
|
|
|
CDockOverlay::eMode Mode)
|
|
|
|
{
|
|
|
|
QLabel* l = new QLabel();
|
|
|
|
l->setObjectName("DockWidgetAreaLabel");
|
|
|
|
|
2019-07-12 16:37:14 +08:00
|
|
|
const qreal metric = dropIndicatiorWidth(l);
|
2018-08-28 19:25:44 +08:00
|
|
|
const QSizeF size(metric, metric);
|
|
|
|
|
2018-11-05 21:12:34 +08:00
|
|
|
l->setPixmap(createHighDpiDropIndicatorPixmap(size, DockWidgetArea, Mode));
|
2018-08-28 19:25:44 +08:00
|
|
|
l->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
|
|
|
l->setAttribute(Qt::WA_TranslucentBackground);
|
2018-11-05 21:12:34 +08:00
|
|
|
l->setProperty("dockWidgetArea", DockWidgetArea);
|
2018-08-28 19:25:44 +08:00
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2018-11-05 21:12:34 +08:00
|
|
|
//============================================================================
|
|
|
|
void updateDropIndicatorIcon(QWidget* DropIndicatorWidget)
|
|
|
|
{
|
|
|
|
QLabel* l = qobject_cast<QLabel*>(DropIndicatorWidget);
|
2019-07-12 16:37:14 +08:00
|
|
|
const qreal metric = dropIndicatiorWidth(l);
|
2018-11-05 21:12:34 +08:00
|
|
|
const QSizeF size(metric, metric);
|
|
|
|
|
|
|
|
int Area = l->property("dockWidgetArea").toInt();
|
|
|
|
l->setPixmap(createHighDpiDropIndicatorPixmap(size, (DockWidgetArea)Area, Mode));
|
|
|
|
}
|
2018-08-28 19:25:44 +08:00
|
|
|
|
|
|
|
//============================================================================
|
2018-11-05 21:12:34 +08:00
|
|
|
QPixmap createHighDpiDropIndicatorPixmap(const QSizeF& size, DockWidgetArea DockWidgetArea,
|
2018-08-28 19:25:44 +08:00
|
|
|
CDockOverlay::eMode Mode)
|
|
|
|
{
|
|
|
|
QColor borderColor = iconColor(CDockOverlayCross::FrameColor);
|
|
|
|
QColor backgroundColor = iconColor(CDockOverlayCross::WindowBackgroundColor);
|
|
|
|
|
2019-01-14 15:59:37 +08:00
|
|
|
#if QT_VERSION >= 0x050600
|
2018-11-05 21:12:34 +08:00
|
|
|
double DevicePixelRatio = _this->window()->devicePixelRatioF();
|
2019-01-14 15:59:37 +08:00
|
|
|
#else
|
|
|
|
double DevicePixelRatio = _this->window()->devicePixelRatio();
|
|
|
|
#endif
|
2018-11-05 21:12:34 +08:00
|
|
|
QSizeF PixmapSize = size * DevicePixelRatio;
|
|
|
|
QPixmap pm(PixmapSize.toSize());
|
2018-08-28 19:25:44 +08:00
|
|
|
pm.fill(QColor(0, 0, 0, 0));
|
|
|
|
|
|
|
|
QPainter p(&pm);
|
|
|
|
QPen pen = p.pen();
|
|
|
|
QRectF ShadowRect(pm.rect());
|
|
|
|
QRectF baseRect;
|
|
|
|
baseRect.setSize(ShadowRect.size() * 0.7);
|
|
|
|
baseRect.moveCenter(ShadowRect.center());
|
|
|
|
|
|
|
|
// Fill
|
|
|
|
QColor ShadowColor = iconColor(CDockOverlayCross::ShadowColor);
|
|
|
|
if (ShadowColor.alpha() == 255)
|
|
|
|
{
|
|
|
|
ShadowColor.setAlpha(64);
|
|
|
|
}
|
|
|
|
p.fillRect(ShadowRect, ShadowColor);
|
|
|
|
|
|
|
|
// Drop area rect.
|
|
|
|
p.save();
|
|
|
|
QRectF areaRect;
|
|
|
|
QLineF areaLine;
|
|
|
|
QRectF nonAreaRect;
|
|
|
|
switch (DockWidgetArea)
|
|
|
|
{
|
|
|
|
case TopDockWidgetArea:
|
|
|
|
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * .5f);
|
|
|
|
nonAreaRect = QRectF(baseRect.x(), ShadowRect.height() * .5f, baseRect.width(), baseRect.height() * .5f);
|
|
|
|
areaLine = QLineF(areaRect.bottomLeft(), areaRect.bottomRight());
|
|
|
|
break;
|
|
|
|
case RightDockWidgetArea:
|
|
|
|
areaRect = QRectF(ShadowRect.width() * .5f, baseRect.y(), baseRect.width() * .5f, baseRect.height());
|
|
|
|
nonAreaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * .5f, baseRect.height());
|
|
|
|
areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft());
|
|
|
|
break;
|
|
|
|
case BottomDockWidgetArea:
|
|
|
|
areaRect = QRectF(baseRect.x(), ShadowRect.height() * .5f, baseRect.width(), baseRect.height() * .5f);
|
|
|
|
nonAreaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * .5f);
|
|
|
|
areaLine = QLineF(areaRect.topLeft(), areaRect.topRight());
|
|
|
|
break;
|
|
|
|
case LeftDockWidgetArea:
|
|
|
|
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * .5f, baseRect.height());
|
|
|
|
nonAreaRect = QRectF(ShadowRect.width() * .5f, baseRect.y(), baseRect.width() * .5f, baseRect.height());
|
|
|
|
areaLine = QLineF(areaRect.topRight(), areaRect.bottomRight());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSizeF baseSize = baseRect.size();
|
|
|
|
if (CDockOverlay::ModeContainerOverlay == Mode && DockWidgetArea != CenterDockWidgetArea)
|
|
|
|
{
|
|
|
|
baseRect = areaRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
p.fillRect(baseRect, backgroundColor);
|
|
|
|
if (areaRect.isValid())
|
|
|
|
{
|
|
|
|
pen = p.pen();
|
|
|
|
pen.setColor(borderColor);
|
|
|
|
QColor Color = iconColor(CDockOverlayCross::OverlayColor);
|
|
|
|
if (Color.alpha() == 255)
|
|
|
|
{
|
|
|
|
Color.setAlpha(64);
|
|
|
|
}
|
|
|
|
p.setBrush(Color);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
p.drawRect(areaRect);
|
|
|
|
|
|
|
|
pen = p.pen();
|
2018-11-05 21:12:34 +08:00
|
|
|
pen.setWidth(1);
|
2018-08-28 19:25:44 +08:00
|
|
|
pen.setColor(borderColor);
|
|
|
|
pen.setStyle(Qt::DashLine);
|
|
|
|
p.setPen(pen);
|
|
|
|
p.drawLine(areaLine);
|
|
|
|
}
|
|
|
|
p.restore();
|
|
|
|
|
|
|
|
p.save();
|
|
|
|
// Draw outer border
|
|
|
|
pen = p.pen();
|
|
|
|
pen.setColor(borderColor);
|
|
|
|
pen.setWidth(1);
|
|
|
|
p.setBrush(Qt::NoBrush);
|
|
|
|
p.setPen(pen);
|
|
|
|
p.drawRect(baseRect);
|
|
|
|
|
|
|
|
// draw window title bar
|
|
|
|
p.setBrush(borderColor);
|
|
|
|
QRectF FrameRect(baseRect.topLeft(), QSizeF(baseRect.width(), baseSize.height() / 10));
|
|
|
|
p.drawRect(FrameRect);
|
|
|
|
p.restore();
|
|
|
|
|
|
|
|
// Draw arrow for outer container drop indicators
|
|
|
|
if (CDockOverlay::ModeContainerOverlay == Mode && DockWidgetArea != CenterDockWidgetArea)
|
|
|
|
{
|
|
|
|
QRectF ArrowRect;
|
|
|
|
ArrowRect.setSize(baseSize);
|
|
|
|
ArrowRect.setWidth(ArrowRect.width() / 4.6);
|
|
|
|
ArrowRect.setHeight(ArrowRect.height() / 2);
|
|
|
|
ArrowRect.moveCenter(QPointF(0, 0));
|
|
|
|
QPolygonF Arrow;
|
|
|
|
Arrow << ArrowRect.topLeft()
|
|
|
|
<< QPointF( ArrowRect.right(), ArrowRect.center().y())
|
|
|
|
<< ArrowRect.bottomLeft();
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
p.setBrush(iconColor(CDockOverlayCross::ArrowColor));
|
|
|
|
p.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
p.translate(nonAreaRect.center().x(), nonAreaRect.center().y());
|
|
|
|
|
|
|
|
switch (DockWidgetArea)
|
|
|
|
{
|
|
|
|
case TopDockWidgetArea:
|
|
|
|
p.rotate(-90);
|
|
|
|
break;
|
|
|
|
case RightDockWidgetArea:
|
|
|
|
break;
|
|
|
|
case BottomDockWidgetArea:
|
|
|
|
p.rotate(90);
|
|
|
|
break;
|
|
|
|
case LeftDockWidgetArea:
|
|
|
|
p.rotate(180);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
p.drawPolygon(Arrow);
|
|
|
|
}
|
|
|
|
|
2018-11-05 21:12:34 +08:00
|
|
|
pm.setDevicePixelRatio(DevicePixelRatio);
|
2018-08-28 19:25:44 +08:00
|
|
|
return pm;
|
|
|
|
}
|
|
|
|
|
2017-04-12 05:26:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
CDockOverlay::CDockOverlay(QWidget* parent, eMode Mode) :
|
|
|
|
QFrame(parent),
|
|
|
|
d(new DockOverlayPrivate(this))
|
|
|
|
{
|
|
|
|
d->Mode = Mode;
|
|
|
|
d->Cross = new CDockOverlayCross(this);
|
2019-11-28 20:05:09 +08:00
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
|
|
|
|
#else
|
2017-04-12 05:26:33 +08:00
|
|
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
2019-11-28 20:05:09 +08:00
|
|
|
#endif
|
2017-04-12 05:26:33 +08:00
|
|
|
setWindowOpacity(1);
|
|
|
|
setWindowTitle("DockOverlay");
|
|
|
|
setAttribute(Qt::WA_NoSystemBackground);
|
|
|
|
setAttribute(Qt::WA_TranslucentBackground);
|
|
|
|
|
|
|
|
d->Cross->setVisible(false);
|
|
|
|
setVisible(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
CDockOverlay::~CDockOverlay()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlay::setAllowedAreas(DockWidgetAreas areas)
|
|
|
|
{
|
|
|
|
if (areas == d->AllowedAreas)
|
|
|
|
return;
|
|
|
|
d->AllowedAreas = areas;
|
|
|
|
d->Cross->reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
DockWidgetAreas CDockOverlay::allowedAreas() const
|
|
|
|
{
|
|
|
|
return d->AllowedAreas;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
DockWidgetArea CDockOverlay::dropAreaUnderCursor() const
|
|
|
|
{
|
|
|
|
DockWidgetArea Result = d->Cross->cursorLocation();
|
|
|
|
if (Result != InvalidDockWidgetArea)
|
|
|
|
{
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-02-14 01:56:04 +08:00
|
|
|
CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(d->TargetWidget.data());
|
2017-04-12 05:26:33 +08:00
|
|
|
if (!DockArea)
|
|
|
|
{
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-02-02 23:03:58 +08:00
|
|
|
if (DockArea->allowedAreas().testFlag(CenterDockWidgetArea)
|
2020-04-27 21:27:34 +08:00
|
|
|
&& !DockArea->titleBar()->isHidden()
|
2020-02-02 23:03:58 +08:00
|
|
|
&& DockArea->titleBarGeometry().contains(DockArea->mapFromGlobal(QCursor::pos())))
|
2017-04-12 05:26:33 +08:00
|
|
|
{
|
|
|
|
return CenterDockWidgetArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-12 17:23:41 +08:00
|
|
|
//============================================================================
|
|
|
|
DockWidgetArea CDockOverlay::visibleDropAreaUnderCursor() const
|
|
|
|
{
|
|
|
|
if (isHidden() || !d->DropPreviewEnabled)
|
|
|
|
{
|
|
|
|
return InvalidDockWidgetArea;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return dropAreaUnderCursor();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-12 05:26:33 +08:00
|
|
|
//============================================================================
|
|
|
|
DockWidgetArea CDockOverlay::showOverlay(QWidget* target)
|
|
|
|
{
|
|
|
|
if (d->TargetWidget == target)
|
|
|
|
{
|
|
|
|
// Hint: We could update geometry of overlay here.
|
|
|
|
DockWidgetArea da = dropAreaUnderCursor();
|
|
|
|
if (da != d->LastLocation)
|
|
|
|
{
|
|
|
|
repaint();
|
|
|
|
d->LastLocation = da;
|
|
|
|
}
|
|
|
|
return da;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->TargetWidget = target;
|
|
|
|
d->LastLocation = InvalidDockWidgetArea;
|
|
|
|
|
|
|
|
// Move it over the target.
|
|
|
|
resize(target->size());
|
|
|
|
QPoint TopLeft = target->mapToGlobal(target->rect().topLeft());
|
|
|
|
move(TopLeft);
|
|
|
|
show();
|
|
|
|
d->Cross->updatePosition();
|
2018-11-05 21:12:34 +08:00
|
|
|
d->Cross->updateOverlayIcons();
|
2017-04-12 05:26:33 +08:00
|
|
|
return dropAreaUnderCursor();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlay::hideOverlay()
|
|
|
|
{
|
|
|
|
hide();
|
|
|
|
d->TargetWidget.clear();
|
|
|
|
d->LastLocation = InvalidDockWidgetArea;
|
2019-11-26 21:40:56 +08:00
|
|
|
d->DropAreaRect = QRect();
|
2017-04-12 05:26:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlay::enableDropPreview(bool Enable)
|
|
|
|
{
|
|
|
|
d->DropPreviewEnabled = Enable;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-26 21:40:56 +08:00
|
|
|
//============================================================================
|
|
|
|
bool CDockOverlay::dropPreviewEnabled() const
|
|
|
|
{
|
|
|
|
return d->DropPreviewEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-12 05:26:33 +08:00
|
|
|
//============================================================================
|
|
|
|
void CDockOverlay::paintEvent(QPaintEvent* event)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event);
|
|
|
|
// Draw rect based on location
|
|
|
|
if (!d->DropPreviewEnabled)
|
|
|
|
{
|
|
|
|
d->DropAreaRect = QRect();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect r = rect();
|
|
|
|
const DockWidgetArea da = dropAreaUnderCursor();
|
|
|
|
double Factor = (CDockOverlay::ModeContainerOverlay == d->Mode) ?
|
|
|
|
3 : 2;
|
|
|
|
|
|
|
|
switch (da)
|
|
|
|
{
|
|
|
|
case TopDockWidgetArea: r.setHeight(r.height() / Factor); break;
|
|
|
|
case RightDockWidgetArea: r.setX(r.width() * (1 - 1 / Factor)); break;
|
|
|
|
case BottomDockWidgetArea: r.setY(r.height() * (1 - 1 / Factor)); break;
|
|
|
|
case LeftDockWidgetArea: r.setWidth(r.width() / Factor); break;
|
|
|
|
case CenterDockWidgetArea: r = rect();break;
|
|
|
|
default: return;
|
|
|
|
}
|
|
|
|
QPainter painter(this);
|
|
|
|
QColor Color = palette().color(QPalette::Active, QPalette::Highlight);
|
2018-09-26 15:57:36 +08:00
|
|
|
QPen Pen = painter.pen();
|
|
|
|
Pen.setColor(Color.darker(120));
|
|
|
|
Pen.setStyle(Qt::SolidLine);
|
|
|
|
Pen.setWidth(1);
|
|
|
|
Pen.setCosmetic(true);
|
|
|
|
painter.setPen(Pen);
|
|
|
|
Color = Color.lighter(130);
|
2017-04-12 05:26:33 +08:00
|
|
|
Color.setAlpha(64);
|
2018-09-26 15:57:36 +08:00
|
|
|
painter.setBrush(Color);
|
|
|
|
painter.drawRect(r.adjusted(0, 0, -1, -1));
|
2017-04-12 05:26:33 +08:00
|
|
|
d->DropAreaRect = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
QRect CDockOverlay::dropOverlayRect() const
|
|
|
|
{
|
|
|
|
return d->DropAreaRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlay::showEvent(QShowEvent* e)
|
|
|
|
{
|
|
|
|
d->Cross->show();
|
|
|
|
QFrame::showEvent(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlay::hideEvent(QHideEvent* e)
|
|
|
|
{
|
|
|
|
d->Cross->hide();
|
|
|
|
QFrame::hideEvent(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-28 19:25:44 +08:00
|
|
|
//============================================================================
|
|
|
|
bool CDockOverlay::event(QEvent *e)
|
|
|
|
{
|
|
|
|
bool Result = Super::event(e);
|
|
|
|
if (e->type() == QEvent::Polish)
|
|
|
|
{
|
|
|
|
d->Cross->setupOverlayCross(d->Mode);
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-12 05:26:33 +08:00
|
|
|
//============================================================================
|
|
|
|
static int areaAlignment(const DockWidgetArea area)
|
|
|
|
{
|
|
|
|
switch (area)
|
|
|
|
{
|
|
|
|
case TopDockWidgetArea: return (int) Qt::AlignHCenter | Qt::AlignBottom;
|
|
|
|
case RightDockWidgetArea: return (int) Qt::AlignLeft | Qt::AlignVCenter;
|
|
|
|
case BottomDockWidgetArea: return (int) Qt::AlignHCenter | Qt::AlignTop;
|
|
|
|
case LeftDockWidgetArea: return (int) Qt::AlignRight | Qt::AlignVCenter;
|
|
|
|
case CenterDockWidgetArea: return (int) Qt::AlignCenter;
|
|
|
|
default: return Qt::AlignCenter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
// DockOverlayCrossPrivate
|
|
|
|
//============================================================================
|
|
|
|
QPoint DockOverlayCrossPrivate::areaGridPosition(const DockWidgetArea area)
|
|
|
|
{
|
|
|
|
if (CDockOverlay::ModeDockAreaOverlay == Mode)
|
|
|
|
{
|
|
|
|
switch (area)
|
|
|
|
{
|
|
|
|
case TopDockWidgetArea: return QPoint(1, 2);
|
|
|
|
case RightDockWidgetArea: return QPoint(2, 3);
|
|
|
|
case BottomDockWidgetArea: return QPoint(3, 2);
|
|
|
|
case LeftDockWidgetArea: return QPoint(2, 1);
|
|
|
|
case CenterDockWidgetArea: return QPoint(2, 2);
|
|
|
|
default: return QPoint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (area)
|
|
|
|
{
|
|
|
|
case TopDockWidgetArea: return QPoint(0, 2);
|
|
|
|
case RightDockWidgetArea: return QPoint(2, 4);
|
|
|
|
case BottomDockWidgetArea: return QPoint(4, 2);
|
|
|
|
case LeftDockWidgetArea: return QPoint(2, 0);
|
|
|
|
case CenterDockWidgetArea: return QPoint(2, 2);
|
|
|
|
default: return QPoint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
CDockOverlayCross::CDockOverlayCross(CDockOverlay* overlay) :
|
|
|
|
QWidget(overlay->parentWidget()),
|
|
|
|
d(new DockOverlayCrossPrivate(this))
|
|
|
|
{
|
|
|
|
d->DockOverlay = overlay;
|
2019-11-28 20:05:09 +08:00
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
|
|
|
|
#else
|
2017-04-12 05:26:33 +08:00
|
|
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
2019-11-28 20:05:09 +08:00
|
|
|
#endif
|
2017-04-12 05:26:33 +08:00
|
|
|
setWindowTitle("DockOverlayCross");
|
|
|
|
setAttribute(Qt::WA_TranslucentBackground);
|
|
|
|
|
|
|
|
d->GridLayout = new QGridLayout();
|
|
|
|
d->GridLayout->setSpacing(0);
|
|
|
|
setLayout(d->GridLayout);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
CDockOverlayCross::~CDockOverlayCross()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::setupOverlayCross(CDockOverlay::eMode Mode)
|
|
|
|
{
|
|
|
|
d->Mode = Mode;
|
|
|
|
|
|
|
|
QHash<DockWidgetArea, QWidget*> areaWidgets;
|
2018-08-28 19:25:44 +08:00
|
|
|
areaWidgets.insert(TopDockWidgetArea, d->createDropIndicatorWidget(TopDockWidgetArea, Mode));
|
|
|
|
areaWidgets.insert(RightDockWidgetArea, d->createDropIndicatorWidget(RightDockWidgetArea, Mode));
|
|
|
|
areaWidgets.insert(BottomDockWidgetArea, d->createDropIndicatorWidget(BottomDockWidgetArea, Mode));
|
|
|
|
areaWidgets.insert(LeftDockWidgetArea, d->createDropIndicatorWidget(LeftDockWidgetArea, Mode));
|
|
|
|
areaWidgets.insert(CenterDockWidgetArea, d->createDropIndicatorWidget(CenterDockWidgetArea, Mode));
|
2019-01-14 15:59:37 +08:00
|
|
|
#if QT_VERSION >= 0x050600
|
2018-11-05 21:12:34 +08:00
|
|
|
d->LastDevicePixelRatio = devicePixelRatioF();
|
2019-01-14 15:59:37 +08:00
|
|
|
#else
|
|
|
|
d->LastDevicePixelRatio = devicePixelRatio();
|
|
|
|
#endif
|
2017-04-12 05:26:33 +08:00
|
|
|
setAreaWidgets(areaWidgets);
|
2018-08-28 19:25:44 +08:00
|
|
|
d->UpdateRequired = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-05 21:12:34 +08:00
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::updateOverlayIcons()
|
|
|
|
{
|
|
|
|
if (windowHandle()->devicePixelRatio() == d->LastDevicePixelRatio)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto Widget : d->DropIndicatorWidgets)
|
|
|
|
{
|
|
|
|
d->updateDropIndicatorIcon(Widget);
|
|
|
|
}
|
2020-02-17 17:41:13 +08:00
|
|
|
#if QT_VERSION >= 0x050600
|
2018-11-05 21:12:34 +08:00
|
|
|
d->LastDevicePixelRatio = devicePixelRatioF();
|
2019-01-14 15:59:37 +08:00
|
|
|
#else
|
|
|
|
d->LastDevicePixelRatio = devicePixelRatio();
|
|
|
|
#endif
|
2018-11-05 21:12:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-28 19:25:44 +08:00
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::setIconColor(eIconColor ColorIndex, const QColor& Color)
|
|
|
|
{
|
|
|
|
d->IconColors[ColorIndex] = Color;
|
|
|
|
d->UpdateRequired = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
QColor CDockOverlayCross::iconColor(eIconColor ColorIndex) const
|
|
|
|
{
|
|
|
|
return d->IconColors[ColorIndex];
|
2017-04-12 05:26:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::setAreaWidgets(const QHash<DockWidgetArea, QWidget*>& widgets)
|
|
|
|
{
|
|
|
|
// Delete old widgets.
|
|
|
|
QMutableHashIterator<DockWidgetArea, QWidget*> i(d->DropIndicatorWidgets);
|
|
|
|
while (i.hasNext())
|
|
|
|
{
|
|
|
|
i.next();
|
|
|
|
QWidget* widget = i.value();
|
|
|
|
d->GridLayout->removeWidget(widget);
|
|
|
|
delete widget;
|
|
|
|
i.remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert new widgets into grid.
|
|
|
|
d->DropIndicatorWidgets = widgets;
|
|
|
|
QHashIterator<DockWidgetArea, QWidget*> i2(d->DropIndicatorWidgets);
|
|
|
|
while (i2.hasNext())
|
|
|
|
{
|
|
|
|
i2.next();
|
|
|
|
const DockWidgetArea area = i2.key();
|
|
|
|
QWidget* widget = i2.value();
|
|
|
|
QPoint p = d->areaGridPosition(area);
|
|
|
|
d->GridLayout->addWidget(widget, p.x(), p.y(), (Qt::Alignment) areaAlignment(area));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CDockOverlay::ModeDockAreaOverlay == d->Mode)
|
|
|
|
{
|
|
|
|
d->GridLayout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
d->GridLayout->setRowStretch(0, 1);
|
|
|
|
d->GridLayout->setRowStretch(1, 0);
|
|
|
|
d->GridLayout->setRowStretch(2, 0);
|
|
|
|
d->GridLayout->setRowStretch(3, 0);
|
|
|
|
d->GridLayout->setRowStretch(4, 1);
|
|
|
|
|
|
|
|
d->GridLayout->setColumnStretch(0, 1);
|
|
|
|
d->GridLayout->setColumnStretch(1, 0);
|
|
|
|
d->GridLayout->setColumnStretch(2, 0);
|
|
|
|
d->GridLayout->setColumnStretch(3, 0);
|
|
|
|
d->GridLayout->setColumnStretch(4, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->GridLayout->setContentsMargins(4, 4, 4, 4);
|
|
|
|
d->GridLayout->setRowStretch(0, 0);
|
|
|
|
d->GridLayout->setRowStretch(1, 1);
|
|
|
|
d->GridLayout->setRowStretch(2, 1);
|
|
|
|
d->GridLayout->setRowStretch(3, 1);
|
|
|
|
d->GridLayout->setRowStretch(4, 0);
|
|
|
|
|
|
|
|
d->GridLayout->setColumnStretch(0, 0);
|
|
|
|
d->GridLayout->setColumnStretch(1, 1);
|
|
|
|
d->GridLayout->setColumnStretch(2, 1);
|
|
|
|
d->GridLayout->setColumnStretch(3, 1);
|
|
|
|
d->GridLayout->setColumnStretch(4, 0);
|
|
|
|
}
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
DockWidgetArea CDockOverlayCross::cursorLocation() const
|
|
|
|
{
|
|
|
|
const QPoint pos = mapFromGlobal(QCursor::pos());
|
|
|
|
QHashIterator<DockWidgetArea, QWidget*> i(d->DropIndicatorWidgets);
|
|
|
|
while (i.hasNext())
|
|
|
|
{
|
|
|
|
i.next();
|
|
|
|
if (d->DockOverlay->allowedAreas().testFlag(i.key())
|
|
|
|
&& i.value()
|
|
|
|
&& i.value()->isVisible()
|
|
|
|
&& i.value()->geometry().contains(pos))
|
|
|
|
{
|
|
|
|
return i.key();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return InvalidDockWidgetArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::showEvent(QShowEvent*)
|
|
|
|
{
|
2018-08-28 19:25:44 +08:00
|
|
|
if (d->UpdateRequired)
|
|
|
|
{
|
|
|
|
setupOverlayCross(d->Mode);
|
|
|
|
}
|
2017-04-12 05:26:33 +08:00
|
|
|
this->updatePosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::updatePosition()
|
|
|
|
{
|
|
|
|
resize(d->DockOverlay->size());
|
|
|
|
QPoint TopLeft = d->DockOverlay->pos();
|
|
|
|
QPoint Offest((this->width() - d->DockOverlay->width()) / 2,
|
|
|
|
(this->height() - d->DockOverlay->height()) / 2);
|
|
|
|
QPoint CrossTopLeft = TopLeft - Offest;
|
|
|
|
move(CrossTopLeft);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::reset()
|
|
|
|
{
|
|
|
|
QList<DockWidgetArea> allAreas;
|
|
|
|
allAreas << TopDockWidgetArea << RightDockWidgetArea
|
|
|
|
<< BottomDockWidgetArea << LeftDockWidgetArea << CenterDockWidgetArea;
|
|
|
|
const DockWidgetAreas allowedAreas = d->DockOverlay->allowedAreas();
|
|
|
|
|
|
|
|
// Update visibility of area widgets based on allowedAreas.
|
|
|
|
for (int i = 0; i < allAreas.count(); ++i)
|
|
|
|
{
|
|
|
|
QPoint p = d->areaGridPosition(allAreas.at(i));
|
|
|
|
QLayoutItem* item = d->GridLayout->itemAtPosition(p.x(), p.y());
|
|
|
|
QWidget* w = nullptr;
|
|
|
|
if (item && (w = item->widget()) != nullptr)
|
|
|
|
{
|
|
|
|
w->setVisible(allowedAreas.testFlag(allAreas.at(i)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-28 19:25:44 +08:00
|
|
|
//============================================================================
|
|
|
|
void CDockOverlayCross::setIconColors(const QString& Colors)
|
|
|
|
{
|
|
|
|
static const QMap<QString, int> ColorCompenentStringMap{
|
|
|
|
{"Frame", CDockOverlayCross::FrameColor},
|
|
|
|
{"Background", CDockOverlayCross::WindowBackgroundColor},
|
|
|
|
{"Overlay", CDockOverlayCross::OverlayColor},
|
|
|
|
{"Arrow", CDockOverlayCross::ArrowColor},
|
|
|
|
{"Shadow", CDockOverlayCross::ShadowColor}};
|
|
|
|
|
2021-08-24 01:47:15 +08:00
|
|
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
|
2021-01-03 01:06:45 +08:00
|
|
|
auto SkipEmptyParts = QString::SkipEmptyParts;
|
|
|
|
#else
|
|
|
|
auto SkipEmptyParts = Qt::SkipEmptyParts;
|
|
|
|
#endif
|
|
|
|
auto ColorList = Colors.split(' ', SkipEmptyParts);
|
2018-08-28 19:25:44 +08:00
|
|
|
for (const auto& ColorListEntry : ColorList)
|
|
|
|
{
|
2021-01-03 01:06:45 +08:00
|
|
|
auto ComponentColor = ColorListEntry.split('=', SkipEmptyParts);
|
2018-08-28 19:25:44 +08:00
|
|
|
int Component = ColorCompenentStringMap.value(ComponentColor[0], -1);
|
|
|
|
if (Component < 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
d->IconColors[Component] = QColor(ComponentColor[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
d->UpdateRequired = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
QString CDockOverlayCross::iconColors() const
|
|
|
|
{
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-12 05:26:33 +08:00
|
|
|
|
|
|
|
} // namespace ads
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|