511 lines
13 KiB
C++
511 lines
13 KiB
C++
|
|
||
|
|
||
|
#include "tdockwindows.h"
|
||
|
#include <QBoxLayout>
|
||
|
#include <QVBoxLayout>
|
||
|
|
||
|
#include <QRegion>
|
||
|
|
||
|
#include <QPainter>
|
||
|
#include <QStyleOption>
|
||
|
|
||
|
#include <QMouseEvent>
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
//-------------------
|
||
|
// Decorations
|
||
|
//-------------------
|
||
|
|
||
|
class TDockDecoAllocator : public DockDecoAllocator
|
||
|
{
|
||
|
DockSeparator *newSeparator(DockLayout *owner, bool orientation, Region *parentRegion);
|
||
|
DockPlaceholder *newPlaceholder(DockWidget *owner, Region *r, int idx, int attributes);
|
||
|
};
|
||
|
|
||
|
//========================================================================
|
||
|
|
||
|
//-----------------------
|
||
|
// TMainWindow
|
||
|
//-----------------------
|
||
|
|
||
|
TMainWindow::TMainWindow(QWidget *parent, Qt::WindowFlags flags)
|
||
|
: QWidget(parent, flags)
|
||
|
{
|
||
|
//Delete on close
|
||
|
setAttribute(Qt::WidgetAttribute(Qt::WA_DeleteOnClose));
|
||
|
|
||
|
//Set a vertical layout to include menu bars
|
||
|
QVBoxLayout *vlayout = new QVBoxLayout;
|
||
|
vlayout->setMargin(0);
|
||
|
vlayout->setSpacing(4);
|
||
|
setLayout(vlayout);
|
||
|
|
||
|
//Allocate the dock layout
|
||
|
m_layout = new DockLayout;
|
||
|
m_layout->setContentsMargins(0, 0, 0, 0);
|
||
|
m_layout->setSpacing(8);
|
||
|
m_layout->setDecoAllocator(new TDockDecoAllocator);
|
||
|
vlayout->addLayout(m_layout);
|
||
|
vlayout->setAlignment(m_layout, Qt::AlignTop);
|
||
|
|
||
|
show(); //NOTA: E' NECESSARIO MOSTRARE LA FINESTRA, prima di dockare qualcosa (altrimenti non viene fatto
|
||
|
//l'update della geometria della main window, e il contentsRect del layout viene sballato!!).
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
TMainWindow::~TMainWindow()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
//!Adds input \b item to this TMainWindow. If item was already
|
||
|
//!assigned to \b this TMainWindow, nothing happens.
|
||
|
void TMainWindow::addDockWidget(TDockWidget *item)
|
||
|
{
|
||
|
if (!m_layout->find(item))
|
||
|
m_layout->addWidget(item);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TMainWindow::removeDockWidget(TDockWidget *item)
|
||
|
{
|
||
|
m_layout->removeWidget(item);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
//NOTE: Unlike QMainWindow::addToolBar, we only allow one
|
||
|
//fixed-size undockable menu bar at top of the dock layout.
|
||
|
void TMainWindow::setMenuWidget(QWidget *menubar)
|
||
|
{
|
||
|
if (menubar) {
|
||
|
QVBoxLayout *vlayout = static_cast<QVBoxLayout *>(layout());
|
||
|
|
||
|
//If necessary, remove current menu bar
|
||
|
if (m_menu && m_menu != menubar)
|
||
|
vlayout->removeWidget(m_menu);
|
||
|
|
||
|
vlayout->insertWidget(0, menubar);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TMainWindow::setDecoAllocator(DockDecoAllocator *allocator)
|
||
|
{
|
||
|
m_layout->setDecoAllocator(allocator);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
//!Sets global thickness of separators between dock widget.
|
||
|
void TMainWindow::setSeparatorsThickness(int thick)
|
||
|
{
|
||
|
if (thick > 0) {
|
||
|
m_layout->setSpacing(thick);
|
||
|
m_layout->redistribute();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TMainWindow::resizeEvent(QResizeEvent *event)
|
||
|
{
|
||
|
m_layout->redistribute();
|
||
|
}
|
||
|
|
||
|
//========================================================================
|
||
|
|
||
|
//-------------------
|
||
|
// TDockWidget
|
||
|
//-------------------
|
||
|
|
||
|
//!Constructs a TDockWidget with given parent and window flags. If parent is
|
||
|
//!a TMainWindow, then the constructed dock widget is assigned to it (addDockWidget'd).
|
||
|
//!TDockWidgets are always floating at construction.
|
||
|
TDockWidget::TDockWidget(QWidget *parent, Qt::WindowFlags flags)
|
||
|
: DockWidget(parent, flags), m_widget(0), m_titlebar(0), m_margin(5)
|
||
|
{
|
||
|
setAutoFillBackground(false);
|
||
|
//setFrameStyle(QFrame::StyledPanel);
|
||
|
|
||
|
QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||
|
layout->setSpacing(0);
|
||
|
setLayout(layout);
|
||
|
|
||
|
//Check if parent is a TMainWindow class
|
||
|
TMainWindow *parentMain = qobject_cast<TMainWindow *>(parent);
|
||
|
if (parentMain)
|
||
|
parentMain->addDockWidget(this);
|
||
|
|
||
|
setDecoAllocator(new TDockDecoAllocator);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
TDockWidget::TDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
|
||
|
: DockWidget(parent, flags), m_widget(0), m_titlebar(0), m_margin(5)
|
||
|
{
|
||
|
setWindowTitle(title);
|
||
|
|
||
|
QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||
|
layout->setSpacing(0);
|
||
|
setLayout(layout);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TDockWidget::setTitleBarWidget(QWidget *titlebar)
|
||
|
{
|
||
|
if (titlebar) {
|
||
|
QBoxLayout *boxLayout = static_cast<QBoxLayout *>(layout());
|
||
|
|
||
|
if (m_titlebar && m_titlebar != titlebar)
|
||
|
boxLayout->removeWidget(m_titlebar);
|
||
|
|
||
|
boxLayout->insertWidget(0, titlebar);
|
||
|
//Set top/left-aligned
|
||
|
boxLayout->setAlignment(titlebar, getOrientation() == vertical ? Qt::AlignTop : Qt::AlignLeft);
|
||
|
|
||
|
m_titlebar = titlebar;
|
||
|
if (m_floating)
|
||
|
setFloatingAppearance();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TDockWidget::windowTitleEvent(QEvent *e)
|
||
|
{
|
||
|
if (m_titlebar)
|
||
|
m_titlebar->update();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TDockWidget::setWidget(QWidget *widget)
|
||
|
{
|
||
|
if (widget) {
|
||
|
QBoxLayout *boxLayout = static_cast<QBoxLayout *>(layout());
|
||
|
|
||
|
if (m_widget && m_widget != widget)
|
||
|
boxLayout->removeWidget(m_widget);
|
||
|
|
||
|
boxLayout->insertWidget(1, widget);
|
||
|
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||
|
|
||
|
m_widget = widget;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TDockWidget::setFloatingAppearance()
|
||
|
{
|
||
|
if (m_titlebar) {
|
||
|
//If has a custom title bar, impose a margin to the layout
|
||
|
//to provide a frame.
|
||
|
layout()->setMargin(m_margin);
|
||
|
|
||
|
if (!m_floating) //was docked
|
||
|
{
|
||
|
//Adding margin to extremal sizes
|
||
|
int addition = 2 * m_margin;
|
||
|
setMinimumSize(QSize(minimumWidth() + addition, minimumHeight() + addition));
|
||
|
setMaximumSize(QSize(maximumWidth() + addition, maximumHeight() + addition));
|
||
|
}
|
||
|
}
|
||
|
//else
|
||
|
// setWindowFlags(Qt::Tool);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
void TDockWidget::setDockedAppearance()
|
||
|
{
|
||
|
//No layout margin is visible when docked
|
||
|
layout()->setMargin(0);
|
||
|
|
||
|
if (m_floating) //was floating
|
||
|
{
|
||
|
//Removing margin from extremal sizes
|
||
|
int addition = 2 * m_margin;
|
||
|
setMinimumSize(QSize(minimumWidth() - addition, minimumHeight() - addition));
|
||
|
setMaximumSize(QSize(maximumWidth() - addition, maximumHeight() - addition));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
bool TDockWidget::isDragGrip(QPoint p)
|
||
|
{
|
||
|
if (!m_titlebar)
|
||
|
return DockWidget::isDragGrip(p);
|
||
|
|
||
|
return m_titlebar->geometry().contains(p);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
int TDockWidget::isResizeGrip(QPoint p)
|
||
|
{
|
||
|
if (m_dragging || (!m_titlebar && m_floating))
|
||
|
return 0;
|
||
|
|
||
|
int marginType = 0;
|
||
|
QRect geom(QPoint(0, 0), QPoint(width(), height()));
|
||
|
int margin = layout()->margin();
|
||
|
QRect contGeom(geom.adjusted(margin, margin, -margin, -margin));
|
||
|
|
||
|
if (geom.contains(p) && !contGeom.contains(p)) {
|
||
|
if (p.x() < 15)
|
||
|
marginType |= leftMargin;
|
||
|
if (p.y() < 15)
|
||
|
marginType |= topMargin;
|
||
|
if (p.x() > width() - 15)
|
||
|
marginType |= rightMargin;
|
||
|
if (p.y() > height() - 15)
|
||
|
marginType |= bottomMargin;
|
||
|
}
|
||
|
|
||
|
return marginType;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
//!Currently working only for \b status = true. If you need to
|
||
|
//!dock a TDockWidget, you \b must specify a dock location by either
|
||
|
//!choosing a placeholder or identifying the Region and insertion index,
|
||
|
//!and then running 'parentLayout()->dockItem(..)'.
|
||
|
void TDockWidget::setFloating(bool status)
|
||
|
{
|
||
|
if (status) {
|
||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||
|
if (!m_floating)
|
||
|
parentLayout()->undockItem(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
//!Specifies the orientation of the dock widget. It can
|
||
|
//!be either \b vertical (default) or \b horizontal, meaning that
|
||
|
//!the titlebar is laid respectively at the top or left side of
|
||
|
//!content widget. Directly speaking, it is equivalent to setting the
|
||
|
//!Qt's QDockWidget::DockWidgetVerticalTitleBar feature.
|
||
|
void TDockWidget::setOrientation(bool direction)
|
||
|
{
|
||
|
QBoxLayout *boxLayout = static_cast<QBoxLayout *>(layout());
|
||
|
QBoxLayout::Direction boxDirection;
|
||
|
|
||
|
if (direction == vertical) {
|
||
|
boxLayout->setAlignment(m_titlebar, Qt::AlignTop);
|
||
|
boxDirection = QBoxLayout::TopToBottom;
|
||
|
} else {
|
||
|
boxLayout->setAlignment(m_titlebar, Qt::AlignLeft);
|
||
|
boxDirection = QBoxLayout::LeftToRight;
|
||
|
}
|
||
|
|
||
|
boxLayout->setDirection(boxDirection);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
bool TDockWidget::getOrientation() const
|
||
|
{
|
||
|
QBoxLayout *boxLayout = static_cast<QBoxLayout *>(layout());
|
||
|
|
||
|
return (boxLayout->direction() == QBoxLayout::TopToBottom) ? vertical : horizontal;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
//!Maximizes \b this TDockWidget, if docked.
|
||
|
void TDockWidget::setMaximized(bool status)
|
||
|
{
|
||
|
parentLayout()->setMaximized(this, status);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
QSize TDockWidget::getDockedMinimumSize()
|
||
|
{
|
||
|
int addedSize = 2 * m_margin;
|
||
|
return m_floating ? minimumSize() -= QSize(addedSize, addedSize) : minimumSize();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
QSize TDockWidget::getDockedMaximumSize()
|
||
|
{
|
||
|
int addedSize = 2 * m_margin;
|
||
|
return m_floating ? maximumSize() -= QSize(addedSize, addedSize) : maximumSize();
|
||
|
}
|
||
|
|
||
|
//========================================================================
|
||
|
|
||
|
//--------------------------
|
||
|
// Custom Decorations
|
||
|
//--------------------------
|
||
|
|
||
|
class TDockSeparator : public DockSeparator
|
||
|
{
|
||
|
public:
|
||
|
TDockSeparator(DockLayout *owner, bool orientation, Region *parentRegion)
|
||
|
: DockSeparator(owner, orientation, parentRegion) {}
|
||
|
|
||
|
void paintEvent(QPaintEvent *pe);
|
||
|
};
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
class TDockPlaceholder : public DockPlaceholder
|
||
|
{
|
||
|
QWidget *m_associated[3];
|
||
|
|
||
|
public:
|
||
|
TDockPlaceholder(DockWidget *owner, Region *r, int idx, int attributes);
|
||
|
~TDockPlaceholder();
|
||
|
|
||
|
void buildGeometry();
|
||
|
|
||
|
void showEvent(QShowEvent *se);
|
||
|
void hideEvent(QHideEvent *he);
|
||
|
};
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
TDockPlaceholder::TDockPlaceholder(DockWidget *owner, Region *r, int idx, int attributes)
|
||
|
: DockPlaceholder(owner, r, idx, attributes)
|
||
|
{
|
||
|
setAutoFillBackground(true);
|
||
|
|
||
|
setObjectName("TDockPlaceholder");
|
||
|
|
||
|
setWindowOpacity(0.8);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
TDockPlaceholder::~TDockPlaceholder()
|
||
|
{
|
||
|
if (isRoot()) {
|
||
|
delete m_associated[0];
|
||
|
delete m_associated[1];
|
||
|
delete m_associated[2];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
inline void TDockPlaceholder::buildGeometry()
|
||
|
{
|
||
|
DockPlaceholder::buildGeometry();
|
||
|
|
||
|
if (isRoot()) {
|
||
|
//Solution 2: Set associated widgets
|
||
|
QRect geom(geometry());
|
||
|
QSize horSize(geom.width(), 6);
|
||
|
QSize vertSize(6, geom.height() + 12);
|
||
|
|
||
|
setGeometry(QRect(geom.topLeft() - QPoint(6, 6), vertSize));
|
||
|
|
||
|
m_associated[0] = new TDockPlaceholder(0, 0, 0, 0);
|
||
|
m_associated[0]->setGeometry(QRect(geom.topLeft() - QPoint(0, 6), horSize));
|
||
|
|
||
|
m_associated[1] = new TDockPlaceholder(0, 0, 0, 0);
|
||
|
m_associated[1]->setGeometry(QRect(geom.topRight() + QPoint(1, -6), vertSize));
|
||
|
|
||
|
m_associated[2] = new TDockPlaceholder(0, 0, 0, 0);
|
||
|
m_associated[2]->setGeometry(QRect(geom.bottomLeft() + QPoint(0, 1), horSize));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------
|
||
|
|
||
|
void TDockPlaceholder::showEvent(QShowEvent *se)
|
||
|
{
|
||
|
if (isRoot()) {
|
||
|
//Show associated widgets
|
||
|
m_associated[0]->show();
|
||
|
m_associated[1]->show();
|
||
|
m_associated[2]->show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------
|
||
|
|
||
|
void TDockPlaceholder::hideEvent(QHideEvent *he)
|
||
|
{
|
||
|
if (isRoot()) {
|
||
|
//Show associated widgets
|
||
|
m_associated[0]->hide();
|
||
|
m_associated[1]->hide();
|
||
|
m_associated[2]->hide();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------
|
||
|
|
||
|
void TDockSeparator::paintEvent(QPaintEvent *pe)
|
||
|
{
|
||
|
QPainter p(this);
|
||
|
QStyleOption opt(0);
|
||
|
opt.state = (getOrientation() == Region::horizontal) ? QStyle::State_None : QStyle::State_Horizontal;
|
||
|
|
||
|
/*if (w->isEnabled())
|
||
|
opt.state |= QStyle::State_Enabled;
|
||
|
if (o != Qt::Horizontal)
|
||
|
opt.state |= QStyle::State_Horizontal;
|
||
|
if (mouse_over)
|
||
|
opt.state |= QStyle::State_MouseOver;*/
|
||
|
|
||
|
opt.rect = QRect(QPoint(0, 0), QSize(geometry().size()));
|
||
|
opt.palette = palette();
|
||
|
|
||
|
style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, &p, this);
|
||
|
|
||
|
p.end();
|
||
|
}
|
||
|
|
||
|
//-------------------------------------
|
||
|
|
||
|
DockSeparator *TDockDecoAllocator::newSeparator(DockLayout *owner, bool orientation, Region *parentRegion)
|
||
|
{
|
||
|
return new TDockSeparator(owner, orientation, parentRegion);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------
|
||
|
|
||
|
DockPlaceholder *TDockDecoAllocator::newPlaceholder(DockWidget *owner, Region *r, int idx, int attributes)
|
||
|
{
|
||
|
return new TDockPlaceholder(owner, r, idx, attributes);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------
|
||
|
|
||
|
void TDockWidget::selectDockPlaceholder(QMouseEvent *me)
|
||
|
{
|
||
|
if (m_placeholders.size() && m_placeholders[0]->isRoot()) {
|
||
|
DockPlaceholder *selected = 0;
|
||
|
|
||
|
QPoint pos = parentWidget()->mapFromGlobal(me->globalPos());
|
||
|
if (parentLayout()->contentsRect().contains(pos))
|
||
|
selected = m_placeholders[0];
|
||
|
|
||
|
if (m_selectedPlace != selected) {
|
||
|
if (m_selectedPlace)
|
||
|
m_selectedPlace->hide();
|
||
|
if (selected)
|
||
|
selected->show();
|
||
|
}
|
||
|
|
||
|
m_selectedPlace = selected;
|
||
|
} else
|
||
|
DockWidget::selectDockPlaceholder(me);
|
||
|
}
|