transferred the docking implementation to DropController
This commit is contained in:
parent
7d7e770df5
commit
4e835dd2eb
10 changed files with 482 additions and 466 deletions
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "docksetup.h"
|
||||
|
||||
#include "internal/dragcontroller.h"
|
||||
#include "internal/dropcontroller.h"
|
||||
#include "internal/dockseparator.h"
|
||||
#include "internal/dockframemodel.h"
|
||||
#include "internal/dockwindowactionscontroller.h"
|
||||
|
@ -51,7 +51,7 @@ class DockWidgetFactory : public KDDockWidgets::DefaultWidgetFactory
|
|||
public:
|
||||
KDDockWidgets::DropIndicatorOverlayInterface* createDropIndicatorOverlay(KDDockWidgets::DropArea* dropArea) const override
|
||||
{
|
||||
return new DragController(dropArea);
|
||||
return new DropController(dropArea);
|
||||
}
|
||||
|
||||
Layouting::Separator* createSeparator(Layouting::Widget* parent = nullptr) const override
|
||||
|
|
|
@ -44,8 +44,8 @@ set (DOCKWINDOW_SRC
|
|||
${CMAKE_CURRENT_LIST_DIR}/dockcentralview.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dockbase.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dockbase.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dragcontroller.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dragcontroller.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dropcontroller.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dropcontroller.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dockseparator.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dockseparator.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/dockframemodel.cpp
|
||||
|
|
|
@ -40,35 +40,6 @@
|
|||
using namespace mu::dock;
|
||||
using namespace mu::async;
|
||||
|
||||
static constexpr double MAX_DISTANCE_TO_HOLDER = 50;
|
||||
|
||||
static bool isPointAllowedForDrop(const QPoint& point, const DropDestination& dropDestination)
|
||||
{
|
||||
QRect dropRect = dropDestination.dock->frameGeometry();
|
||||
|
||||
if (!dropRect.contains(point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dropDestination.dropDistance == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dropDestination.dropLocation == Location::Left) {
|
||||
if (std::abs(dropRect.left() - point.x()) <= dropDestination.dropDistance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dropDestination.dropLocation == Location::Right) {
|
||||
if (std::abs(dropRect.right() - point.x()) <= dropDestination.dropDistance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DockWindow::DockWindow(QQuickItem* parent)
|
||||
: QQuickItem(parent),
|
||||
m_toolBars(this),
|
||||
|
@ -231,39 +202,14 @@ void DockWindow::toggleDockFloating(const QString& dockName)
|
|||
}
|
||||
}
|
||||
|
||||
DropDestination DockWindow::hover(const QString& draggedDockName, const QPoint& globalPos)
|
||||
DockPageView* DockWindow::currentPage() const
|
||||
{
|
||||
DockBase* draggedDock = m_currentPage ? m_currentPage->dockByName(draggedDockName) : nullptr;
|
||||
|
||||
if (!draggedDock) {
|
||||
return DropDestination();
|
||||
}
|
||||
|
||||
QPoint hoveredLocalPos = m_mainWindow->mapFromGlobal(globalPos);
|
||||
DropDestination dropDestination = resolveDropDestination(draggedDock, hoveredLocalPos);
|
||||
|
||||
if (auto toolBar = dynamic_cast<DockToolBarView*>(draggedDock)) {
|
||||
updateToolBarOrientation(toolBar, dropDestination);
|
||||
}
|
||||
|
||||
setCurrentDropDestination(draggedDock, dropDestination);
|
||||
|
||||
return m_currentDropDestination;
|
||||
return m_currentPage;
|
||||
}
|
||||
|
||||
void DockWindow::endHover()
|
||||
QQuickItem& DockWindow::asItem() const
|
||||
{
|
||||
if (!m_currentDropDestination.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentDropDestination.dock->hideHighlighting();
|
||||
|
||||
if (m_currentDropDestination.dock->type() == DockType::DockingHolder) {
|
||||
m_currentDropDestination.dock->close();
|
||||
}
|
||||
|
||||
m_currentDropDestination.clear();
|
||||
return *m_mainWindow;
|
||||
}
|
||||
|
||||
DockingHolderView* DockWindow::mainToolBarDockingHolder() const
|
||||
|
@ -555,237 +501,3 @@ QList<DockToolBarView*> DockWindow::topLevelToolBars(const DockPageView* page) c
|
|||
|
||||
return toolBars;
|
||||
}
|
||||
|
||||
bool DockWindow::isMouseOverDock(const QPoint& mouseLocalPos, const DockBase* dock) const
|
||||
{
|
||||
QRect geometry = dock ? dock->frameGeometry() : QRect();
|
||||
return geometry.contains(mouseLocalPos);
|
||||
}
|
||||
|
||||
void DockWindow::updateToolBarOrientation(DockToolBarView* draggedToolBar, const DropDestination& dropDestination)
|
||||
{
|
||||
IF_ASSERT_FAILED(draggedToolBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
framework::Orientation orientation = framework::Orientation::Horizontal;
|
||||
|
||||
if (!dropDestination.isValid()) {
|
||||
draggedToolBar->setOrientation(static_cast<Qt::Orientation>(orientation));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dropDestination.dock->location()) {
|
||||
case Location::Left:
|
||||
case Location::Right:
|
||||
orientation = framework::Orientation::Vertical;
|
||||
break;
|
||||
case Location::Top:
|
||||
case Location::Bottom:
|
||||
orientation = framework::Orientation::Horizontal;
|
||||
break;
|
||||
case Location::Center:
|
||||
case Location::Undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
draggedToolBar->setOrientation(static_cast<Qt::Orientation>(orientation));
|
||||
}
|
||||
|
||||
void DockWindow::setCurrentDropDestination(const DockBase* draggedDock, const DropDestination& dropDestination)
|
||||
{
|
||||
if (m_currentDropDestination == dropDestination) {
|
||||
return;
|
||||
}
|
||||
|
||||
endHover();
|
||||
|
||||
m_currentDropDestination = dropDestination;
|
||||
|
||||
if (!m_currentDropDestination.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto showHighlighting = [this, draggedDock]() {
|
||||
QRect highlightingRect = resolveHighlightingRect(draggedDock, m_currentDropDestination);
|
||||
m_currentDropDestination.dock->showHighlighting(highlightingRect);
|
||||
};
|
||||
|
||||
if (m_currentDropDestination.dock->type() != DockType::DockingHolder) {
|
||||
showHighlighting();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_currentDropDestination.dock->location()) {
|
||||
case Location::Left:
|
||||
case Location::Right:
|
||||
m_currentDropDestination.dock->setMinimumWidth(draggedDock->minimumWidth());
|
||||
break;
|
||||
case Location::Top:
|
||||
case Location::Bottom:
|
||||
m_currentDropDestination.dock->setMinimumHeight(draggedDock->minimumHeight());
|
||||
break;
|
||||
case Location::Center:
|
||||
case Location::Undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
m_currentDropDestination.dock->open();
|
||||
showHighlighting();
|
||||
m_currentDropDestination.dock->init();
|
||||
}
|
||||
|
||||
DropDestination DockWindow::resolveDropDestination(const DockBase* draggedDock, const QPoint& localPos) const
|
||||
{
|
||||
if (draggedDock->type() == DockType::Panel) {
|
||||
DropDestination destination;
|
||||
|
||||
destination.dock = resolveTabifyPanel(dynamic_cast<const DockPanelView*>(draggedDock), localPos);
|
||||
destination.dropLocation = resolveDropLocation(destination.dock, localPos);
|
||||
|
||||
if (destination.isValid()) {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
const DockingHolderView* holder = resolveDockingHolder(draggedDock->type(), localPos);
|
||||
QList<DropDestination> destinations = draggedDock->dropDestinations();
|
||||
|
||||
for (const DropDestination& destination : destinations) {
|
||||
if (holder == destination.dock) {
|
||||
return destination;
|
||||
}
|
||||
|
||||
if (isPointAllowedForDrop(localPos, destination)) {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
return DropDestination();
|
||||
}
|
||||
|
||||
DockingHolderView* DockWindow::resolveDockingHolder(DockType draggedDockType, const QPoint& localPos) const
|
||||
{
|
||||
if (!m_currentPage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!m_mainWindow->contains(localPos)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QRect centralGeometry = m_currentPage->centralDock()->frameGeometry();
|
||||
|
||||
// TODO: Need to take any panels docked at top into account
|
||||
if (localPos.y() <= centralGeometry.top() + MAX_DISTANCE_TO_HOLDER) {
|
||||
return m_currentPage->holder(draggedDockType, Location::Top);
|
||||
}
|
||||
|
||||
if (localPos.y() >= centralGeometry.bottom() - MAX_DISTANCE_TO_HOLDER) {
|
||||
return m_currentPage->holder(draggedDockType, Location::Bottom);
|
||||
}
|
||||
|
||||
if (localPos.x() <= MAX_DISTANCE_TO_HOLDER) {
|
||||
return m_currentPage->holder(draggedDockType, Location::Left);
|
||||
}
|
||||
|
||||
if (localPos.x() >= m_mainWindow->rect().right() - MAX_DISTANCE_TO_HOLDER) {
|
||||
return m_currentPage->holder(draggedDockType, Location::Right);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DockPanelView* DockWindow::resolveTabifyPanel(const DockPanelView* panel, const QPoint& localPos) const
|
||||
{
|
||||
QList<DockPanelView*> tabs = m_currentPage->possibleTabs(panel);
|
||||
|
||||
for (DockPanelView* tab : tabs) {
|
||||
if (isMouseOverDock(localPos, tab)) {
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Location DockWindow::resolveDropLocation(const DockBase* hoveredDock, const QPoint& localPos) const
|
||||
{
|
||||
if (!hoveredDock) {
|
||||
return Location::Undefined;
|
||||
}
|
||||
|
||||
QRect geometry = hoveredDock->frameGeometry();
|
||||
Location dockLocation = hoveredDock->location();
|
||||
|
||||
qreal frameEnd = geometry.bottom();
|
||||
qreal mousePos = localPos.y();
|
||||
Location beginDropLocation = Location::Top;
|
||||
Location endDropLocation = Location::Bottom;
|
||||
|
||||
if (dockLocation == Location::Top || dockLocation == Location::Bottom) {
|
||||
mousePos = localPos.x();
|
||||
frameEnd = geometry.right();
|
||||
beginDropLocation = Location::Left;
|
||||
endDropLocation = Location::Right;
|
||||
}
|
||||
|
||||
if (mousePos <= frameEnd / 3) {
|
||||
return beginDropLocation;
|
||||
}
|
||||
|
||||
if (mousePos <= frameEnd / 1.5) {
|
||||
return Location::Center;
|
||||
}
|
||||
|
||||
if (mousePos <= frameEnd) {
|
||||
return endDropLocation;
|
||||
}
|
||||
|
||||
return Location::Undefined;
|
||||
}
|
||||
|
||||
QRect DockWindow::resolveHighlightingRect(const DockBase* draggedDock, const DropDestination& destination) const
|
||||
{
|
||||
if (!destination.isValid()) {
|
||||
return QRect();
|
||||
}
|
||||
|
||||
QRect frameGeometry = destination.dock->frameGeometry();
|
||||
int frameWidth = frameGeometry.width();
|
||||
int frameHeight = frameGeometry.height();
|
||||
QRect fullFrameHighlightingRect = QRect(0, 0, frameWidth, frameHeight);
|
||||
|
||||
if (destination.dock->type() == DockType::DockingHolder) {
|
||||
return fullFrameHighlightingRect;
|
||||
}
|
||||
|
||||
if (destination.dock->type() == DockType::Central) {
|
||||
int draggedDockWidth = draggedDock->frameGeometry().width();
|
||||
|
||||
if (destination.dropLocation == Location::Left) {
|
||||
return QRect(0, 0, draggedDockWidth, frameHeight);
|
||||
}
|
||||
|
||||
if (destination.dropLocation == Location::Right) {
|
||||
return QRect(frameWidth - draggedDockWidth, 0, draggedDockWidth, frameHeight);
|
||||
}
|
||||
}
|
||||
|
||||
switch (destination.dropLocation) {
|
||||
case Location::Top:
|
||||
return QRect(0, 0, frameWidth, frameHeight / 2);
|
||||
case Location::Bottom:
|
||||
return QRect(0, frameHeight / 2, frameWidth, frameHeight / 2);
|
||||
case Location::Left:
|
||||
return QRect(0, 0, frameWidth / 2, frameHeight);
|
||||
case Location::Right:
|
||||
return QRect(frameWidth / 2, 0, frameWidth / 2, frameHeight);
|
||||
case Location::Center:
|
||||
return fullFrameHighlightingRect;
|
||||
case Location::Undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
return QRect();
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ namespace mu::dock {
|
|||
class DockToolBarView;
|
||||
class DockingHolderView;
|
||||
class DockPageView;
|
||||
class DockPanelView;
|
||||
class DockWindow : public QQuickItem, public IDockWindow, public async::Asyncable
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -86,8 +85,8 @@ public:
|
|||
bool isDockFloating(const QString& dockName) const override;
|
||||
void toggleDockFloating(const QString& dockName) override;
|
||||
|
||||
DropDestination hover(const QString& draggedDockName, const QPoint& globalPos) override;
|
||||
void endHover() override;
|
||||
DockPageView* currentPage() const override;
|
||||
QQuickItem& asItem() const override;
|
||||
|
||||
public slots:
|
||||
void setMainToolBarDockingHolder(DockingHolderView* mainToolBarDockingHolder);
|
||||
|
@ -129,22 +128,11 @@ private:
|
|||
|
||||
QList<DockToolBarView*> topLevelToolBars(const DockPageView* page) const;
|
||||
|
||||
bool isMouseOverDock(const QPoint& mouseLocalPos, const DockBase* dock) const;
|
||||
void updateToolBarOrientation(DockToolBarView* draggedToolBar, const DropDestination& dropDestination = DropDestination());
|
||||
void setCurrentDropDestination(const DockBase* draggedDock, const DropDestination& dropDestination);
|
||||
|
||||
DropDestination resolveDropDestination(const DockBase* draggedDock, const QPoint& localPos) const;
|
||||
DockingHolderView* resolveDockingHolder(DockType draggedDockType, const QPoint& localPos) const;
|
||||
DockPanelView* resolveTabifyPanel(const DockPanelView* panel, const QPoint& localPos) const;
|
||||
Location resolveDropLocation(const DockBase* hoveredDock, const QPoint& localPos) const;
|
||||
QRect resolveHighlightingRect(const DockBase* draggedDock, const DropDestination& destination) const;
|
||||
|
||||
KDDockWidgets::MainWindowBase* m_mainWindow = nullptr;
|
||||
DockPageView* m_currentPage = nullptr;
|
||||
uicomponents::QmlListProperty<DockToolBarView> m_toolBars;
|
||||
DockingHolderView* m_mainToolBarDockingHolder = nullptr;
|
||||
uicomponents::QmlListProperty<DockPageView> m_pages;
|
||||
DropDestination m_currentDropDestination;
|
||||
async::Channel<QStringList> m_docksOpenStatusChanged;
|
||||
|
||||
bool m_quiting = false;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
class QPoint;
|
||||
|
||||
namespace mu::dock {
|
||||
class DockPageView;
|
||||
class IDockWindow
|
||||
{
|
||||
public:
|
||||
|
@ -44,8 +45,8 @@ public:
|
|||
virtual bool isDockFloating(const QString& dockName) const = 0;
|
||||
virtual void toggleDockFloating(const QString& dockName) = 0;
|
||||
|
||||
virtual DropDestination hover(const QString& draggedDockName, const QPoint& globalPos) = 0;
|
||||
virtual void endHover() = 0;
|
||||
virtual DockPageView* currentPage() const = 0;
|
||||
virtual QQuickItem& asItem() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ signals:
|
|||
|
||||
protected:
|
||||
friend class DockWindow;
|
||||
friend class DragController;
|
||||
friend class DropController;
|
||||
|
||||
virtual DockType type() const = 0;
|
||||
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dragcontroller.h"
|
||||
|
||||
#include "../idockwindow.h"
|
||||
#include "../docktypes.h"
|
||||
|
||||
using namespace mu::dock;
|
||||
|
||||
using DockWidget = KDDockWidgets::DockWidgetBase;
|
||||
using KDDropLocation = KDDockWidgets::DropIndicatorOverlayInterface::DropLocation;
|
||||
|
||||
namespace mu::dock {
|
||||
static const DockWidget* draggedDock()
|
||||
{
|
||||
auto windowBeingDragged = KDDockWidgets::DragController::instance()->windowBeingDragged();
|
||||
if (!windowBeingDragged || windowBeingDragged->dockWidgets().isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return windowBeingDragged->dockWidgets().first();
|
||||
}
|
||||
|
||||
static KDDropLocation dropLocationToKDDockLocation(Location location)
|
||||
{
|
||||
switch (location) {
|
||||
case Location::Undefined: return KDDropLocation::DropLocation_None;
|
||||
case Location::Left: return KDDropLocation::DropLocation_Left;
|
||||
case Location::Right: return KDDropLocation::DropLocation_Right;
|
||||
case Location::Center: return KDDropLocation::DropLocation_Center;
|
||||
case Location::Top: return KDDropLocation::DropLocation_Top;
|
||||
case Location::Bottom: return KDDropLocation::DropLocation_Bottom;
|
||||
}
|
||||
|
||||
return KDDropLocation::DropLocation_None;
|
||||
}
|
||||
}
|
||||
|
||||
DragController::DragController(KDDockWidgets::DropArea* dropArea)
|
||||
: KDDockWidgets::DropIndicatorOverlayInterface(dropArea)
|
||||
{
|
||||
}
|
||||
|
||||
KDDropLocation DragController::hover_impl(QPoint globalPos)
|
||||
{
|
||||
IDockWindow* window = dockWindow();
|
||||
const DockWidget* dock = draggedDock();
|
||||
|
||||
if (!window || !dock) {
|
||||
return DropLocation_None;
|
||||
}
|
||||
|
||||
DropDestination destination = window->hover(dock->uniqueName(), globalPos);
|
||||
setCurrentDropLocation(dropLocationToKDDockLocation(destination.dropLocation));
|
||||
|
||||
if (destination.isValid()) {
|
||||
setHoveredFrame(destination.dock->dockWidget()->frame());
|
||||
}
|
||||
|
||||
return currentDropLocation();
|
||||
}
|
||||
|
||||
void DragController::updateVisibility()
|
||||
{
|
||||
if (draggedDock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentDropLocation(DropLocation_None);
|
||||
|
||||
if (auto window = dockWindow()) {
|
||||
window->endHover();
|
||||
}
|
||||
}
|
||||
|
||||
QPoint DragController::posForIndicator(KDDropLocation) const
|
||||
{
|
||||
return QPoint();
|
||||
}
|
||||
|
||||
IDockWindow* DragController::dockWindow() const
|
||||
{
|
||||
return dockWindowProvider()->window();
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MU_DOCK_DRAGCONTROLLER_H
|
||||
#define MU_DOCK_DRAGCONTROLLER_H
|
||||
|
||||
#include "modularity/ioc.h"
|
||||
#include "../idockwindowprovider.h"
|
||||
|
||||
#include "thirdparty/KDDockWidgets/src/private/DropIndicatorOverlayInterface_p.h"
|
||||
|
||||
namespace mu::dock {
|
||||
class DragController : public KDDockWidgets::DropIndicatorOverlayInterface
|
||||
{
|
||||
INJECT(dock, IDockWindowProvider, dockWindowProvider)
|
||||
|
||||
public:
|
||||
explicit DragController(KDDockWidgets::DropArea* dropArea);
|
||||
|
||||
DropLocation hover_impl(QPoint globalPos) override;
|
||||
QPoint posForIndicator(DropLocation) const override;
|
||||
|
||||
private:
|
||||
void updateVisibility() override;
|
||||
|
||||
IDockWindow* dockWindow() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MU_DOCK_DRAGCONTROLLER_H
|
398
src/appshell/view/dockwindow/internal/dropcontroller.cpp
Normal file
398
src/appshell/view/dockwindow/internal/dropcontroller.cpp
Normal file
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dropcontroller.h"
|
||||
|
||||
#include "../idockwindow.h"
|
||||
#include "../dockpanelview.h"
|
||||
#include "../dockpageview.h"
|
||||
#include "../docktoolbarview.h"
|
||||
|
||||
#include "globaltypes.h"
|
||||
#include "log.h"
|
||||
|
||||
using namespace mu::dock;
|
||||
|
||||
static constexpr double MAX_DISTANCE_TO_HOLDER = 50;
|
||||
|
||||
using DockWidget = KDDockWidgets::DockWidgetBase;
|
||||
using KDDropLocation = KDDockWidgets::DropIndicatorOverlayInterface::DropLocation;
|
||||
|
||||
namespace mu::dock {
|
||||
static const DockWidget* draggedDock()
|
||||
{
|
||||
auto windowBeingDragged = KDDockWidgets::DragController::instance()->windowBeingDragged();
|
||||
if (!windowBeingDragged || windowBeingDragged->dockWidgets().isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return windowBeingDragged->dockWidgets().first();
|
||||
}
|
||||
|
||||
static KDDropLocation dropLocationToKDDockLocation(Location location)
|
||||
{
|
||||
switch (location) {
|
||||
case Location::Undefined: return KDDropLocation::DropLocation_None;
|
||||
case Location::Left: return KDDropLocation::DropLocation_Left;
|
||||
case Location::Right: return KDDropLocation::DropLocation_Right;
|
||||
case Location::Center: return KDDropLocation::DropLocation_Center;
|
||||
case Location::Top: return KDDropLocation::DropLocation_Top;
|
||||
case Location::Bottom: return KDDropLocation::DropLocation_Bottom;
|
||||
}
|
||||
|
||||
return KDDropLocation::DropLocation_None;
|
||||
}
|
||||
|
||||
static bool isPointAllowedForDrop(const QPoint& point, const DropDestination& dropDestination)
|
||||
{
|
||||
QRect dropRect = dropDestination.dock->frameGeometry();
|
||||
|
||||
if (!dropRect.contains(point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dropDestination.dropDistance == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dropDestination.dropLocation == Location::Left) {
|
||||
if (std::abs(dropRect.left() - point.x()) <= dropDestination.dropDistance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dropDestination.dropLocation == Location::Right) {
|
||||
if (std::abs(dropRect.right() - point.x()) <= dropDestination.dropDistance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DropController::DropController(KDDockWidgets::DropArea* dropArea)
|
||||
: KDDockWidgets::DropIndicatorOverlayInterface(dropArea)
|
||||
{
|
||||
}
|
||||
|
||||
KDDropLocation DropController::hover_impl(QPoint globalPos)
|
||||
{
|
||||
const DockWidget* dock = draggedDock();
|
||||
const DockPageView* page = currentPage();
|
||||
|
||||
if (!page || !dock) {
|
||||
return DropLocation_None;
|
||||
}
|
||||
|
||||
DockBase* draggedDock = page->dockByName(dock->uniqueName());
|
||||
|
||||
if (!draggedDock) {
|
||||
return DropLocation_None;
|
||||
}
|
||||
|
||||
QPoint hoveredLocalPos = dockWindow()->asItem().mapFromGlobal(globalPos).toPoint();
|
||||
DropDestination dropDestination = resolveDropDestination(draggedDock, hoveredLocalPos);
|
||||
|
||||
if (auto toolBar = dynamic_cast<DockToolBarView*>(draggedDock)) {
|
||||
updateToolBarOrientation(toolBar, dropDestination);
|
||||
}
|
||||
|
||||
setCurrentDropDestination(draggedDock, dropDestination);
|
||||
setCurrentDropLocation(dropLocationToKDDockLocation(m_currentDropDestination.dropLocation));
|
||||
|
||||
if (m_currentDropDestination.isValid()) {
|
||||
setHoveredFrame(m_currentDropDestination.dock->dockWidget()->frame());
|
||||
}
|
||||
|
||||
return currentDropLocation();
|
||||
}
|
||||
|
||||
void DropController::updateVisibility()
|
||||
{
|
||||
if (draggedDock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentDropLocation(DropLocation_None);
|
||||
endHover();
|
||||
}
|
||||
|
||||
QPoint DropController::posForIndicator(KDDropLocation) const
|
||||
{
|
||||
return QPoint();
|
||||
}
|
||||
|
||||
void DropController::endHover()
|
||||
{
|
||||
if (!m_currentDropDestination.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentDropDestination.dock->hideHighlighting();
|
||||
|
||||
if (m_currentDropDestination.dock->type() == DockType::DockingHolder) {
|
||||
m_currentDropDestination.dock->close();
|
||||
}
|
||||
|
||||
m_currentDropDestination.clear();
|
||||
}
|
||||
|
||||
bool DropController::isMouseOverDock(const QPoint& mouseLocalPos, const DockBase* dock) const
|
||||
{
|
||||
QRect geometry = dock ? dock->frameGeometry() : QRect();
|
||||
return geometry.contains(mouseLocalPos);
|
||||
}
|
||||
|
||||
void DropController::updateToolBarOrientation(DockToolBarView* draggedToolBar, const DropDestination& dropDestination)
|
||||
{
|
||||
IF_ASSERT_FAILED(draggedToolBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
framework::Orientation orientation = framework::Orientation::Horizontal;
|
||||
|
||||
if (!dropDestination.isValid()) {
|
||||
draggedToolBar->setOrientation(static_cast<Qt::Orientation>(orientation));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dropDestination.dock->location()) {
|
||||
case Location::Left:
|
||||
case Location::Right:
|
||||
orientation = framework::Orientation::Vertical;
|
||||
break;
|
||||
case Location::Top:
|
||||
case Location::Bottom:
|
||||
orientation = framework::Orientation::Horizontal;
|
||||
break;
|
||||
case Location::Center:
|
||||
case Location::Undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
draggedToolBar->setOrientation(static_cast<Qt::Orientation>(orientation));
|
||||
}
|
||||
|
||||
void DropController::setCurrentDropDestination(const DockBase* draggedDock, const DropDestination& dropDestination)
|
||||
{
|
||||
if (m_currentDropDestination == dropDestination) {
|
||||
return;
|
||||
}
|
||||
|
||||
endHover();
|
||||
|
||||
m_currentDropDestination = dropDestination;
|
||||
|
||||
if (!m_currentDropDestination.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto showHighlighting = [this, draggedDock]() {
|
||||
QRect highlightingRect = resolveHighlightingRect(draggedDock, m_currentDropDestination);
|
||||
m_currentDropDestination.dock->showHighlighting(highlightingRect);
|
||||
};
|
||||
|
||||
if (m_currentDropDestination.dock->type() != DockType::DockingHolder) {
|
||||
showHighlighting();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_currentDropDestination.dock->location()) {
|
||||
case Location::Left:
|
||||
case Location::Right:
|
||||
m_currentDropDestination.dock->setMinimumWidth(draggedDock->minimumWidth());
|
||||
break;
|
||||
case Location::Top:
|
||||
case Location::Bottom:
|
||||
m_currentDropDestination.dock->setMinimumHeight(draggedDock->minimumHeight());
|
||||
break;
|
||||
case Location::Center:
|
||||
case Location::Undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
m_currentDropDestination.dock->open();
|
||||
showHighlighting();
|
||||
m_currentDropDestination.dock->init();
|
||||
}
|
||||
|
||||
DropDestination DropController::resolveDropDestination(const DockBase* draggedDock, const QPoint& localPos) const
|
||||
{
|
||||
if (draggedDock->type() == DockType::Panel) {
|
||||
DropDestination destination;
|
||||
|
||||
destination.dock = resolveTabifyPanel(dynamic_cast<const DockPanelView*>(draggedDock), localPos);
|
||||
destination.dropLocation = resolveDropLocation(destination.dock, localPos);
|
||||
|
||||
if (destination.isValid()) {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
const DockingHolderView* holder = resolveDockingHolder(draggedDock->type(), localPos);
|
||||
QList<DropDestination> destinations = draggedDock->dropDestinations();
|
||||
|
||||
for (const DropDestination& destination : destinations) {
|
||||
if (holder == destination.dock) {
|
||||
return destination;
|
||||
}
|
||||
|
||||
if (isPointAllowedForDrop(localPos, destination)) {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
return DropDestination();
|
||||
}
|
||||
|
||||
DockingHolderView* DropController::resolveDockingHolder(DockType draggedDockType, const QPoint& localPos) const
|
||||
{
|
||||
if (!dockWindow()->asItem().contains(localPos)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QRect centralGeometry = currentPage()->centralDock()->frameGeometry();
|
||||
|
||||
// TODO: Need to take any panels docked at top into account
|
||||
if (localPos.y() <= centralGeometry.top() + MAX_DISTANCE_TO_HOLDER) {
|
||||
return currentPage()->holder(draggedDockType, Location::Top);
|
||||
}
|
||||
|
||||
if (localPos.y() >= centralGeometry.bottom() - MAX_DISTANCE_TO_HOLDER) {
|
||||
return currentPage()->holder(draggedDockType, Location::Bottom);
|
||||
}
|
||||
|
||||
if (localPos.x() <= MAX_DISTANCE_TO_HOLDER) {
|
||||
return currentPage()->holder(draggedDockType, Location::Left);
|
||||
}
|
||||
|
||||
if (localPos.x() >= dockWindow()->asItem().boundingRect().right() - MAX_DISTANCE_TO_HOLDER) {
|
||||
return currentPage()->holder(draggedDockType, Location::Right);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DockPanelView* DropController::resolveTabifyPanel(const DockPanelView* panel, const QPoint& localPos) const
|
||||
{
|
||||
QList<DockPanelView*> tabs = currentPage()->possibleTabs(panel);
|
||||
|
||||
for (DockPanelView* tab : tabs) {
|
||||
if (isMouseOverDock(localPos, tab)) {
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Location DropController::resolveDropLocation(const DockBase* hoveredDock, const QPoint& localPos) const
|
||||
{
|
||||
if (!hoveredDock) {
|
||||
return Location::Undefined;
|
||||
}
|
||||
|
||||
QRect geometry = hoveredDock->frameGeometry();
|
||||
Location dockLocation = hoveredDock->location();
|
||||
|
||||
qreal frameEnd = geometry.bottom();
|
||||
qreal mousePos = localPos.y();
|
||||
Location beginDropLocation = Location::Top;
|
||||
Location endDropLocation = Location::Bottom;
|
||||
|
||||
if (dockLocation == Location::Top || dockLocation == Location::Bottom) {
|
||||
mousePos = localPos.x();
|
||||
frameEnd = geometry.right();
|
||||
beginDropLocation = Location::Left;
|
||||
endDropLocation = Location::Right;
|
||||
}
|
||||
|
||||
if (mousePos <= frameEnd / 3) {
|
||||
return beginDropLocation;
|
||||
}
|
||||
|
||||
if (mousePos <= frameEnd / 1.5) {
|
||||
return Location::Center;
|
||||
}
|
||||
|
||||
if (mousePos <= frameEnd) {
|
||||
return endDropLocation;
|
||||
}
|
||||
|
||||
return Location::Undefined;
|
||||
}
|
||||
|
||||
QRect DropController::resolveHighlightingRect(const DockBase* draggedDock, const DropDestination& destination) const
|
||||
{
|
||||
if (!destination.isValid()) {
|
||||
return QRect();
|
||||
}
|
||||
|
||||
QRect frameGeometry = destination.dock->frameGeometry();
|
||||
int frameWidth = frameGeometry.width();
|
||||
int frameHeight = frameGeometry.height();
|
||||
QRect fullFrameHighlightingRect = QRect(0, 0, frameWidth, frameHeight);
|
||||
|
||||
if (destination.dock->type() == DockType::DockingHolder) {
|
||||
return fullFrameHighlightingRect;
|
||||
}
|
||||
|
||||
if (destination.dock->type() == DockType::Central) {
|
||||
int draggedDockWidth = draggedDock->frameGeometry().width();
|
||||
|
||||
if (destination.dropLocation == Location::Left) {
|
||||
return QRect(0, 0, draggedDockWidth, frameHeight);
|
||||
}
|
||||
|
||||
if (destination.dropLocation == Location::Right) {
|
||||
return QRect(frameWidth - draggedDockWidth, 0, draggedDockWidth, frameHeight);
|
||||
}
|
||||
}
|
||||
|
||||
switch (destination.dropLocation) {
|
||||
case Location::Top:
|
||||
return QRect(0, 0, frameWidth, frameHeight / 2);
|
||||
case Location::Bottom:
|
||||
return QRect(0, frameHeight / 2, frameWidth, frameHeight / 2);
|
||||
case Location::Left:
|
||||
return QRect(0, 0, frameWidth / 2, frameHeight);
|
||||
case Location::Right:
|
||||
return QRect(frameWidth / 2, 0, frameWidth / 2, frameHeight);
|
||||
case Location::Center:
|
||||
return fullFrameHighlightingRect;
|
||||
case Location::Undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
return QRect();
|
||||
}
|
||||
|
||||
IDockWindow* DropController::dockWindow() const
|
||||
{
|
||||
return dockWindowProvider()->window();
|
||||
}
|
||||
|
||||
DockPageView* DropController::currentPage() const
|
||||
{
|
||||
return dockWindow() ? dockWindow()->currentPage() : nullptr;
|
||||
}
|
70
src/appshell/view/dockwindow/internal/dropcontroller.h
Normal file
70
src/appshell/view/dockwindow/internal/dropcontroller.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MU_DOCK_DROPCONTROLLER_H
|
||||
#define MU_DOCK_DROPCONTROLLER_H
|
||||
|
||||
#include "modularity/ioc.h"
|
||||
#include "../idockwindowprovider.h"
|
||||
|
||||
#include "dockbase.h"
|
||||
|
||||
#include "thirdparty/KDDockWidgets/src/private/DropIndicatorOverlayInterface_p.h"
|
||||
|
||||
namespace mu::dock {
|
||||
class DockPanelView;
|
||||
class DockingHolderView;
|
||||
class DockToolBarView;
|
||||
class DockPageView;
|
||||
class DropController : public KDDockWidgets::DropIndicatorOverlayInterface
|
||||
{
|
||||
INJECT(dock, IDockWindowProvider, dockWindowProvider)
|
||||
|
||||
public:
|
||||
explicit DropController(KDDockWidgets::DropArea* dropArea);
|
||||
|
||||
DropLocation hover_impl(QPoint globalPos) override;
|
||||
QPoint posForIndicator(DropLocation) const override;
|
||||
|
||||
private:
|
||||
void updateVisibility() override;
|
||||
|
||||
void endHover();
|
||||
|
||||
bool isMouseOverDock(const QPoint& mouseLocalPos, const DockBase* dock) const;
|
||||
void updateToolBarOrientation(DockToolBarView* draggedToolBar, const DropDestination& dropDestination = DropDestination());
|
||||
void setCurrentDropDestination(const DockBase* draggedDock, const DropDestination& dropDestination);
|
||||
|
||||
DropDestination resolveDropDestination(const DockBase* draggedDock, const QPoint& localPos) const;
|
||||
DockingHolderView* resolveDockingHolder(DockType draggedDockType, const QPoint& localPos) const;
|
||||
DockPanelView* resolveTabifyPanel(const DockPanelView* panel, const QPoint& localPos) const;
|
||||
Location resolveDropLocation(const DockBase* hoveredDock, const QPoint& localPos) const;
|
||||
QRect resolveHighlightingRect(const DockBase* draggedDock, const DropDestination& destination) const;
|
||||
|
||||
IDockWindow* dockWindow() const;
|
||||
DockPageView* currentPage() const;
|
||||
|
||||
DropDestination m_currentDropDestination;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MU_DOCK_DROPCONTROLLER_H
|
Loading…
Reference in a new issue