Files
KDDockWidgets/src/private/DropArea.cpp
Sergio Martins 86e58dd8a7 Merge MultiSplitterLayout into MultiSplitter
No need to have two. It's a leftover from the old layouting engine
2020-06-11 16:34:28 +01:00

283 lines
9.1 KiB
C++

/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DropArea_p.h"
#include "Logging_p.h"
#include "DockWidgetBase.h"
#include "Draggable_p.h"
#include "FloatingWindow_p.h"
#include "Config.h"
#include "DropIndicatorOverlayInterface_p.h"
#include "FrameworkWidgetFactory.h"
#include "MainWindowBase.h"
#include "multisplitter/Item_p.h"
#include "DockRegistry_p.h"
// #include "indicators/AnimatedIndicators_p.h"
#include "WindowBeingDragged_p.h"
using namespace KDDockWidgets;
/**
* @file
* @brief A MultiSplitter with support for drop indicators when hovering over.
*
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
DropArea::DropArea(QWidgetOrQuick *parent)
: MultiSplitter(parent)
, m_dropIndicatorOverlay(Config::self().frameworkWidgetFactory()->createDropIndicatorOverlay(this))
{
qCDebug(creation) << "DropArea";
}
DropArea::~DropArea()
{
m_inDestructor = true;
qCDebug(creation) << "~DropArea";
}
int DropArea::numFrames() const
{
return visibleCount();
}
Frame *DropArea::frameContainingPos(QPoint globalPos) const
{
const Layouting::Item::List &items = this->items();
for (Layouting::Item *item : items) {
auto frame = static_cast<Frame*>(item->guestAsQObject());
if (!frame || !frame->QWidget::isVisible()) {
continue;
}
if (frame->containsMouse(globalPos))
return frame;
}
return nullptr;
}
Layouting::Item *DropArea::centralFrame() const
{
for (Layouting::Item *item : this->items()) {
if (auto f = static_cast<Frame*>(item->guestAsQObject())) {
if (f->isCentralFrame())
return item;
}
}
return nullptr;
}
void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBase *relativeTo, AddingOption option)
{
if (!dw || dw == relativeTo || location == Location_None) {
qWarning() << Q_FUNC_INFO << "Invalid parameters" << dw << relativeTo << location;
return;
}
if ((option & AddingOption_StartHidden) && dw->frame() != nullptr) {
// StartHidden is just to be used at startup, not to moving stuff around
qWarning() << Q_FUNC_INFO << "Dock widget already exists in the layout";
return;
}
if (!validateAffinity(dw))
return;
Frame *frame = nullptr;
Frame *relativeToFrame = relativeTo ? relativeTo->frame() : nullptr;
dw->saveLastFloatingGeometry();
// Check if the dock widget already exists in the layout
if (contains(dw)) {
Frame *oldFrame = dw->frame();
if (oldFrame->hasSingleDockWidget()) {
Q_ASSERT(oldFrame->contains(dw));
// The frame only has this dock widget, and the frame is already in the layout. So move the frame instead
frame = oldFrame;
} else {
frame = Config::self().frameworkWidgetFactory()->createFrame();
frame->addWidget(dw);
}
} else {
frame = Config::self().frameworkWidgetFactory()->createFrame();
frame->addWidget(dw);
}
if (option & AddingOption_StartHidden) {
addWidget(dw, location, relativeToFrame, DefaultSizeMode::Fair, option);
} else {
addWidget(frame, location, relativeToFrame, DefaultSizeMode::Fair, option);
}
}
bool DropArea::contains(DockWidgetBase *dw) const
{
return dw->frame() && MultiSplitter::contains(dw->frame());
}
QStringList DropArea::affinities() const
{
if (auto mw = mainWindow()) {
return mw->affinities();
} else if (auto fw = floatingWindow()) {
return fw->affinities();
}
return {};
}
void DropArea::layoutParentContainerEqually(DockWidgetBase *dw)
{
Layouting::Item *item = itemForFrame(dw->frame());
if (!item) {
qWarning() << Q_FUNC_INFO << "Item not found for" << dw << dw->frame();
return;
}
layoutEqually(item->parentContainer());
}
void DropArea::hover(FloatingWindow *floatingWindow, QPoint globalPos)
{
if (!validateAffinity(floatingWindow))
return;
Frame *frame = frameContainingPos(globalPos); // Frame is nullptr if MainWindowOption_HasCentralFrame isn't set
m_dropIndicatorOverlay->setWindowBeingDragged(floatingWindow);
m_dropIndicatorOverlay->setHoveredFrame(frame);
m_dropIndicatorOverlay->hover(globalPos);
}
static bool isOutterLocation(DropIndicatorOverlayInterface::DropLocation location)
{
switch (location) {
case DropIndicatorOverlayInterface::DropLocation_OutterLeft:
case DropIndicatorOverlayInterface::DropLocation_OutterTop:
case DropIndicatorOverlayInterface::DropLocation_OutterRight:
case DropIndicatorOverlayInterface::DropLocation_OutterBottom:
return true;
default:
return false;
}
}
bool DropArea::drop(FloatingWindow *droppedWindow, QPoint globalPos)
{
if (droppedWindow == window()) {
qWarning() << "Refusing to drop onto itself"; // Doesn't happen
return false;
}
if (m_dropIndicatorOverlay->currentDropLocation() == DropIndicatorOverlayInterface::DropLocation_None) {
qCDebug(hovering) << "DropArea::drop: bailing out, drop location = none";
return false;
}
qCDebug(dropping) << "DropArea::drop:" << droppedWindow;
hover(droppedWindow, globalPos);
Frame *acceptingFrame = m_dropIndicatorOverlay->hoveredFrame();
if (!(acceptingFrame || isOutterLocation(m_dropIndicatorOverlay->currentDropLocation()))) {
qWarning() << "DropArea::drop: asserted with frame=" << acceptingFrame << "; Location=" << m_dropIndicatorOverlay->currentDropLocation();
return false;
}
bool result = true;
auto droploc = m_dropIndicatorOverlay->currentDropLocation();
switch (droploc) {
case DropIndicatorOverlayInterface::DropLocation_Left:
case DropIndicatorOverlayInterface::DropLocation_Top:
case DropIndicatorOverlayInterface::DropLocation_Bottom:
case DropIndicatorOverlayInterface::DropLocation_Right:
result = drop(droppedWindow, DropIndicatorOverlayInterface::multisplitterLocationFor(droploc), acceptingFrame);
break;
case DropIndicatorOverlayInterface::DropLocation_OutterLeft:
case DropIndicatorOverlayInterface::DropLocation_OutterTop:
case DropIndicatorOverlayInterface::DropLocation_OutterRight:
case DropIndicatorOverlayInterface::DropLocation_OutterBottom:
result = drop(droppedWindow, DropIndicatorOverlayInterface::multisplitterLocationFor(droploc), nullptr);
break;
case DropIndicatorOverlayInterface::DropLocation_Center:
qCDebug(hovering) << "Tabbing" << droppedWindow << "into" << acceptingFrame;
if (!validateAffinity(droppedWindow))
return false;
acceptingFrame->addWidget(droppedWindow);
break;
default:
qWarning() << "DropArea::drop: Unexpected drop location" << m_dropIndicatorOverlay->currentDropLocation();
result = false;
break;
}
if (result)
raiseAndActivate();
return result;
}
bool DropArea::drop(QWidgetOrQuick *droppedWindow, KDDockWidgets::Location location, Frame *relativeTo)
{
qCDebug(docking) << "DropArea::addFrame";
if (auto dock = qobject_cast<DockWidgetBase *>(droppedWindow)) {
if (!validateAffinity(dock))
return false;
auto frame = Config::self().frameworkWidgetFactory()->createFrame();
frame->addWidget(dock);
addWidget(frame, location, relativeTo, DefaultSizeMode::FairButFloor);
} else if (auto floatingWindow = qobject_cast<FloatingWindow *>(droppedWindow)) {
if (!validateAffinity(floatingWindow))
return false;
addMultiSplitter(floatingWindow->dropArea(), location, relativeTo);
floatingWindow->scheduleDeleteLater();
return true;
} else {
qWarning() << "Unknown dropped widget" << droppedWindow;
return false;
}
return true;
}
void DropArea::removeHover()
{
m_dropIndicatorOverlay->setWindowBeingDragged(nullptr);
m_dropIndicatorOverlay->setCurrentDropLocation(DropIndicatorOverlayInterface::DropLocation_None);
}
template<typename T>
bool DropArea::validateAffinity(T *window) const
{
if (!DockRegistry::self()->affinitiesMatch(window->affinities(), affinities())) {
// Commented the warning, so we don't warn when hovering over
//qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
//<< window->affinityName() << affinityName();
return false;
}
return true;
}