AdaptixC2-Mod0/Libs/kddockwidgets/core/DropIndicatorOverlay.cpp
2026-04-06 00:20:51 -05:00

263 lines
7.8 KiB
C++

/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "core/DropIndicatorOverlay.h"
#include "core/DropIndicatorOverlay_p.h"
#include "Config.h"
#include "Platform.h"
#include "ViewFactory.h"
#include "core/DropArea.h"
#include "core/Group.h"
#include "core/Logging_p.h"
#include "core/Controller_p.h"
#include "core/WindowBeingDragged_p.h"
#include "core/DragController_p.h"
#include "core/DockRegistry_p.h"
using namespace KDDockWidgets;
using namespace KDDockWidgets::Core;
DropIndicatorOverlay::DropIndicatorOverlay(DropArea *dropArea, View *view)
: Controller(ViewType::DropAreaIndicatorOverlay, view)
, d(new Private())
, m_dropArea(dropArea)
{
setVisible(false);
view->setViewName(QStringLiteral("DropIndicatorOverlay"));
// Set transparent for mouse events so that topLevel->childAt() never returns the drop indicator
// overlay
view->enableAttribute(Qt::WA_TransparentForMouseEvents);
d->dropIndicatorsInhibitedConnection = DockRegistry::self()->dptr()->dropIndicatorsInhibitedChanged.connect([this](bool inhibited) {
if (inhibited) {
removeHover();
} else {
// Re-add hover. Fastest way is simply faking a mouse move
if (auto state = object_cast<StateDragging *>(DragController::instance()->activeState())) {
state->handleMouseMove(Platform::instance()->cursorPos());
}
}
if (WindowBeingDragged *wbd = DragController::instance()->windowBeingDragged()) {
/// Don't use transparency if drop areas are inhibited
wbd->updateTransparency(!inhibited);
}
});
}
DropIndicatorOverlay::DropIndicatorOverlay(Core::DropArea *dropArea)
: DropIndicatorOverlay(dropArea, Platform::instance()->createView(this, dropArea->view()))
{
}
DropIndicatorOverlay::~DropIndicatorOverlay()
{
delete d;
}
void DropIndicatorOverlay::setWindowBeingDragged(bool is)
{
if (is == m_draggedWindowIsHovering)
return;
m_draggedWindowIsHovering = is;
if (is) {
view()->setGeometry(m_dropArea->rect());
view()->raise();
} else {
setHoveredGroup(nullptr);
}
setVisible(is);
updateVisibility();
}
Rect DropIndicatorOverlay::hoveredGroupRect() const
{
return m_hoveredGroupRect;
}
void DropIndicatorOverlay::setHoveredGroup(Core::Group *group)
{
if (group == m_hoveredGroup)
return;
// if (WindowBeingDragged *wbd = DragController::instance()->windowBeingDragged()) {
// if (wbd->isInWaylandDrag(group)) {
// // With wayland, we don't detach the group before the mouse release.
// // Instead, we start a QDrag, with this group as cursor QPixmap.
// // Here we catch the case where we're dragging the pixmap onto the group
// // where're already dragging. That's a no-op.
// return;
// }
// }
if (m_hoveredGroup)
d->groupConnection = KDBindings::ScopedConnection();
m_hoveredGroup = group;
if (m_hoveredGroup) {
d->groupConnection = group->Controller::dptr()->aboutToBeDeleted.connect([this] { onGroupDestroyed(); });
setHoveredGroupRect(m_hoveredGroup->view()->geometry());
} else {
setHoveredGroupRect(Rect());
}
updateVisibility();
d->hoveredGroupChanged.emit(m_hoveredGroup);
onHoveredGroupChanged(m_hoveredGroup);
}
bool DropIndicatorOverlay::isHovered() const
{
return m_draggedWindowIsHovering;
}
DropLocation DropIndicatorOverlay::currentDropLocation() const
{
return m_currentDropLocation;
}
KDDockWidgets::Location DropIndicatorOverlay::multisplitterLocationFor(DropLocation dropLoc)
{
switch (dropLoc) {
case DropLocation_None:
return KDDockWidgets::Location_None;
case DropLocation_Left:
case DropLocation_OutterLeft:
return KDDockWidgets::Location_OnLeft;
case DropLocation_OutterTop:
case DropLocation_Top:
return KDDockWidgets::Location_OnTop;
case DropLocation_OutterRight:
case DropLocation_Right:
return KDDockWidgets::Location_OnRight;
case DropLocation_OutterBottom:
case DropLocation_Bottom:
return KDDockWidgets::Location_OnBottom;
case DropLocation_Center:
return KDDockWidgets::Location_None;
case DropLocation_Inner:
case DropLocation_Outter:
case DropLocation_Horizontal:
case DropLocation_Vertical:
KDDW_ERROR("Unexpected drop location={}", dropLoc);
break;
}
return KDDockWidgets::Location_None;
}
bool DropIndicatorOverlay::dropIndicatorVisible(DropLocation dropLoc) const
{
if (dropLoc == DropLocation_None)
return false;
WindowBeingDragged *windowBeingDragged = DragController::instance()->windowBeingDragged();
if (!windowBeingDragged)
return false;
const Core::DockWidget::List source = windowBeingDragged->dockWidgets();
const Core::DockWidget::List target =
m_hoveredGroup ? m_hoveredGroup->dockWidgets() : Core::DockWidget::List();
const bool isInner = dropLoc & DropLocation_Inner;
const bool isOutter = dropLoc & DropLocation_Outter;
if (isInner) {
if (!m_hoveredGroup)
return false;
} else if (isOutter) {
// If there's only 1 group in the layout, the outer indicators are redundant, as they do the
// same thing as the internal ones. But there might be another window obscuring our target,
// so it's useful to show the outer indicators in this case
const bool isTheOnlyGroup = m_hoveredGroup && m_hoveredGroup->isTheOnlyGroup();
if (isTheOnlyGroup
&& !DockRegistry::self()->isProbablyObscured(m_hoveredGroup->view()->window(),
windowBeingDragged))
return false;
} else if (dropLoc == DropLocation_Center) {
if (!m_hoveredGroup || !m_hoveredGroup->isDockable())
return false;
// Only allow to dock to center if the affinities match
if (!DockRegistry::self()->affinitiesMatch(m_hoveredGroup->affinities(),
windowBeingDragged->affinities()))
return false;
} else {
KDDW_ERROR("Unknown drop indicator location={}", dropLoc);
return false;
}
if (auto dropIndicatorAllowedFunc = Config::self().dropIndicatorAllowedFunc()) {
DropArea *dropArea = DragController::instance()->dropAreaUnderCursor();
if (!dropIndicatorAllowedFunc(dropLoc, source, target, dropArea))
return false;
}
return true;
}
void DropIndicatorOverlay::onGroupDestroyed()
{
setHoveredGroup(nullptr);
}
DropIndicatorOverlay::Private *DropIndicatorOverlay::dptr() const
{
return d;
}
void DropIndicatorOverlay::onHoveredGroupChanged(Core::Group *)
{
}
void DropIndicatorOverlay::setCurrentDropLocation(DropLocation location)
{
if (m_currentDropLocation != location) {
m_currentDropLocation = location;
d->currentDropLocationChanged.emit();
}
}
DropLocation DropIndicatorOverlay::hover(Point globalPos)
{
const DropLocation loc = hover_impl(globalPos);
setCurrentDropLocation(loc);
return loc;
}
void DropIndicatorOverlay::setHoveredGroupRect(Rect rect)
{
if (m_hoveredGroupRect != rect) {
m_hoveredGroupRect = rect;
d->hoveredGroupRectChanged.emit();
}
}
void DropIndicatorOverlay::removeHover()
{
setWindowBeingDragged(false);
setCurrentDropLocation(DropLocation_None);
}
Group *DropIndicatorOverlay::hoveredGroup() const
{
return m_hoveredGroup;
}
void DropIndicatorOverlay::updateVisibility()
{
}