LCOV - code coverage report
Current view: top level - muse/framework/dockwindow_v2/internal - dropcontroller.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 210 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "dropcontroller.h"
      24              : 
      25              : #include "../idockwindow.h"
      26              : 
      27              : #include "qml/Muse/Dock/dockcentralview.h"
      28              : #include "qml/Muse/Dock/dockingholderview.h"
      29              : #include "qml/Muse/Dock/dockpageview.h"
      30              : #include "qml/Muse/Dock/dockpanelview.h"
      31              : #include "qml/Muse/Dock/docktoolbarview.h"
      32              : 
      33              : #include "globaltypes.h"
      34              : 
      35              : // This include pulls in kdbindings/signal.h which is incompatible with
      36              : // Qt's `emit` macro being defined. Temporarily undefine it, then restore it.
      37              : #ifdef emit
      38              : #undef emit
      39              : #include "kddockwidgets/src/core/DragController_p.h"
      40              : #include "kddockwidgets/src/core/DropArea.h"
      41              : #define emit
      42              : #else
      43              : #include "kddockwidgets/src/core/DragController_p.h"
      44              : #include "kddockwidgets/src/core/DropArea.h"
      45              : #endif
      46              : 
      47              : #include "kddockwidgets/src/core/DockWidget.h"
      48              : #include "kddockwidgets/src/qtquick/views/DockWidget.h"
      49              : #include "kddockwidgets/src/qtquick/views/View.h"
      50              : 
      51              : #include "log.h"
      52              : 
      53              : using KDDropLocation = KDDockWidgets::DropLocation;
      54              : 
      55              : namespace muse::dock {
      56              : static constexpr double MAX_DISTANCE_TO_HOLDER = 50;
      57              : 
      58            0 : static KDDropLocation dropLocationToKDDockLocation(Location location)
      59              : {
      60            0 :     switch (location) {
      61            0 :     case Location::Undefined: return KDDropLocation::DropLocation_None;
      62            0 :     case Location::Left: return KDDropLocation::DropLocation_Left;
      63            0 :     case Location::Right: return KDDropLocation::DropLocation_Right;
      64            0 :     case Location::Center: return KDDropLocation::DropLocation_Center;
      65            0 :     case Location::Top: return KDDropLocation::DropLocation_Top;
      66            0 :     case Location::Bottom: return KDDropLocation::DropLocation_Bottom;
      67              :     }
      68              : 
      69            0 :     return KDDropLocation::DropLocation_None;
      70              : }
      71              : 
      72            0 : static bool isPointAllowedForDrop(const QPoint& point, const DropDestination& dropDestination)
      73              : {
      74            0 :     QRect dropRect = dropDestination.dock->frameGeometry();
      75              : 
      76            0 :     if (!dropRect.contains(point)) {
      77            0 :         return false;
      78              :     }
      79              : 
      80            0 :     if (dropDestination.dropDistance == 0) {
      81            0 :         return true;
      82              :     }
      83              : 
      84            0 :     if (dropDestination.dropLocation == Location::Left) {
      85            0 :         if (std::abs(dropRect.left() - point.x()) <= dropDestination.dropDistance) {
      86            0 :             return true;
      87              :         }
      88              :     }
      89              : 
      90            0 :     if (dropDestination.dropLocation == Location::Right) {
      91            0 :         if (std::abs(dropRect.right() - point.x()) <= dropDestination.dropDistance) {
      92            0 :             return true;
      93              :         }
      94              :     }
      95              : 
      96            0 :     return false;
      97              : }
      98              : }
      99              : 
     100              : using namespace muse::dock;
     101              : 
     102            0 : DropController::DropController(KDDockWidgets::Core::ClassicDropIndicatorOverlay* classicIndicators,
     103              :                                KDDockWidgets::Core::View* parent,
     104            0 :                                const modularity::ContextPtr& iocCtx)
     105              :     : Contextable(iocCtx)
     106            0 :     , m_classicIndicators(classicIndicators)
     107            0 :     , m_dropArea(parent ? parent->asDropAreaController() : nullptr)
     108              : {
     109            0 : }
     110              : 
     111            0 : KDDropLocation DropController::hover(KDDockWidgets::Point globalPos)
     112              : {
     113              :     //! NOTE: The drop destinations are resolved in the main window only, so don't allow
     114              :     //! dropping into the layout of a floating window, e.g. of an undocked toolbar under the cursor
     115            0 :     if (m_dropArea && !m_dropArea->isInMainWindow()) {
     116            0 :         return KDDockWidgets::DropLocation_None;
     117              :     }
     118              : 
     119            0 :     DockBase* draggedDock = this->draggedDock();
     120            0 :     if (!draggedDock) {
     121            0 :         return KDDockWidgets::DropLocation_None;
     122              :     }
     123              : 
     124            0 :     QPoint hoveredLocalPos = dockWindow()->asItem().mapFromGlobal(globalPos).toPoint();
     125            0 :     DropDestination dropDestination = resolveDropDestination(draggedDock, hoveredLocalPos);
     126              : 
     127            0 :     if (auto toolBar = dynamic_cast<DockToolBarView*>(draggedDock)) {
     128            0 :         updateToolBarOrientation(toolBar, dropDestination);
     129              :     }
     130              : 
     131            0 :     setCurrentDropDestination(draggedDock, dropDestination);
     132              : 
     133            0 :     if (m_currentDropDestination.isValid()) {
     134            0 :         auto* dw = m_currentDropDestination.dock->dockWidget();
     135            0 :         auto* dwView = qobject_cast<KDDockWidgets::QtQuick::DockWidget*>(
     136            0 :             KDDockWidgets::QtQuick::asQQuickItem(dw));
     137            0 :         if (dwView) {
     138            0 :             m_classicIndicators->setHoveredGroup(dwView->group());
     139              :         }
     140              :     }
     141              : 
     142            0 :     return dropLocationToKDDockLocation(m_currentDropDestination.dropLocation);
     143              : }
     144              : 
     145            0 : void DropController::setVisible(bool visible)
     146              : {
     147            0 :     if (visible) {
     148            0 :         return;
     149              :     }
     150              : 
     151            0 :     DockBase* draggedDock = this->draggedDock();
     152            0 :     if (auto toolBar = dynamic_cast<DockToolBarView*>(draggedDock)) {
     153            0 :         updateToolBarOrientation(toolBar);
     154              :     }
     155              : 
     156            0 :     endHover();
     157              : }
     158              : 
     159            0 : KDDockWidgets::Point DropController::posForIndicator(KDDockWidgets::DropLocation) const
     160              : {
     161            0 :     return KDDockWidgets::Point();
     162              : }
     163              : 
     164            0 : void DropController::endHover()
     165              : {
     166            0 :     if (!m_currentDropDestination.isValid()) {
     167            0 :         return;
     168              :     }
     169              : 
     170            0 :     m_currentDropDestination.dock->hideHighlighting();
     171              : 
     172            0 :     if (m_currentDropDestination.dock->type() == DockType::DockingHolder) {
     173            0 :         m_currentDropDestination.dock->close();
     174              :     }
     175              : 
     176            0 :     m_currentDropDestination.clear();
     177              : }
     178              : 
     179            0 : bool DropController::isMouseOverDock(const QPoint& mouseLocalPos, const DockBase* dock) const
     180              : {
     181            0 :     QRect geometry = dock ? dock->frameGeometry() : QRect();
     182            0 :     return geometry.contains(mouseLocalPos);
     183              : }
     184              : 
     185            0 : void DropController::updateToolBarOrientation(DockToolBarView* draggedToolBar, const DropDestination& dropDestination)
     186              : {
     187            0 :     IF_ASSERT_FAILED(draggedToolBar) {
     188            0 :         return;
     189              :     }
     190              : 
     191            0 :     muse::Orientation orientation = muse::Orientation::Horizontal;
     192              : 
     193            0 :     if (!dropDestination.isValid()) {
     194            0 :         draggedToolBar->setOrientation(static_cast<Qt::Orientation>(orientation));
     195            0 :         return;
     196              :     }
     197              : 
     198            0 :     switch (dropDestination.dock->location()) {
     199            0 :     case Location::Left:
     200              :     case Location::Right:
     201            0 :         orientation = muse::Orientation::Vertical;
     202            0 :         break;
     203            0 :     case Location::Top:
     204              :     case Location::Bottom:
     205            0 :         orientation = muse::Orientation::Horizontal;
     206            0 :         break;
     207            0 :     case Location::Center:
     208              :     case Location::Undefined:
     209            0 :         break;
     210              :     }
     211              : 
     212            0 :     draggedToolBar->setOrientation(static_cast<Qt::Orientation>(orientation));
     213              : }
     214              : 
     215            0 : void DropController::setCurrentDropDestination(const DockBase* draggedDock, const DropDestination& dropDestination)
     216              : {
     217            0 :     if (m_currentDropDestination == dropDestination) {
     218            0 :         return;
     219              :     }
     220              : 
     221            0 :     endHover();
     222              : 
     223            0 :     m_currentDropDestination = dropDestination;
     224              : 
     225            0 :     if (!m_currentDropDestination.isValid()) {
     226            0 :         return;
     227              :     }
     228              : 
     229            0 :     auto showHighlighting = [this, draggedDock]() {
     230            0 :         QRect highlightingRect = resolveHighlightingRect(draggedDock, m_currentDropDestination);
     231            0 :         m_currentDropDestination.dock->showHighlighting(highlightingRect);
     232            0 :     };
     233              : 
     234            0 :     if (m_currentDropDestination.dock->type() != DockType::DockingHolder) {
     235            0 :         showHighlighting();
     236            0 :         return;
     237              :     }
     238              : 
     239            0 :     switch (m_currentDropDestination.dock->location()) {
     240            0 :     case Location::Left:
     241              :     case Location::Right:
     242            0 :         m_currentDropDestination.dock->setMinimumWidth(draggedDock->minimumWidth());
     243            0 :         break;
     244            0 :     case Location::Top:
     245              :     case Location::Bottom:
     246            0 :         m_currentDropDestination.dock->setMinimumHeight(draggedDock->minimumHeight());
     247            0 :         break;
     248            0 :     case Location::Center:
     249              :     case Location::Undefined:
     250            0 :         break;
     251              :     }
     252              : 
     253            0 :     m_currentDropDestination.dock->open();
     254            0 :     showHighlighting();
     255            0 :     m_currentDropDestination.dock->init();
     256              : }
     257              : 
     258            0 : DropDestination DropController::resolveDropDestination(const DockBase* draggedDock, const QPoint& localPos) const
     259              : {
     260            0 :     if (draggedDock->type() == DockType::Panel) {
     261            0 :         DropDestination destination;
     262              : 
     263            0 :         destination.dock = resolvePanelForDrop(dynamic_cast<const DockPanelView*>(draggedDock), localPos);
     264            0 :         destination.dropLocation = resolveDropLocation(destination.dock, localPos);
     265              : 
     266            0 :         if (destination.isValid()) {
     267            0 :             return destination;
     268              :         }
     269              :     }
     270              : 
     271            0 :     const DockingHolderView* holder = resolveDockingHolder(draggedDock->type(), localPos);
     272            0 :     QList<DropDestination> destinations = draggedDock->dropDestinations();
     273              : 
     274            0 :     for (const DropDestination& destination : destinations) {
     275            0 :         if (holder == destination.dock) {
     276            0 :             return destination;
     277              :         }
     278              : 
     279            0 :         if (isPointAllowedForDrop(localPos, destination)) {
     280            0 :             return destination;
     281              :         }
     282              :     }
     283              : 
     284            0 :     return DropDestination();
     285            0 : }
     286              : 
     287            0 : DockingHolderView* DropController::resolveDockingHolder(DockType draggedDockType, const QPoint& localPos) const
     288              : {
     289            0 :     if (!dockWindow()->asItem().contains(localPos)) {
     290            0 :         return nullptr;
     291              :     }
     292              : 
     293            0 :     QRect centralGeometry = currentPage()->centralDock()->frameGeometry();
     294              : 
     295              :     // TODO: Need to take any panels docked at top into account
     296            0 :     if (localPos.y() <= centralGeometry.top() + MAX_DISTANCE_TO_HOLDER) {
     297            0 :         return currentPage()->holder(draggedDockType, Location::Top);
     298              :     }
     299              : 
     300            0 :     if (localPos.y() >= centralGeometry.bottom() - MAX_DISTANCE_TO_HOLDER) {
     301            0 :         return currentPage()->holder(draggedDockType, Location::Bottom);
     302              :     }
     303              : 
     304            0 :     if (localPos.x() <= MAX_DISTANCE_TO_HOLDER) {
     305            0 :         return currentPage()->holder(draggedDockType, Location::Left);
     306              :     }
     307              : 
     308            0 :     if (localPos.x() >= dockWindow()->asItem().boundingRect().right() - MAX_DISTANCE_TO_HOLDER) {
     309            0 :         return currentPage()->holder(draggedDockType, Location::Right);
     310              :     }
     311              : 
     312            0 :     return nullptr;
     313              : }
     314              : 
     315            0 : DockPanelView* DropController::resolvePanelForDrop(const DockPanelView* panel, const QPoint& localPos) const
     316              : {
     317            0 :     QList<DockPanelView*> panels = currentPage()->findPanelsForDropping(panel);
     318              : 
     319            0 :     for (DockPanelView* p : panels) {
     320            0 :         if (isMouseOverDock(localPos, p)) {
     321            0 :             return p;
     322              :         }
     323              :     }
     324              : 
     325            0 :     return nullptr;
     326            0 : }
     327              : 
     328            0 : muse::dock::Location DropController::resolveDropLocation(const DockBase* hoveredDock, const QPoint& localPos) const
     329              : {
     330            0 :     if (!hoveredDock) {
     331            0 :         return Location::Undefined;
     332              :     }
     333              : 
     334            0 :     QRect geometry = hoveredDock->frameGeometry();
     335            0 :     Location dockLocation = hoveredDock->location();
     336              : 
     337            0 :     qreal frameEnd = geometry.bottom();
     338            0 :     qreal mousePos = localPos.y();
     339            0 :     Location beginDropLocation = Location::Top;
     340            0 :     Location endDropLocation = Location::Bottom;
     341              : 
     342            0 :     if (dockLocation == Location::Top || dockLocation == Location::Bottom) {
     343            0 :         mousePos = localPos.x();
     344            0 :         frameEnd = geometry.right();
     345            0 :         beginDropLocation = Location::Left;
     346            0 :         endDropLocation = Location::Right;
     347              :     }
     348              : 
     349            0 :     if (mousePos <= frameEnd / 3) {
     350            0 :         return beginDropLocation;
     351              :     }
     352              : 
     353            0 :     if (mousePos <= frameEnd / 1.5) {
     354            0 :         return Location::Center;
     355              :     }
     356              : 
     357            0 :     if (mousePos <= frameEnd) {
     358            0 :         return endDropLocation;
     359              :     }
     360              : 
     361            0 :     return Location::Undefined;
     362              : }
     363              : 
     364            0 : QRect DropController::resolveHighlightingRect(const DockBase* draggedDock, const DropDestination& destination) const
     365              : {
     366            0 :     if (!destination.isValid()) {
     367            0 :         return QRect();
     368              :     }
     369              : 
     370            0 :     QRect frameGeometry = destination.dock->frameGeometry();
     371            0 :     int frameWidth = frameGeometry.width();
     372            0 :     int frameHeight = frameGeometry.height();
     373            0 :     QRect fullFrameHighlightingRect = QRect(0, 0, frameWidth, frameHeight);
     374              : 
     375            0 :     if (destination.dock->type() == DockType::DockingHolder) {
     376            0 :         return fullFrameHighlightingRect;
     377              :     }
     378              : 
     379            0 :     if (destination.dock->type() == DockType::Central) {
     380            0 :         int draggedDockWidth = draggedDock->frameGeometry().width();
     381              : 
     382            0 :         if (destination.dropLocation == Location::Left) {
     383            0 :             return QRect(0, 0, draggedDockWidth, frameHeight);
     384              :         }
     385              : 
     386            0 :         if (destination.dropLocation == Location::Right) {
     387            0 :             return QRect(frameWidth - draggedDockWidth, 0, draggedDockWidth, frameHeight);
     388              :         }
     389              :     }
     390              : 
     391            0 :     switch (destination.dropLocation) {
     392            0 :     case Location::Top:
     393            0 :         return QRect(0, 0, frameWidth, frameHeight / 2);
     394            0 :     case Location::Bottom:
     395            0 :         return QRect(0, frameHeight / 2, frameWidth, frameHeight / 2);
     396            0 :     case Location::Left:
     397            0 :         return QRect(0, 0, frameWidth / 2, frameHeight);
     398            0 :     case Location::Right:
     399            0 :         return QRect(frameWidth / 2, 0, frameWidth / 2, frameHeight);
     400            0 :     case Location::Center:
     401            0 :         return fullFrameHighlightingRect;
     402            0 :     case Location::Undefined:
     403            0 :         break;
     404              :     }
     405              : 
     406            0 :     return QRect();
     407              : }
     408              : 
     409            0 : IDockWindow* DropController::dockWindow() const
     410              : {
     411            0 :     return dockWindowProvider()->window();
     412              : }
     413              : 
     414            0 : DockPageView* DropController::currentPage() const
     415              : {
     416            0 :     return dockWindow() ? dockWindow()->currentPage() : nullptr;
     417              : }
     418              : 
     419            0 : DockBase* DropController::draggedDock() const
     420              : {
     421            0 :     auto windowBeingDragged = KDDockWidgets::Core::DragController::instance(iocContext()->id)->windowBeingDragged();
     422            0 :     if (!windowBeingDragged || windowBeingDragged->dockWidgets().isEmpty()) {
     423            0 :         return nullptr;
     424              :     }
     425              : 
     426            0 :     const DockPageView* page = currentPage();
     427            0 :     if (!page) {
     428            0 :         return nullptr;
     429              :     }
     430              : 
     431            0 :     KDDockWidgets::Core::DockWidget* draggedDockWidget = windowBeingDragged->dockWidgets().first();
     432            0 :     for (DockBase* dock : page->allDocks()) {
     433            0 :         if (dock->dockWidget() == draggedDockWidget) {
     434            0 :             return dock;
     435              :         }
     436            0 :     }
     437              : 
     438            0 :     return nullptr;
     439              : }
        

Generated by: LCOV version 2.0-1