Init commit for GitHub.
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pro.user
|
33
AdvancedDockingSystem/AdvancedDockingSystem.pro
Normal file
@ -0,0 +1,33 @@
|
||||
TARGET = AdvancedDockingSystem
|
||||
|
||||
QT += core gui
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TEMPLATE = lib
|
||||
|
||||
CONFIG += staticlib
|
||||
|
||||
INCLUDEPATH += $$PWD/src
|
||||
|
||||
SOURCES += \
|
||||
src/ads/section_content.cpp \
|
||||
src/ads/ads.cpp \
|
||||
src/ads/section_widget.cpp \
|
||||
src/ads/section_title_widget.cpp \
|
||||
src/ads/container_widget.cpp \
|
||||
src/ads/drop_overlay.cpp \
|
||||
src/ads/floating_widget.cpp \
|
||||
src/ads/section_content_widget.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/ads/ads.h \
|
||||
src/ads/container_widget.h \
|
||||
src/ads/section_widget.h \
|
||||
src/ads/section_content.h \
|
||||
src/ads/section_title_widget.h \
|
||||
src/ads/section_content_widget.h \
|
||||
src/ads/drop_overlay.h \
|
||||
src/ads/floating_widget.h
|
||||
|
||||
RESOURCES += \
|
||||
res/ads.qrc
|
18
AdvancedDockingSystem/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Advanced Docking System
|
||||
Developed by Manuel Freiholz
|
||||
|
||||
|
||||
## Notes
|
||||
- SectionContent may safe a "size-type" property, which defines how the size of the widget
|
||||
should be handled.
|
||||
- PerCent: Resize in proportion to other widgets.
|
||||
- Fixed: Width or height are fixed (based on orientation).
|
||||
|
||||
|
||||
## TODO / Issues
|
||||
[ ] Drop indicator images should be fully visible over the DropOverlay rectangle.
|
||||
[ ] Clean up of unused e.g. count()<=1 QSplitters doesn't work well.
|
||||
|
||||
|
||||
## License notes
|
||||
- Copied drop images from http://www.codeproject.com/Articles/140209/Building-a-Docking-Window-Management-Solution-in-W
|
16
AdvancedDockingSystem/res/ads.qrc
Normal file
@ -0,0 +1,16 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>img/dnd-thumbnail.png</file>
|
||||
<file>img/dock-bottom.png</file>
|
||||
<file>img/dock-center.png</file>
|
||||
<file>img/dock-left.png</file>
|
||||
<file>img/dock-right.png</file>
|
||||
<file>img/dock-top.png</file>
|
||||
<file>img/split-bottom.png</file>
|
||||
<file>img/split-left.png</file>
|
||||
<file>img/split-right.png</file>
|
||||
<file>img/split-top.png</file>
|
||||
<file>img/splitter-horizontal.png</file>
|
||||
<file>img/splitter-vertical.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
AdvancedDockingSystem/res/img/dnd-thumbnail.png
Normal file
After Width: | Height: | Size: 249 B |
BIN
AdvancedDockingSystem/res/img/dock-bottom.png
Normal file
After Width: | Height: | Size: 489 B |
BIN
AdvancedDockingSystem/res/img/dock-center.png
Normal file
After Width: | Height: | Size: 402 B |
BIN
AdvancedDockingSystem/res/img/dock-left.png
Normal file
After Width: | Height: | Size: 527 B |
BIN
AdvancedDockingSystem/res/img/dock-right.png
Normal file
After Width: | Height: | Size: 516 B |
BIN
AdvancedDockingSystem/res/img/dock-top.png
Normal file
After Width: | Height: | Size: 498 B |
BIN
AdvancedDockingSystem/res/img/split-bottom.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
AdvancedDockingSystem/res/img/split-left.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
AdvancedDockingSystem/res/img/split-right.png
Normal file
After Width: | Height: | Size: 647 B |
BIN
AdvancedDockingSystem/res/img/split-top.png
Normal file
After Width: | Height: | Size: 619 B |
BIN
AdvancedDockingSystem/res/img/splitter-horizontal.png
Normal file
After Width: | Height: | Size: 326 B |
BIN
AdvancedDockingSystem/res/img/splitter-vertical.png
Normal file
After Width: | Height: | Size: 406 B |
60
AdvancedDockingSystem/src/ads/ads.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "ads.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSplitter>
|
||||
|
||||
#include "container_widget.h"
|
||||
|
||||
const QString DragData::MIMETYPE = QString("qt/ads-dragdata");
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
ContainerWidget* findParentContainerWidget(QWidget* w)
|
||||
{
|
||||
ContainerWidget* cw = 0;
|
||||
QWidget* next = w;
|
||||
do
|
||||
{
|
||||
if ((cw = dynamic_cast<ContainerWidget*>(next)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
next = next->parentWidget();
|
||||
}
|
||||
while (next);
|
||||
return cw;
|
||||
}
|
||||
|
||||
SectionWidget* findParentSectionWidget(class QWidget* w)
|
||||
{
|
||||
SectionWidget* cw = 0;
|
||||
QWidget* next = w;
|
||||
do
|
||||
{
|
||||
if ((cw = dynamic_cast<SectionWidget*>(next)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
next = next->parentWidget();
|
||||
}
|
||||
while (next);
|
||||
return cw;
|
||||
}
|
||||
|
||||
QSplitter* findParentSplitter(class QWidget* w)
|
||||
{
|
||||
QSplitter* cw = 0;
|
||||
QWidget* next = w;
|
||||
do
|
||||
{
|
||||
if ((cw = dynamic_cast<QSplitter*>(next)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
next = next->parentWidget();
|
||||
}
|
||||
while (next);
|
||||
return cw;
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
85
AdvancedDockingSystem/src/ads/ads.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef ADS_H
|
||||
#define ADS_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <QSharedPointer>
|
||||
class QSplitter;
|
||||
|
||||
/*
|
||||
* ADS - Advanced Docking System for Qt
|
||||
* Developed by Manuel Freiholz
|
||||
*/
|
||||
|
||||
#define ADS_NAMESPACE_BEGIN namespace ads {
|
||||
#define ADS_NAMESPACE_END }
|
||||
|
||||
// Width of the native window frame border (based on OS).
|
||||
#define ADS_WINDOW_FRAME_BORDER_WIDTH 7
|
||||
|
||||
// Indicates whether ADS should include animations.
|
||||
//#define ADS_ANIMATIONS_ENABLED 1
|
||||
//#define ADS_ANIMATION_DURATION 150
|
||||
|
||||
// DragData contains information about dragged contents (SectionContent).
|
||||
// e.g. from where is has been dragged.
|
||||
class DragData
|
||||
{
|
||||
public:
|
||||
static const QString MIMETYPE;
|
||||
|
||||
QByteArray serialize() const
|
||||
{
|
||||
QByteArray ba;
|
||||
QDataStream out(&ba, QIODevice::WriteOnly);
|
||||
out << contentUid;
|
||||
out << sectionUid;
|
||||
return ba;
|
||||
}
|
||||
|
||||
void deserialize(const QByteArray& bytes)
|
||||
{
|
||||
QDataStream in(bytes);
|
||||
in >> contentUid;
|
||||
in >> sectionUid;
|
||||
}
|
||||
|
||||
QString toString() const
|
||||
{
|
||||
return QString("content-uid=%1; section-uid=%2").arg(contentUid).arg(sectionUid);
|
||||
}
|
||||
|
||||
public:
|
||||
int contentUid;
|
||||
int sectionUid;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
enum DropArea
|
||||
{
|
||||
TopDropArea,
|
||||
RightDropArea,
|
||||
BottomDropArea,
|
||||
LeftDropArea,
|
||||
CenterDropArea,
|
||||
InvalidDropArea
|
||||
};
|
||||
|
||||
class InternalContentData
|
||||
{
|
||||
public:
|
||||
InternalContentData() : titleWidget(0), contentWidget(0) {}
|
||||
QSharedPointer<class SectionContent> content;
|
||||
class SectionTitleWidget* titleWidget;
|
||||
class SectionContentWidget* contentWidget;
|
||||
};
|
||||
|
||||
class ContainerWidget* findParentContainerWidget(class QWidget* w);
|
||||
class SectionWidget* findParentSectionWidget(class QWidget* w);
|
||||
QSplitter* findParentSplitter(class QWidget* w);
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
|
||||
#endif // ADC_H
|
272
AdvancedDockingSystem/src/ads/container_widget.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
#include "container_widget.h"
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMenu>
|
||||
#include <QSplitter>
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
// Static Helper //////////////////////////////////////////////////////
|
||||
|
||||
static QSplitter* newSplitter(Qt::Orientation orientation = Qt::Horizontal)
|
||||
{
|
||||
QSplitter* s = new QSplitter();
|
||||
s->setChildrenCollapsible(false);
|
||||
s->setOpaqueResize(false);
|
||||
s->setOrientation(orientation);
|
||||
return s;
|
||||
}
|
||||
|
||||
//static void deleteEmptySplitter(ContainerWidget* container)
|
||||
//{
|
||||
// auto splitters = container->findChildren<QSplitter*>();
|
||||
// for (auto i = 0; i < splitters.count(); ++i)
|
||||
// {
|
||||
// if (splitters[i]->count() == 0 && container->_splitter != splitters[i])
|
||||
// {
|
||||
// qDebug() << "Delete empty QSplitter";
|
||||
// splitters[i]->deleteLater();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
ContainerWidget::ContainerWidget(QWidget *parent) :
|
||||
QFrame(parent),
|
||||
_mainLayout(NULL),
|
||||
_orientation(Qt::Horizontal),
|
||||
_splitter(NULL)
|
||||
{
|
||||
_mainLayout = new QGridLayout();
|
||||
_mainLayout->setContentsMargins(9, 9, 9, 9);
|
||||
_mainLayout->setSpacing(9);
|
||||
setLayout(_mainLayout);
|
||||
}
|
||||
|
||||
Qt::Orientation ContainerWidget::orientation() const
|
||||
{
|
||||
return _orientation;
|
||||
}
|
||||
|
||||
void ContainerWidget::setOrientation(Qt::Orientation orientation)
|
||||
{
|
||||
if (_orientation != orientation)
|
||||
{
|
||||
_orientation = orientation;
|
||||
emit orientationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ContainerWidget::dropContent(const InternalContentData& data, SectionWidget* targetSection, DropArea area)
|
||||
{
|
||||
if (!targetSection)
|
||||
{
|
||||
qWarning() << "Drop on invalid targetSection";
|
||||
return;
|
||||
}
|
||||
|
||||
auto targetSectionSplitter = findParentSplitter(targetSection);
|
||||
|
||||
// Drop logic based on area.
|
||||
switch (area)
|
||||
{
|
||||
case TopDropArea:
|
||||
{
|
||||
auto sw = new SectionWidget(this);
|
||||
sw->addContent(data, true);
|
||||
if (targetSectionSplitter->orientation() == Qt::Vertical)
|
||||
{
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
targetSectionSplitter->insertWidget(index, sw);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
auto s = newSplitter(Qt::Vertical);
|
||||
s->addWidget(sw);
|
||||
s->addWidget(targetSection);
|
||||
targetSectionSplitter->insertWidget(index, s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RightDropArea:
|
||||
{
|
||||
auto sw = new SectionWidget(this);
|
||||
sw->addContent(data, true);
|
||||
if (targetSectionSplitter->orientation() == Qt::Horizontal)
|
||||
{
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
targetSectionSplitter->insertWidget(index + 1, sw);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
auto s = newSplitter(Qt::Horizontal);
|
||||
s->addWidget(targetSection);
|
||||
s->addWidget(sw);
|
||||
targetSectionSplitter->insertWidget(index, s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BottomDropArea:
|
||||
{
|
||||
auto sw = new SectionWidget(this);
|
||||
sw->addContent(data, true);
|
||||
if (targetSectionSplitter->orientation() == Qt::Vertical)
|
||||
{
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
targetSectionSplitter->insertWidget(index + 1, sw);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
auto s = newSplitter(Qt::Vertical);
|
||||
s->addWidget(targetSection);
|
||||
s->addWidget(sw);
|
||||
targetSectionSplitter->insertWidget(index, s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LeftDropArea:
|
||||
{
|
||||
auto sw = new SectionWidget(this);
|
||||
sw->addContent(data, true);
|
||||
if (targetSectionSplitter->orientation() == Qt::Horizontal)
|
||||
{
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
targetSectionSplitter->insertWidget(index, sw);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s = newSplitter(Qt::Horizontal);
|
||||
s->addWidget(sw);
|
||||
auto index = targetSectionSplitter->indexOf(targetSection);
|
||||
targetSectionSplitter->insertWidget(index, s);
|
||||
s->addWidget(targetSection);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CenterDropArea:
|
||||
{
|
||||
targetSection->addContent(data, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContainerWidget::addSection(SectionWidget* section)
|
||||
{
|
||||
// Create default splitter.
|
||||
if (!_splitter)
|
||||
{
|
||||
_splitter = new QSplitter(_orientation);
|
||||
_splitter->setChildrenCollapsible(false);
|
||||
_splitter->setOpaqueResize(false);
|
||||
_mainLayout->addWidget(_splitter, 0, 0);
|
||||
}
|
||||
if (_splitter->indexOf(section) != -1)
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << QString("Section has already been added");
|
||||
return;
|
||||
}
|
||||
_splitter->addWidget(section);
|
||||
}
|
||||
|
||||
void ContainerWidget::splitSections(SectionWidget* s1, SectionWidget* s2, Qt::Orientation orientation)
|
||||
{
|
||||
addSection(s1);
|
||||
addSection(s2);
|
||||
//_sections.append(s2);
|
||||
|
||||
auto currentSplitter = findParentSplitter(s1);
|
||||
if (currentSplitter)
|
||||
{
|
||||
const int index = currentSplitter->indexOf(s1);
|
||||
auto splitter = new QSplitter(orientation, this);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
splitter->setOpaqueResize(false);
|
||||
splitter->addWidget(s1);
|
||||
splitter->addWidget(s2);
|
||||
currentSplitter->insertWidget(index, splitter);
|
||||
}
|
||||
// _mainLayout->replaceWidget(section1, splitter);
|
||||
// splitter->addWidget(section1);
|
||||
// splitter->addWidget(section2);
|
||||
}
|
||||
|
||||
SectionWidget* ContainerWidget::sectionAt(const QPoint& pos) const
|
||||
{
|
||||
auto gpos = mapToGlobal(pos);
|
||||
for (int i = 0; i < _sections.size(); ++i)
|
||||
{
|
||||
auto sw = _sections[i];
|
||||
if (sw->rect().contains(sw->mapFromGlobal(gpos)))
|
||||
{
|
||||
return sw;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QRect ContainerWidget::outerTopDropRect() const
|
||||
{
|
||||
auto r = rect();
|
||||
auto h = r.height() / 100 * 5;
|
||||
return QRect(r.left(), r.top(), r.width(), h);
|
||||
}
|
||||
|
||||
QRect ContainerWidget::outerRightDropRect() const
|
||||
{
|
||||
auto r = rect();
|
||||
auto w = r.width() / 100 * 5;
|
||||
return QRect(r.right() - w, r.top(), w, r.height());
|
||||
}
|
||||
|
||||
QRect ContainerWidget::outerBottomDropRect() const
|
||||
{
|
||||
auto r = rect();
|
||||
auto h = r.height() / 100 * 5;
|
||||
return QRect(r.left(), r.bottom() - h, r.width(), h);
|
||||
}
|
||||
|
||||
QRect ContainerWidget::outerLeftDropRect() const
|
||||
{
|
||||
auto r = rect();
|
||||
auto w = r.width() / 100 * 5;
|
||||
return QRect(r.left(), r.top(), w, r.height());
|
||||
}
|
||||
|
||||
void ContainerWidget::paintEvent(QPaintEvent* e)
|
||||
{
|
||||
QFrame::paintEvent(e);
|
||||
|
||||
// QPainter p(this);
|
||||
// p.fillRect(outerTopDropRect(), QBrush(QColor(Qt::red)));
|
||||
// p.fillRect(outerRightDropRect(), QBrush(QColor(Qt::green)));
|
||||
// p.fillRect(outerBottomDropRect(), QBrush(QColor(Qt::blue)));
|
||||
// p.fillRect(outerLeftDropRect(), QBrush(QColor(Qt::yellow)));
|
||||
}
|
||||
|
||||
void ContainerWidget::contextMenuEvent(QContextMenuEvent*)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// if (_contents.size() <= 0)
|
||||
// {
|
||||
// QFrame::contextMenuEvent(e);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Menu with all available contents.
|
||||
// QMenu m;
|
||||
// for (int i = 0; i < _contents.size(); ++i)
|
||||
// {
|
||||
// auto c = _contents[i];
|
||||
// m.addAction(QIcon(), QString("Content %1").arg(c->uid()));
|
||||
// }
|
||||
// m.exec();
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
69
AdvancedDockingSystem/src/ads/container_widget.h
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef ADS_CONTAINERWIDGET_H
|
||||
#define ADS_CONTAINERWIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QGridLayout>
|
||||
#include <QPoint>
|
||||
#include <QList>
|
||||
class QSplitter;
|
||||
|
||||
#include "ads.h"
|
||||
#include "section_content.h"
|
||||
#include "section_widget.h"
|
||||
#include "floating_widget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
// ContainerWidget is the main container to provide the docking
|
||||
// functionality. It manages mulitple Sections and all possible areas.
|
||||
class ContainerWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
|
||||
|
||||
friend class SectionWidget;
|
||||
|
||||
public:
|
||||
explicit ContainerWidget(QWidget *parent = NULL);
|
||||
|
||||
Qt::Orientation orientation() const;
|
||||
void setOrientation(Qt::Orientation orientation);
|
||||
|
||||
void dropContent(const InternalContentData& data, SectionWidget* targetSection, DropArea area);
|
||||
|
||||
void addSection(SectionWidget* section);
|
||||
void splitSections(SectionWidget* section1, SectionWidget* section2, Qt::Orientation orientation = Qt::Horizontal);
|
||||
SectionWidget* sectionAt(const QPoint& pos) const;
|
||||
|
||||
// Drop areas for the ContainerWidget
|
||||
QRect outerTopDropRect() const;
|
||||
QRect outerRightDropRect() const;
|
||||
QRect outerBottomDropRect() const;
|
||||
QRect outerLeftDropRect() const;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent*);
|
||||
virtual void contextMenuEvent(QContextMenuEvent*);
|
||||
|
||||
private: // Make private!
|
||||
|
||||
|
||||
signals:
|
||||
void orientationChanged();
|
||||
|
||||
public:
|
||||
// Existing sections.
|
||||
// SectionWidgets are always visible.
|
||||
QList<SectionWidget*> _sections;
|
||||
|
||||
// All currently active Floatingwidgets.
|
||||
QList<FloatingWidget*> _floatingWidgets;
|
||||
|
||||
// Layout stuff
|
||||
QGridLayout* _mainLayout;
|
||||
Qt::Orientation _orientation; ///< Default orientation of new sections.
|
||||
QSplitter* _splitter; ///< Default/main splitter.
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
203
AdvancedDockingSystem/src/ads/drop_overlay.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include "drop_overlay.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPointer>
|
||||
#include <QPaintEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <QMoveEvent>
|
||||
#include <QPainter>
|
||||
#include <QGridLayout>
|
||||
#include <QCursor>
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtWidgets/QLabel>
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
// Helper /////////////////////////////////////////////////////////////
|
||||
|
||||
static QWidget* createDropWidget(const QString& img)
|
||||
{
|
||||
auto label = new QLabel();
|
||||
label->setObjectName("DropAreaLabel");
|
||||
label->setPixmap(QPixmap(img));
|
||||
return label;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
DropOverlay::DropOverlay(QWidget *parent) :
|
||||
QFrame(parent),
|
||||
_splitAreas(NULL)
|
||||
{
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
setWindowFlags(windowFlags() | Qt::Tool);
|
||||
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
|
||||
setWindowOpacity(0.2);
|
||||
|
||||
auto l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
setLayout(l);
|
||||
|
||||
_splitAreas = new DropSplitAreas(parent);
|
||||
_splitAreas->move(pos());
|
||||
_splitAreas->resize(size());
|
||||
_splitAreas->setVisible(true);
|
||||
}
|
||||
|
||||
DropOverlay::~DropOverlay()
|
||||
{
|
||||
if (_splitAreas)
|
||||
{
|
||||
delete _splitAreas;
|
||||
_splitAreas = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DropArea DropOverlay::cursorLocation() const
|
||||
{
|
||||
auto loc = InvalidDropArea;
|
||||
if (_splitAreas)
|
||||
{
|
||||
loc = _splitAreas->cursorLocation();
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
void DropOverlay::paintEvent(QPaintEvent*)
|
||||
{
|
||||
QPainter p(this);
|
||||
|
||||
// Draw rect over the entire size + border.
|
||||
auto r = rect();
|
||||
r.setWidth(r.width() - 1);
|
||||
r.setHeight(r.height() - 1);
|
||||
|
||||
p.fillRect(r, QBrush(QColor(0, 100, 255), Qt::Dense4Pattern));
|
||||
p.setBrush(QBrush(QColor(0, 100, 255)));
|
||||
p.drawRect(r);
|
||||
}
|
||||
|
||||
void DropOverlay::resizeEvent(QResizeEvent* e)
|
||||
{
|
||||
// Keep it in center of DropOverlay
|
||||
if (_splitAreas)
|
||||
{
|
||||
_splitAreas->resize(e->size());
|
||||
}
|
||||
}
|
||||
|
||||
void DropOverlay::moveEvent(QMoveEvent* e)
|
||||
{
|
||||
// Keep it in center of DropOverlay
|
||||
if (_splitAreas)
|
||||
{
|
||||
_splitAreas->move(e->pos());
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
DropSplitAreas::DropSplitAreas(QWidget* parent) : AbstractDropAreas(parent)
|
||||
{
|
||||
//setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
setWindowFlags(windowFlags() | Qt::Tool);
|
||||
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
|
||||
|
||||
_top = createDropWidget(":/img/split-top.png");
|
||||
_right = createDropWidget(":/img/split-right.png");
|
||||
_bottom = createDropWidget(":/img/split-bottom.png");
|
||||
_left = createDropWidget(":/img/split-left.png");
|
||||
_center = createDropWidget(":/img/dock-center.png");
|
||||
|
||||
QGridLayout* grid = new QGridLayout();
|
||||
grid->setContentsMargins(0, 0, 0, 0);
|
||||
grid->setSpacing(6);
|
||||
grid->addWidget(_top, 0, 1, Qt::AlignHCenter | Qt::AlignBottom);
|
||||
grid->addWidget(_right, 1, 2, Qt::AlignLeft | Qt::AlignVCenter);
|
||||
grid->addWidget(_bottom, 2, 1, Qt::AlignHCenter | Qt::AlignTop);
|
||||
grid->addWidget(_left, 1, 0, Qt::AlignRight | Qt::AlignVCenter);
|
||||
grid->addWidget(_center, 1, 1, Qt::AlignCenter);
|
||||
|
||||
QBoxLayout* bl1 = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
bl1->setContentsMargins(0, 0, 0, 0);
|
||||
bl1->setSpacing(0);
|
||||
setLayout(bl1);
|
||||
|
||||
QBoxLayout* bl2 = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
bl2->setContentsMargins(0, 0, 0, 0);
|
||||
bl2->setSpacing(0);
|
||||
|
||||
bl1->addStretch(1);
|
||||
bl1->addLayout(bl2);
|
||||
bl2->addStretch(1);
|
||||
bl2->addLayout(grid, 0);
|
||||
bl2->addStretch(1);
|
||||
bl1->addStretch(1);
|
||||
}
|
||||
|
||||
DropArea DropSplitAreas::cursorLocation() const
|
||||
{
|
||||
auto loc = InvalidDropArea;
|
||||
auto pos = mapFromGlobal(QCursor::pos());
|
||||
if (_top->geometry().contains(pos))
|
||||
{
|
||||
loc = TopDropArea;
|
||||
}
|
||||
else if (_right->geometry().contains(pos))
|
||||
{
|
||||
loc = RightDropArea;
|
||||
}
|
||||
else if (_bottom->geometry().contains(pos))
|
||||
{
|
||||
loc = BottomDropArea;
|
||||
}
|
||||
else if (_left->geometry().contains(pos))
|
||||
{
|
||||
loc = LeftDropArea;
|
||||
}
|
||||
else if (_center->geometry().contains(pos))
|
||||
{
|
||||
loc = CenterDropArea;
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
// Globals ////////////////////////////////////////////////////////////
|
||||
|
||||
static QPointer<DropOverlay> MyOverlay;
|
||||
static QPointer<QWidget> MyOverlayParent;
|
||||
|
||||
DropArea showDropOverlay(QWidget* parent)
|
||||
{
|
||||
if (MyOverlay)
|
||||
{
|
||||
if (MyOverlayParent == parent)
|
||||
{
|
||||
// Hint: We could update geometry of overlay here.
|
||||
return MyOverlay->cursorLocation();
|
||||
}
|
||||
hideDropOverlay();
|
||||
}
|
||||
|
||||
// Create new overlay and move it directly over the "parent" widget.
|
||||
MyOverlay = new DropOverlay(parent);
|
||||
MyOverlay->resize(parent->size());
|
||||
MyOverlay->move(parent->mapToGlobal(parent->rect().topLeft()));
|
||||
MyOverlay->show();
|
||||
MyOverlayParent = parent;
|
||||
return MyOverlay->cursorLocation();
|
||||
}
|
||||
|
||||
void hideDropOverlay()
|
||||
{
|
||||
if (MyOverlay)
|
||||
{
|
||||
MyOverlay->hide();
|
||||
delete MyOverlay;
|
||||
MyOverlayParent.clear();
|
||||
}
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
61
AdvancedDockingSystem/src/ads/drop_overlay.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef DROP_OVERLAY_H
|
||||
#define DROP_OVERLAY_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
// DropOverlay paints a translucent rectangle over another widget.
|
||||
// It can also show different types of drop area indicators.
|
||||
class DropOverlay : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DropOverlay(QWidget* parent);
|
||||
virtual ~DropOverlay();
|
||||
DropArea cursorLocation() const;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *e);
|
||||
virtual void resizeEvent(QResizeEvent* e);
|
||||
virtual void moveEvent(QMoveEvent* e);
|
||||
|
||||
private:
|
||||
class DropSplitAreas* _splitAreas;
|
||||
};
|
||||
|
||||
// AbstractDropAreas is used as base for drop area indicator widgets.
|
||||
class AbstractDropAreas : public QWidget
|
||||
{
|
||||
public:
|
||||
AbstractDropAreas(QWidget* parent) : QWidget(parent) {}
|
||||
virtual DropArea cursorLocation() const = 0;
|
||||
};
|
||||
|
||||
// DropSplitAreas shows a cross with 5 different drop area possibilities.
|
||||
class DropSplitAreas : public AbstractDropAreas
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DropSplitAreas(QWidget* parent);
|
||||
virtual DropArea cursorLocation() const;
|
||||
|
||||
private:
|
||||
QWidget* _top;
|
||||
QWidget* _right;
|
||||
QWidget* _bottom;
|
||||
QWidget* _left;
|
||||
QWidget* _center;
|
||||
};
|
||||
|
||||
|
||||
DropArea showDropOverlay(QWidget* parent);
|
||||
void hideDropOverlay();
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
||||
|
72
AdvancedDockingSystem/src/ads/floating_widget.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "floating_widget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QSizePolicy>
|
||||
#include <QMouseEvent>
|
||||
#include <QStyle>
|
||||
|
||||
#include "section_title_widget.h"
|
||||
#include "section_content_widget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
FloatingWidget::FloatingWidget(SectionContent::RefPtr sc, SectionTitleWidget* titleWidget, SectionContentWidget* contentWidget, QWidget* parent) :
|
||||
QWidget(parent, Qt::CustomizeWindowHint | Qt::Tool),
|
||||
_content(sc),
|
||||
_titleWidget(titleWidget),
|
||||
_contentWidget(contentWidget)
|
||||
{
|
||||
auto l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
setLayout(l);
|
||||
|
||||
// Title + Controls
|
||||
_titleLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
_titleLayout->addWidget(titleWidget, 1);
|
||||
|
||||
auto maximizeButton = new QPushButton();
|
||||
maximizeButton->setObjectName("maximizeButton");
|
||||
maximizeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarMaxButton));
|
||||
maximizeButton->setToolTip(tr("Maximize"));
|
||||
maximizeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
_titleLayout->addWidget(maximizeButton);
|
||||
|
||||
auto closeButton = new QPushButton();
|
||||
closeButton->setObjectName("closeButton");
|
||||
closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
|
||||
closeButton->setToolTip(tr("Close"));
|
||||
closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
_titleLayout->addWidget(closeButton);
|
||||
QObject::connect(closeButton, &QPushButton::clicked, this, &FloatingWidget::close);
|
||||
|
||||
l->addLayout(_titleLayout, 0);
|
||||
|
||||
// Content
|
||||
l->addWidget(contentWidget, 1);
|
||||
contentWidget->show();
|
||||
}
|
||||
|
||||
InternalContentData FloatingWidget::takeContent()
|
||||
{
|
||||
InternalContentData data;
|
||||
data.content = _content;
|
||||
data.titleWidget = _titleWidget;
|
||||
data.contentWidget = _contentWidget;
|
||||
|
||||
_titleLayout->removeWidget(_titleWidget);
|
||||
_titleWidget = NULL;
|
||||
|
||||
layout()->removeWidget(_contentWidget);
|
||||
_contentWidget = NULL;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void FloatingWidget::closeEvent(QCloseEvent*)
|
||||
{
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
39
AdvancedDockingSystem/src/ads/floating_widget.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef FLOATINGWIDGET_H
|
||||
#define FLOATINGWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads.h"
|
||||
#include "section_content.h"
|
||||
|
||||
class QBoxLayout;
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
class SectionTitleWidget;
|
||||
class SectionContentWidget;
|
||||
|
||||
// FloatingWidget holds and displays SectionContent as a floating window.
|
||||
// It can be resized, moved and dropped back into a SectionWidget.
|
||||
class FloatingWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FloatingWidget(SectionContent::RefPtr sc, SectionTitleWidget* titleWidget, SectionContentWidget* contentWidget, QWidget* parent = nullptr);
|
||||
InternalContentData takeContent();
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent* e);
|
||||
|
||||
private:
|
||||
SectionContent::RefPtr _content;
|
||||
SectionTitleWidget* _titleWidget;
|
||||
SectionContentWidget* _contentWidget;
|
||||
|
||||
QBoxLayout* _titleLayout;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
56
AdvancedDockingSystem/src/ads/section_content.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "section_content.h"
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
int SectionContent::NextUid = 1;
|
||||
QHash<int, SectionContent*> SectionContent::LookupMap;
|
||||
|
||||
SectionContent::SectionContent(QWidget* title, QWidget* content, const QString& uniqueName) :
|
||||
_uid(NextUid++),
|
||||
_uniqueName(uniqueName)
|
||||
{
|
||||
_title = title;
|
||||
_content = content;
|
||||
LookupMap.insert(_uid, this);
|
||||
}
|
||||
|
||||
SectionContent::~SectionContent()
|
||||
{
|
||||
LookupMap.remove(_uid);
|
||||
delete _title;
|
||||
delete _content;
|
||||
}
|
||||
|
||||
int SectionContent::uid() const
|
||||
{
|
||||
return _uid;
|
||||
}
|
||||
|
||||
QString SectionContent::uniqueName() const
|
||||
{
|
||||
return _uniqueName;
|
||||
}
|
||||
|
||||
QWidget* SectionContent::titleWidget() const
|
||||
{
|
||||
return _title;
|
||||
}
|
||||
|
||||
QWidget* SectionContent::contentWidget() const
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
|
||||
SectionContent::RefPtr SectionContent::newSectionContent(QWidget* title, QWidget* content)
|
||||
{
|
||||
return QSharedPointer<SectionContent>(new SectionContent(title, content));
|
||||
}
|
||||
|
||||
SectionContent::RefPtr SectionContent::newSectionContent(const QString& title, QWidget* content)
|
||||
{
|
||||
return QSharedPointer<SectionContent>(new SectionContent(new QLabel(title), content));
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
40
AdvancedDockingSystem/src/ads/section_content.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef SECTIONCONTENT_H
|
||||
#define SECTIONCONTENT_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
#include <QHash>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "ads.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
class SectionContent
|
||||
{
|
||||
public:
|
||||
typedef QSharedPointer<SectionContent> RefPtr;
|
||||
|
||||
SectionContent(QWidget* title, QWidget* content, const QString& uniqueName = QString()); ///< Do not use!
|
||||
virtual ~SectionContent();
|
||||
|
||||
int uid() const;
|
||||
QString uniqueName() const;
|
||||
QWidget* titleWidget() const;
|
||||
QWidget* contentWidget() const;
|
||||
|
||||
static RefPtr newSectionContent(QWidget* title, QWidget* content);
|
||||
static RefPtr newSectionContent(const QString& title, QWidget* content);
|
||||
|
||||
public:
|
||||
const int _uid;
|
||||
const QString _uniqueName;
|
||||
QPointer<QWidget> _title;
|
||||
QPointer<QWidget> _content;
|
||||
|
||||
static int NextUid;
|
||||
static QHash<int, SectionContent*> LookupMap;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
18
AdvancedDockingSystem/src/ads/section_content_widget.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "section_content_widget.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
SectionContentWidget::SectionContentWidget(SectionContent::RefPtr c, QWidget* parent) :
|
||||
QFrame(parent),
|
||||
_content(c)
|
||||
{
|
||||
auto l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
l->addWidget(_content->contentWidget());
|
||||
setLayout(l);
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
25
AdvancedDockingSystem/src/ads/section_content_widget.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef SECTION_CONTENT_WIDGET_H
|
||||
#define SECTION_CONTENT_WIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads.h"
|
||||
#include "section_content.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
class SectionWidget;
|
||||
|
||||
class SectionContentWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SectionContentWidget(SectionContent::RefPtr c, QWidget* parent = 0);
|
||||
|
||||
private:
|
||||
SectionContent::RefPtr _content;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
212
AdvancedDockingSystem/src/ads/section_title_widget.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
#include "section_title_widget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <QApplication>
|
||||
#include <QBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QMimeData>
|
||||
#include <QDrag>
|
||||
#include <QCursor>
|
||||
#include <QStyle>
|
||||
|
||||
#ifdef ADS_ANIMATIONS_ENABLED
|
||||
#include <QPropertyAnimation>
|
||||
#include <QParallelAnimationGroup>
|
||||
#endif
|
||||
|
||||
#include "drop_overlay.h"
|
||||
#include "section_content.h"
|
||||
#include "section_widget.h"
|
||||
#include "floating_widget.h"
|
||||
#include "container_widget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
SectionTitleWidget::SectionTitleWidget(SectionContent::RefPtr content, QWidget* parent) :
|
||||
QFrame(parent),
|
||||
_content(content),
|
||||
_activeTab(false)
|
||||
{
|
||||
auto l = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
l->addWidget(content->titleWidget());
|
||||
setLayout(l);
|
||||
}
|
||||
|
||||
bool SectionTitleWidget::isActiveTab() const
|
||||
{
|
||||
return _activeTab;
|
||||
}
|
||||
|
||||
void SectionTitleWidget::setActiveTab(bool active)
|
||||
{
|
||||
if (active != _activeTab)
|
||||
{
|
||||
_activeTab = active;
|
||||
|
||||
style()->unpolish(this);
|
||||
style()->polish(this);
|
||||
update();
|
||||
|
||||
emit activeTabChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SectionTitleWidget::mousePressEvent(QMouseEvent* ev)
|
||||
{
|
||||
// qDebug() << Q_FUNC_INFO << ev->pos();
|
||||
if (ev->button() == Qt::LeftButton)
|
||||
{
|
||||
_dragStartPos = ev->pos();
|
||||
ev->accept();
|
||||
return;
|
||||
}
|
||||
QFrame::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
|
||||
{
|
||||
// qDebug() << Q_FUNC_INFO << ev->pos();
|
||||
if (!_dragStartPos.isNull())
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
// Drop contents of FloatingWidget into SectionWidget.
|
||||
if (_fw)
|
||||
{
|
||||
auto cw = findParentContainerWidget(this);
|
||||
auto sw = cw->sectionAt(cw->mapFromGlobal(ev->globalPos()));
|
||||
if (sw)
|
||||
{
|
||||
auto loc = showDropOverlay(sw);
|
||||
if (loc == InvalidDropArea)
|
||||
{
|
||||
qDebug() << "Invalid drop area";
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(ADS_ANIMATIONS_ENABLED)
|
||||
auto data = _fw->takeContent();
|
||||
_fw->deleteLater();
|
||||
_fw.clear();
|
||||
cw->dropContent(data, sw, loc);
|
||||
#else
|
||||
auto moveAnim = new QPropertyAnimation(_fw, "pos", this);
|
||||
moveAnim->setStartValue(_fw->pos());
|
||||
moveAnim->setEndValue(sw->mapToGlobal(sw->rect().topLeft()));
|
||||
moveAnim->setDuration(ADS_ANIMATION_DURATION);
|
||||
|
||||
auto resizeAnim = new QPropertyAnimation(_fw, "size", this);
|
||||
resizeAnim->setStartValue(_fw->size());
|
||||
resizeAnim->setEndValue(sw->size());
|
||||
resizeAnim->setDuration(ADS_ANIMATION_DURATION);
|
||||
|
||||
auto animGroup = new QParallelAnimationGroup(this);
|
||||
QObject::connect(animGroup, &QPropertyAnimation::finished, [this, sw]()
|
||||
{
|
||||
auto data = _fw->takeContent();
|
||||
_fw->deleteLater();
|
||||
_fw.clear();
|
||||
sw->addContent(data, true);
|
||||
});
|
||||
animGroup->addAnimation(moveAnim);
|
||||
animGroup->addAnimation(resizeAnim);
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// Mouse is over a outer-edge drop area
|
||||
else if (false)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Reset
|
||||
_dragStartPos = QPoint();
|
||||
hideDropOverlay();
|
||||
QFrame::mouseReleaseEvent(ev);
|
||||
}
|
||||
|
||||
void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
|
||||
{
|
||||
ContainerWidget* cw = findParentContainerWidget(this);
|
||||
SectionWidget* section = NULL;
|
||||
|
||||
// Move already existing FloatingWidget
|
||||
if (_fw)
|
||||
{
|
||||
const QPoint moveToPos = ev->globalPos() - (_dragStartPos + QPoint(ADS_WINDOW_FRAME_BORDER_WIDTH, ADS_WINDOW_FRAME_BORDER_WIDTH));
|
||||
_fw->move(moveToPos);
|
||||
|
||||
// Show drop indicator
|
||||
if (true)
|
||||
{
|
||||
// Mouse is over a SectionWidget
|
||||
section = cw->sectionAt(cw->mapFromGlobal(QCursor::pos()));
|
||||
if (section)
|
||||
{
|
||||
showDropOverlay(section);
|
||||
}
|
||||
// Mouse is at the edge of the ContainerWidget
|
||||
// Top, Right, Bottom, Left
|
||||
else if (cw->outerTopDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
qDebug() << "Top area...";
|
||||
}
|
||||
else if (cw->outerRightDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
qDebug() << "Right area...";
|
||||
}
|
||||
else if (cw->outerBottomDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
qDebug() << "Bottom area...";
|
||||
}
|
||||
else if (cw->outerLeftDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
qDebug() << "Left area...";
|
||||
}
|
||||
else
|
||||
{
|
||||
hideDropOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
ev->accept();
|
||||
return;
|
||||
}
|
||||
// Begin to drag title inside the title area to switch its position inside the SectionWidget.
|
||||
else if (!_dragStartPos.isNull() && (ev->buttons() & Qt::LeftButton)
|
||||
&& (section = findParentSectionWidget(this)) != NULL && section->titleAreaGeometry().contains(section->mapFromGlobal(ev->globalPos())))
|
||||
{
|
||||
// qDebug() << "Move inside title area" << ev->pos();
|
||||
}
|
||||
// Begin to drag/float the SectionContent.
|
||||
else if (!_dragStartPos.isNull() && (ev->buttons() & Qt::LeftButton) && (ev->globalPos() - _dragStartPos).manhattanLength() >= QApplication::startDragDistance()
|
||||
&& (section = findParentSectionWidget(this)) != NULL)
|
||||
{
|
||||
// Create floating widget.
|
||||
auto data = section->take(_content->uid(), false);
|
||||
|
||||
_fw = new FloatingWidget(data.content, data.titleWidget, data.contentWidget, cw);
|
||||
_fw->resize(section->size());
|
||||
setActiveTab(false);
|
||||
|
||||
const QPoint moveToPos = ev->globalPos() - (_dragStartPos + QPoint(ADS_WINDOW_FRAME_BORDER_WIDTH, ADS_WINDOW_FRAME_BORDER_WIDTH));
|
||||
_fw->move(moveToPos);
|
||||
_fw->show();
|
||||
|
||||
// Delete old section, if it is empty now.
|
||||
if (section->contents().isEmpty())
|
||||
{
|
||||
delete section;
|
||||
section = NULL;
|
||||
}
|
||||
ev->accept();
|
||||
return;
|
||||
}
|
||||
QFrame::mouseMoveEvent(ev);
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
44
AdvancedDockingSystem/src/ads/section_title_widget.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef SECTION_TITLE_WIDGET_H
|
||||
#define SECTION_TITLE_WIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QPoint>
|
||||
#include "ads.h"
|
||||
#include "section_content.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
class SectionWidget;
|
||||
class FloatingWidget;
|
||||
|
||||
class SectionTitleWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool activeTab MEMBER _activeTab NOTIFY activeTabChanged)
|
||||
|
||||
friend class SectionWidget;
|
||||
|
||||
SectionContent::RefPtr _content;
|
||||
QPointer<FloatingWidget> _fw;
|
||||
QPoint _dragStartPos;
|
||||
|
||||
bool _activeTab;
|
||||
|
||||
public:
|
||||
SectionTitleWidget(SectionContent::RefPtr content, QWidget* parent);
|
||||
|
||||
bool isActiveTab() const;
|
||||
void setActiveTab(bool active);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent* ev);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* ev);
|
||||
virtual void mouseMoveEvent(QMouseEvent* ev);
|
||||
|
||||
signals:
|
||||
void activeTabChanged();
|
||||
void clicked();
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
231
AdvancedDockingSystem/src/ads/section_widget.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
#include "section_widget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBoxLayout>
|
||||
#include <QStackedLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QtWidgets/QSplitter>
|
||||
|
||||
#if defined(ADS_ANIMATIONS_ENABLED)
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#endif
|
||||
|
||||
#include "drop_overlay.h"
|
||||
#include "section_content.h"
|
||||
#include "section_title_widget.h"
|
||||
#include "section_content_widget.h"
|
||||
#include "floating_widget.h"
|
||||
#include "container_widget.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
int SectionWidget::NextUid = 1;
|
||||
QHash<int, SectionWidget*> SectionWidget::LookupMap;
|
||||
|
||||
SectionWidget::SectionWidget(ContainerWidget* parent) :
|
||||
QFrame(parent),
|
||||
_uid(NextUid++),
|
||||
_container(parent)
|
||||
{
|
||||
//setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
auto l = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
setLayout(l);
|
||||
|
||||
_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
_tabsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_tabsLayout->setSpacing(0);
|
||||
_tabsLayout->addStretch(1);
|
||||
l->addLayout(_tabsLayout);
|
||||
|
||||
_contentsLayout = new QStackedLayout();
|
||||
_contentsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_contentsLayout->setSpacing(0);
|
||||
l->addLayout(_contentsLayout, 1);
|
||||
|
||||
#if defined(ADS_ANIMATIONS_ENABLED)
|
||||
auto shadow = new QGraphicsDropShadowEffect(this);
|
||||
shadow->setOffset(0, 0);
|
||||
shadow->setBlurRadius(8);
|
||||
setGraphicsEffect(shadow);
|
||||
#endif
|
||||
|
||||
LookupMap.insert(_uid, this);
|
||||
_container->_sections.append(this);
|
||||
}
|
||||
|
||||
SectionWidget::~SectionWidget()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
LookupMap.remove(_uid);
|
||||
_container->_sections.removeAll(this);
|
||||
|
||||
// Delete empty QSplitter.
|
||||
auto splitter = findParentSplitter(this);
|
||||
if (splitter && splitter->count() == 0)
|
||||
{
|
||||
splitter->deleteLater();
|
||||
qDebug() << "Delete empty splitter";
|
||||
}
|
||||
}
|
||||
|
||||
int SectionWidget::uid() const
|
||||
{
|
||||
return _uid;
|
||||
}
|
||||
|
||||
ContainerWidget* SectionWidget::containerWidget() const
|
||||
{
|
||||
return _container;
|
||||
}
|
||||
|
||||
QRect SectionWidget::titleAreaGeometry() const
|
||||
{
|
||||
return _tabsLayout->geometry();
|
||||
}
|
||||
|
||||
QRect SectionWidget::contentAreaGeometry() const
|
||||
{
|
||||
return _contentsLayout->geometry();
|
||||
}
|
||||
|
||||
void SectionWidget::addContent(SectionContent::RefPtr c)
|
||||
{
|
||||
_contents.append(c);
|
||||
|
||||
auto title = new SectionTitleWidget(c, nullptr);
|
||||
_sectionTitles.append(title);
|
||||
_tabsLayout->insertWidget(_tabsLayout->count() - 1, title);
|
||||
QObject::connect(title, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
|
||||
|
||||
auto content = new SectionContentWidget(c, nullptr);
|
||||
_sectionContents.append(content);
|
||||
_contentsLayout->addWidget(content);
|
||||
|
||||
// Active first TAB.
|
||||
if (_contents.size() == 1)
|
||||
setCurrentIndex(0);
|
||||
// Switch to newest.
|
||||
// else
|
||||
// setCurrentIndex(_contentsLayout->count() - 1);
|
||||
}
|
||||
|
||||
void SectionWidget::addContent(const InternalContentData& data, bool autoActivate)
|
||||
{
|
||||
_contents.append(data.content);
|
||||
|
||||
_sectionTitles.append(data.titleWidget);
|
||||
_tabsLayout->insertWidget(_tabsLayout->count() - 1, data.titleWidget);
|
||||
QObject::connect(data.titleWidget, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
|
||||
|
||||
_sectionContents.append(data.contentWidget);
|
||||
_contentsLayout->addWidget(data.contentWidget);
|
||||
|
||||
// Active first TAB.
|
||||
if (_contents.size() == 1)
|
||||
setCurrentIndex(0);
|
||||
// Switch to newest.
|
||||
else if (autoActivate)
|
||||
setCurrentIndex(_contentsLayout->count() - 1);
|
||||
}
|
||||
|
||||
// take removes a widget from the SectionWidget but does not delete
|
||||
// the used SectionTitle- and SectionContent-Widget. Instead it returns
|
||||
// these objects.
|
||||
InternalContentData SectionWidget::take(int uid, bool del)
|
||||
{
|
||||
InternalContentData data;
|
||||
|
||||
// Find SectionContent.
|
||||
SectionContent::RefPtr sc;
|
||||
int index = -1;
|
||||
for (int i = 0; i < _contents.count(); i++)
|
||||
{
|
||||
if (_contents[i]->uid() != uid)
|
||||
continue;
|
||||
index = i;
|
||||
sc = _contents.takeAt(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// Title wrapper widget (TAB)
|
||||
auto title = _sectionTitles.takeAt(index);
|
||||
if (title)
|
||||
{
|
||||
_tabsLayout->removeWidget(title);
|
||||
title->disconnect(this);
|
||||
if (del)
|
||||
title->deleteLater();
|
||||
}
|
||||
|
||||
// Content wrapper widget (CONTENT)
|
||||
auto content = _sectionContents.takeAt(index);
|
||||
if (content)
|
||||
{
|
||||
_contentsLayout->removeWidget(content);
|
||||
content->disconnect(this);
|
||||
if (del)
|
||||
content->deleteLater();
|
||||
}
|
||||
|
||||
// Select the previous tab as activeTab.
|
||||
if (_contents.size() > 0 && title->isActiveTab())
|
||||
{
|
||||
if (index > 0)
|
||||
setCurrentIndex(index - 1);
|
||||
else
|
||||
setCurrentIndex(0);
|
||||
}
|
||||
|
||||
data.content = sc;
|
||||
data.titleWidget = title;
|
||||
data.contentWidget = content;
|
||||
return data;
|
||||
}
|
||||
|
||||
void SectionWidget::setCurrentIndex(int index)
|
||||
{
|
||||
// Set active TAB.
|
||||
for (int i = 0; i < _tabsLayout->count(); ++i)
|
||||
{
|
||||
auto item = _tabsLayout->itemAt(i);
|
||||
if (item->widget())
|
||||
{
|
||||
auto stw = dynamic_cast<SectionTitleWidget*>(item->widget());
|
||||
if (i == index)
|
||||
stw->setActiveTab(true);
|
||||
else
|
||||
stw->setActiveTab(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Set active CONTENT.
|
||||
_contentsLayout->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void SectionWidget::paintEvent(QPaintEvent* e)
|
||||
{
|
||||
QFrame::paintEvent(e);
|
||||
|
||||
// QPainter p(this);
|
||||
// auto r = rect();
|
||||
// p.drawText(rect(), Qt::AlignCenter | Qt::AlignVCenter, QString("x=%1; y=%2; w=%3; h=%4").arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()));
|
||||
}
|
||||
|
||||
void SectionWidget::onSectionTitleClicked()
|
||||
{
|
||||
auto stw = qobject_cast<SectionTitleWidget*>(sender());
|
||||
if (stw)
|
||||
{
|
||||
auto index = _tabsLayout->indexOf(stw);
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
72
AdvancedDockingSystem/src/ads/section_widget.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef SECTION_WIDGET_H
|
||||
#define SECTION_WIDGET_H
|
||||
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads.h"
|
||||
#include "section_content.h"
|
||||
|
||||
class QBoxLayout;
|
||||
class QStackedLayout;
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
class ContainerWidget;
|
||||
class SectionTitleWidget;
|
||||
class SectionContentWidget;
|
||||
|
||||
// SectionWidget manages multiple instances of SectionContent.
|
||||
// It displays a title TAB, which is clickable and will switch to
|
||||
// the contents associated to the title when clicked.
|
||||
class SectionWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SectionWidget(ContainerWidget* parent);
|
||||
virtual ~SectionWidget();
|
||||
|
||||
int uid() const;
|
||||
ContainerWidget* containerWidget() const;
|
||||
|
||||
QRect titleAreaGeometry() const;
|
||||
QRect contentAreaGeometry() const;
|
||||
|
||||
QList<SectionContent::RefPtr> contents() const { return _contents; }
|
||||
void addContent(SectionContent::RefPtr c);
|
||||
void addContent(const InternalContentData& data, bool autoActivate);
|
||||
InternalContentData take(int uid, bool del = true);
|
||||
|
||||
public slots:
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent*);
|
||||
|
||||
private slots:
|
||||
void onSectionTitleClicked();
|
||||
|
||||
private:
|
||||
const int _uid;
|
||||
|
||||
ContainerWidget* _container;
|
||||
QList<SectionContent::RefPtr> _contents;
|
||||
QList<SectionTitleWidget*> _sectionTitles;
|
||||
QList<SectionContentWidget*> _sectionContents;
|
||||
|
||||
QBoxLayout *_tabsLayout;
|
||||
QStackedLayout *_contentsLayout;
|
||||
|
||||
QPoint _mousePressPoint;
|
||||
SectionContent::RefPtr _mousePressContent;
|
||||
SectionTitleWidget* _mousePressTitleWidget;
|
||||
|
||||
static int NextUid;
|
||||
static QHash<int, SectionWidget*> LookupMap;
|
||||
};
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
#endif
|
33
AdvancedDockingSystemDemo/AdvancedDockingSystemDemo.pro
Normal file
@ -0,0 +1,33 @@
|
||||
TARGET = AdvancedDockingSystemDemo
|
||||
|
||||
QT += core gui
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += \
|
||||
src/main.cpp \
|
||||
src/mainwindow.cpp \
|
||||
src/icontitlewidget.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/mainwindow.h \
|
||||
src/icontitlewidget.h
|
||||
|
||||
FORMS += \
|
||||
src/mainwindow.ui
|
||||
|
||||
|
||||
# Dependency: AdvancedDockingSystem
|
||||
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/release/ -lAdvancedDockingSystem
|
||||
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/debug/ -lAdvancedDockingSystem
|
||||
else:unix: LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/ -lAdvancedDockingSystem
|
||||
|
||||
INCLUDEPATH += $$PWD/../AdvancedDockingSystem/src
|
||||
DEPENDPATH += $$PWD/../AdvancedDockingSystem/src
|
||||
|
||||
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/release/libAdvancedDockingSystem.a
|
||||
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/debug/libAdvancedDockingSystem.a
|
||||
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/release/AdvancedDockingSystem.lib
|
||||
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/debug/AdvancedDockingSystem.lib
|
||||
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/libAdvancedDockingSystem.a
|
32
AdvancedDockingSystemDemo/src/icontitlewidget.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "icontitlewidget.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QStyle>
|
||||
|
||||
IconTitleWidget::IconTitleWidget(const QIcon& icon, const QString& title, QWidget *parent) :
|
||||
QFrame(parent)
|
||||
{
|
||||
auto l = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(l);
|
||||
|
||||
auto titleIcon = new QLabel();
|
||||
if (icon.isNull())
|
||||
{
|
||||
titleIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxInformation).pixmap(16, 16));
|
||||
}
|
||||
else
|
||||
{
|
||||
titleIcon->setPixmap(icon.pixmap(16, 16));
|
||||
}
|
||||
l->addWidget(titleIcon);
|
||||
|
||||
auto titleText = new QLabel(title);
|
||||
auto titleFont = titleText->font();
|
||||
titleFont.setBold(true);
|
||||
titleText->setFont(titleFont);
|
||||
l->addWidget(titleText, 1);
|
||||
}
|
19
AdvancedDockingSystemDemo/src/icontitlewidget.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef ICONTITLEWIDGET_H
|
||||
#define ICONTITLEWIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
class QIcon;
|
||||
class QString;
|
||||
|
||||
class IconTitleWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit IconTitleWidget(const QIcon& icon, const QString& title, QWidget *parent = 0);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // ICONTITLEWIDGET_H
|
70
AdvancedDockingSystemDemo/src/main.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include <QString>
|
||||
#include <QRect>
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
#include "mainwindow.h"
|
||||
|
||||
static void centerWidget(QWidget* widget)
|
||||
{
|
||||
if (widget)
|
||||
{
|
||||
QDesktopWidget deskWidget;
|
||||
const auto screenIndex = deskWidget.primaryScreen();
|
||||
const auto deskRect = deskWidget.availableGeometry(screenIndex);
|
||||
const auto x = (deskRect.width() - widget->rect().width()) / 2;
|
||||
const auto y = (deskRect.height() - widget->rect().height()) / 2;
|
||||
widget->move(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static void resizeWidgetPerCent(QWidget* widget, qreal widthPC, qreal heightPC)
|
||||
{
|
||||
if (widget && widthPC >= 0.0 && heightPC >= 0.0)
|
||||
{
|
||||
QDesktopWidget deskWidget;
|
||||
const auto screenIndex = deskWidget.primaryScreen();
|
||||
const auto deskRect = deskWidget.availableGeometry(screenIndex);
|
||||
const auto w = (deskRect.width() / 100) * widthPC;
|
||||
const auto h = (deskRect.height() / 100) * heightPC;
|
||||
widget->resize(w, h);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
a.setQuitOnLastWindowClosed(true);
|
||||
|
||||
Q_INIT_RESOURCE(ads);
|
||||
|
||||
// Development style.
|
||||
// a.setStyleSheet(""
|
||||
// " QSplitter::handle { border: 1px solid #000000; background: #000000; } "
|
||||
// " ads--ContainerWidget { border: 1px solid #ff0000; background: #FFE6E6; } "
|
||||
// " ads--SectionWidget { border: 1px solid #00ff00; background: #E6FFE6; } "
|
||||
// " ads--SectionTitleWidget { border: 1px solid #0000ff; background: #E6E6FF; } "
|
||||
// " ads--SectionTitleWidget[activeTab=\"true\"] { border: 1px solid #0000ff; background: #9696FF; } "
|
||||
// " ads--SectionContentWidget { border: 1px solid #FFFF00; background: #FFFFE6; } "
|
||||
// );
|
||||
|
||||
// PARTsolutions style.
|
||||
a.setStyleSheet(""
|
||||
" QSplitter::handle:vertical { image: url(:/img/splitter-horizontal.png); } "
|
||||
" QSplitter::handle:horizontal { image: url(:/img/splitter-vertical.png); } "
|
||||
" ads--ContainerWidget { border: 0; background: #9ab6ca; } "
|
||||
" ads--SectionWidget { border-width: 1px; border-color: #ffffff; border-style: solid; background: #7c9eb3; padding: 0; margin: 0; } "
|
||||
" ads--SectionTitleWidget { border-right: 1px solid #E7F3F8; background: #7c9eb3; } "
|
||||
" ads--SectionTitleWidget[activeTab=\"true\"] { border: 1px solid #E7F3F8; background: #E7F3F8; } "
|
||||
" ads--SectionTitleWidget IconTitleWidget QLabel { color: #000000; } "
|
||||
" ads--SectionContentWidget { border: 0px solid #E7F3F8; background: #ffffff; } "
|
||||
" ads--FloatingWidget QPushButton { background: #E7F3F8; } "
|
||||
);
|
||||
|
||||
MainWindow mw;
|
||||
resizeWidgetPerCent(&mw, 60, 80);
|
||||
centerWidget(&mw);
|
||||
mw.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
98
AdvancedDockingSystemDemo/src/mainwindow.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
#include <QCalendarWidget>
|
||||
#include <QFrame>
|
||||
#include <QTreeView>
|
||||
#include <QFileSystemModel>
|
||||
|
||||
#include "ads/container_widget.h"
|
||||
#include "ads/section_widget.h"
|
||||
#include "ads/section_content.h"
|
||||
|
||||
#include "icontitlewidget.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int CONTENT_COUNT = 0;
|
||||
|
||||
static ads::SectionContent::RefPtr createLongTextLabelSC()
|
||||
{
|
||||
auto w = new QLabel();
|
||||
w->setWordWrap(true);
|
||||
w->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
w->setText(QString("Lorem Ipsum ist ein einfacher Demo-Text für die Print- und Schriftindustrie. Lorem Ipsum ist in der Industrie bereits der Standard Demo-Text seit 1500, als ein unbekannter Schriftsteller eine Hand voll Wörter nahm und diese durcheinander warf um ein Musterbuch zu erstellen. Es hat nicht nur 5 Jahrhunderte überlebt, sondern auch in Spruch in die elektronische Schriftbearbeitung geschafft (bemerke, nahezu unverändert). Bekannt wurde es 1960, mit dem erscheinen von Letrase, welches Passagen von Lorem Ipsum enhielt, so wie Desktop Software wie Aldus PageMaker - ebenfalls mit Lorem Ipsum."));
|
||||
return ads::SectionContent::newSectionContent(new IconTitleWidget(QIcon(), QString("Title %1").arg(++CONTENT_COUNT)), w);
|
||||
}
|
||||
|
||||
static ads::SectionContent::RefPtr createCalendarSC()
|
||||
{
|
||||
auto w = new QCalendarWidget();
|
||||
return ads::SectionContent::newSectionContent(new IconTitleWidget(QIcon(), QString("Title %1").arg(++CONTENT_COUNT)), w);
|
||||
}
|
||||
|
||||
static ads::SectionContent::RefPtr createTreeSC()
|
||||
{
|
||||
auto m = new QFileSystemModel();
|
||||
m->setRootPath(QDir::currentPath());
|
||||
auto w = new QTreeView();
|
||||
w->setModel(m);
|
||||
return ads::SectionContent::newSectionContent(new IconTitleWidget(QIcon(), QString("Title %1").arg(++CONTENT_COUNT)), w);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
QObject::connect(ui->actionAddSectionContent, &QAction::triggered, this, &MainWindow::onActionAddSectionContentTriggered);
|
||||
|
||||
// CREATE SOME TESTING DOCKS
|
||||
_container = new ads::ContainerWidget();
|
||||
_container->setOrientation(Qt::Vertical);
|
||||
setCentralWidget(_container);
|
||||
|
||||
auto leftSection = new ads::SectionWidget(_container);
|
||||
leftSection->addContent(createLongTextLabelSC());
|
||||
_container->addSection(leftSection);
|
||||
|
||||
auto middleSection = new ads::SectionWidget(_container);
|
||||
middleSection->addContent(createCalendarSC());
|
||||
_container->addSection(middleSection);
|
||||
|
||||
auto middleBottom = new ads::SectionWidget(_container);
|
||||
middleBottom->addContent(createLongTextLabelSC());
|
||||
_container->addSection(middleBottom);
|
||||
// _container->splitSections(middleSection, middleBottom, Qt::Vertical);
|
||||
|
||||
auto rightSection = new ads::SectionWidget(_container);
|
||||
rightSection->addContent(createLongTextLabelSC());
|
||||
_container->addSection(rightSection);
|
||||
|
||||
// auto middleTopRight = new ads::SectionWidget(_container);
|
||||
// middleTopRight->addContent(createLongTextLabelSC());
|
||||
// _container->splitSections(middleSection, middleTopRight);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::onActionAddSectionContentTriggered()
|
||||
{
|
||||
return;
|
||||
|
||||
// auto titleWidget = new IconTitleWidget(QIcon(), QString("Title"));
|
||||
// auto contentWidget = createRandomWidget(-1, -1);
|
||||
// auto content = ads::SectionContent::newSectionContent(titleWidget, contentWidget);
|
||||
|
||||
// auto section = new ads::SectionWidget(_container);
|
||||
// _container->addSection(section);
|
||||
// section->addContent(content);
|
||||
}
|
31
AdvancedDockingSystemDemo/src/mainwindow.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "ads/ads.h"
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
class ContainerWidget;
|
||||
ADS_NAMESPACE_END
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
virtual ~MainWindow();
|
||||
|
||||
private slots:
|
||||
void onActionAddSectionContentTriggered();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
ads::ContainerWidget* _container;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
46
AdvancedDockingSystemDemo/src/mainwindow.ui
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget"/>
|
||||
<widget class="QToolBar" name="mainToolBar">
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionAddSectionContent"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<action name="actionAddSectionContent">
|
||||
<property name="text">
|
||||
<string>Add SectionContent</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|