LCOV - code coverage report
Current view: top level - muse/framework/uicomponents/qml/Muse/UiComponents - windowview.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 275 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 "windowview.h"
      24              : 
      25              : #include <QQuickView>
      26              : #include <QScreen>
      27              : #include <QTimer>
      28              : #include <QUrl>
      29              : 
      30              : #include "ui/inavigation.h"
      31              : 
      32              : #include "log.h"
      33              : 
      34              : using namespace muse::uicomponents;
      35              : 
      36            0 : WindowView::WindowView(QQuickItem* parent)
      37            0 :     : QObject(parent), Contextable(muse::iocCtxForQmlObject(this))
      38              : {
      39            0 : }
      40              : 
      41            0 : void WindowView::componentComplete()
      42              : {
      43            0 :     init();
      44            0 : }
      45              : 
      46            0 : void WindowView::init()
      47              : {
      48            0 :     IF_ASSERT_FAILED(engine()) {
      49            0 :         return;
      50              :     }
      51              : 
      52            0 :     initView();
      53            0 :     setViewContent(m_contentItem);
      54              : 
      55            0 :     connect(this, &WindowView::isContentReadyChanged, this, [this]() {
      56            0 :         if (isContentReady() && m_shouldOpenOnReady) {
      57            0 :             doOpen();
      58              :         }
      59            0 :     });
      60              : 
      61            0 :     navigationController()->navigationChanged().onNotify(this, [this]() {
      62            0 :         if (!(m_focusPolicies & FocusPolicy::TabFocus)) {
      63            0 :             return;
      64              :         }
      65              : 
      66            0 :         ui::INavigationPanel* navigationPanel = navigationController()->activePanel();
      67            0 :         if (!navigationPanel) {
      68            0 :             return;
      69              :         }
      70              : 
      71            0 :         if (navigationPanel->window() == m_view && !hasActiveFocus()) {
      72            0 :             forceActiveFocus();
      73              :         }
      74              :     });
      75              : 
      76            0 :     emit windowChanged();
      77              : }
      78              : 
      79            0 : void WindowView::initView()
      80              : {
      81              :     //! NOTE: do not set the window when constructing the view
      82              :     //! This causes different bugs on different OS (e.g., no transparency for popups on windows)
      83            0 :     m_view = new QQuickView(engine(), nullptr);
      84              : 
      85              :     //! NOTE: We must set the parent
      86              :     //! Otherwise, the garbage collector may take ownership of the view and destroy it when we don't expect it
      87            0 :     m_view->QObject::setParent(this);
      88              : 
      89            0 :     m_view->setObjectName(objectName() + "_WindowView_QQuickView");
      90            0 :     m_view->setResizeMode(QQuickView::SizeRootObjectToView);
      91              : 
      92              :     //! NOTE It is important that there is a connection to this signal with an error,
      93              :     //! otherwise the default action will be performed - displaying a message and terminating.
      94              :     //! We will not be able to switch to another backend.
      95            0 :     QObject::connect(m_view, &QQuickWindow::sceneGraphError, this, [this](QQuickWindow::SceneGraphError, const QString& msg) {
      96            0 :         LOGE() << "[" << objectName() << "] scene graph error: " << msg;
      97            0 :     });
      98              : 
      99            0 :     QObject::connect(m_view, &QQuickView::closing, this, [this](QQuickCloseEvent* event){
     100              :         // Ugly hack because QQuickCloseEvent is defined in a private Qt header
     101            0 :         emit aboutToClose(reinterpret_cast<QObject*>(event));
     102            0 :     });
     103              : 
     104            0 :     m_view->installEventFilter(this);
     105            0 : }
     106              : 
     107            0 : void WindowView::setViewContent(QQuickItem* item)
     108              : {
     109            0 :     if (!m_view || !item) {
     110            0 :         return;
     111              :     }
     112              : 
     113            0 :     m_view->setContent(QUrl(), nullptr, item);
     114              : 
     115            0 :     connect(item, &QQuickItem::implicitWidthChanged, this, [this, item]() {
     116            0 :         if (!m_view->isVisible()) {
     117            0 :             return;
     118              :         }
     119            0 :         if (item->implicitWidth() != m_view->width()) {
     120            0 :             updateSize(QSize(item->implicitWidth(), item->implicitHeight()));
     121              :         }
     122              :     });
     123              : 
     124            0 :     connect(item, &QQuickItem::implicitHeightChanged, this, [this, item]() {
     125            0 :         if (!m_view->isVisible()) {
     126            0 :             return;
     127              :         }
     128            0 :         if (item->implicitHeight() != m_view->height()) {
     129            0 :             updateSize(QSize(item->implicitWidth(), item->implicitHeight()));
     130              :         }
     131              :     });
     132              : }
     133              : 
     134            0 : void WindowView::open()
     135              : {
     136            0 :     if ((m_openPolicies & OpenPolicy::OpenOnContentReady) && !m_isContentReady) {
     137            0 :         m_shouldOpenOnReady = true;
     138            0 :         return;
     139              :     }
     140              : 
     141            0 :     doOpen();
     142              : }
     143              : 
     144            0 : void WindowView::beforeOpen()
     145              : {
     146            0 : }
     147              : 
     148            0 : void WindowView::doOpen()
     149              : {
     150            0 :     if (isOpened()) {
     151            0 :         repositionWindowIfNeed();
     152            0 :         return;
     153              :     }
     154              : 
     155            0 :     IF_ASSERT_FAILED(m_view) {
     156            0 :         return;
     157              :     }
     158              : 
     159            0 :     resolveParentWindow();
     160            0 :     updateGeometry();
     161              : 
     162            0 :     beforeOpen();
     163              : 
     164            0 :     resolveNavigationParentControl();
     165              : 
     166            0 :     showView();
     167              : 
     168            0 :     m_globalPos = QPointF(); // invalidate
     169              : 
     170            0 :     emit isOpenedChanged();
     171            0 :     emit opened();
     172              : }
     173              : 
     174            0 : void WindowView::showView()
     175              : {
     176            0 :     if (!m_view) {
     177            0 :         return;
     178              :     }
     179              : 
     180            0 :     const QRect geometry = viewGeometry();
     181            0 :     m_view->setGeometry(geometry);
     182              : 
     183            0 :     QScreen* screen = resolveScreen();
     184            0 :     m_view->setScreen(screen);
     185              : 
     186            0 :     if (m_openPolicies & OpenPolicy::NoActivateFocus) {
     187            0 :         m_view->setFlag(Qt::WindowDoesNotAcceptFocus);
     188              :     }
     189              : 
     190            0 :     assert(m_parentWindow);
     191            0 :     m_view->setTransientParent(m_parentWindow);
     192              : 
     193            0 :     connect(m_parentWindow, &QWindow::visibleChanged, this, [this](){
     194            0 :         if (!m_view->transientParent() || !m_view->transientParent()->isVisible()) {
     195            0 :             close();
     196              :         }
     197            0 :     });
     198              : 
     199            0 :     m_view->show();
     200            0 :     afterShow();
     201              : 
     202            0 :     const QQuickItem* item = m_view->rootObject();
     203            0 :     if (item) {
     204            0 :         updateSize(QSize(item->implicitWidth(), item->implicitHeight()));
     205              :     }
     206              : 
     207            0 :     if (!(m_openPolicies & OpenPolicy::NoActivateFocus)) {
     208            0 :         QTimer::singleShot(0, this, [this]() {
     209            0 :             forceActiveFocus();
     210            0 :         });
     211              :     }
     212              : }
     213              : 
     214            0 : void WindowView::onHidden()
     215              : {
     216            0 :     emit isOpenedChanged();
     217            0 :     emit closed(m_forceClosed);
     218              : 
     219            0 :     activateNavigationParentControl();
     220            0 : }
     221              : 
     222            0 : void WindowView::close(bool force)
     223              : {
     224            0 :     if (!isOpened()) {
     225            0 :         return;
     226              :     }
     227              : 
     228            0 :     IF_ASSERT_FAILED(m_view) {
     229            0 :         return;
     230              :     }
     231              : 
     232            0 :     m_forceClosed = force;
     233            0 :     m_view->close();
     234              : }
     235              : 
     236            0 : void WindowView::toggleOpened()
     237              : {
     238            0 :     if (isOpened()) {
     239            0 :         close();
     240              :     } else {
     241            0 :         open();
     242              :     }
     243            0 : }
     244              : 
     245            0 : bool WindowView::isOpened() const
     246              : {
     247            0 :     return m_view ? m_view->isVisible() : false;
     248              : }
     249              : 
     250            0 : bool WindowView::eventFilter(QObject* watched, QEvent* event)
     251              : {
     252            0 :     if (watched == m_view) {
     253            0 :         switch (event->type()) {
     254            0 :         case QEvent::Hide:
     255            0 :             onHidden();
     256            0 :             break;
     257            0 :         case QEvent::FocusIn:
     258            0 :             if (m_view->rootObject()) {
     259            0 :                 m_view->rootObject()->forceActiveFocus();
     260              :             }
     261            0 :             break;
     262            0 :         case QEvent::FocusOut:
     263            0 :             if (!(m_focusPolicies & FocusPolicy::ClickFocus)) {
     264              :                 // Undo what's done in TextInput{Field,Area}.qml ensureActiveFocus
     265            0 :                 m_view->setFlag(Qt::WindowDoesNotAcceptFocus);
     266              :             }
     267            0 :             break;
     268            0 :         case QEvent::MouseButtonPress:
     269            0 :             if (m_focusPolicies & FocusPolicy::ClickFocus) {
     270            0 :                 forceActiveFocus();
     271              :             }
     272            0 :             break;
     273            0 :         default:
     274            0 :             break;
     275              :         }
     276              :     }
     277              : 
     278            0 :     return QObject::eventFilter(watched, event);
     279              : }
     280              : 
     281            0 : QQmlEngine* WindowView::engine() const
     282              : {
     283            0 :     if (m_engine) {
     284            0 :         return m_engine;
     285              :     }
     286              : 
     287            0 :     return qmlEngine(this);
     288              : }
     289              : 
     290            0 : void WindowView::setEngine(QQmlEngine* engine)
     291              : {
     292            0 :     if (m_engine == engine) {
     293            0 :         return;
     294              :     }
     295              : 
     296            0 :     m_engine = engine;
     297              : }
     298              : 
     299            0 : QQuickItem* WindowView::parentItem() const
     300              : {
     301            0 :     if (!parent()) {
     302            0 :         return nullptr;
     303              :     }
     304              : 
     305            0 :     return qobject_cast<QQuickItem*>(parent());
     306              : }
     307              : 
     308            0 : void WindowView::setParentItem(QQuickItem* parent)
     309              : {
     310            0 :     if (parentItem() == parent) {
     311            0 :         return;
     312              :     }
     313              : 
     314            0 :     QObject::setParent(parent);
     315            0 :     emit parentItemChanged();
     316              : }
     317              : 
     318            0 : QQuickItem* WindowView::contentItem() const
     319              : {
     320            0 :     return m_contentItem;
     321              : }
     322              : 
     323            0 : void WindowView::setContentItem(QQuickItem* content)
     324              : {
     325            0 :     if (m_contentItem == content) {
     326            0 :         return;
     327              :     }
     328              : 
     329            0 :     m_contentItem = content;
     330            0 :     emit contentItemChanged();
     331              : }
     332              : 
     333            0 : int WindowView::contentWidth() const
     334              : {
     335            0 :     return m_contentWidth;
     336              : }
     337              : 
     338            0 : void WindowView::setContentWidth(int contentWidth)
     339              : {
     340            0 :     if (m_contentWidth == contentWidth) {
     341            0 :         return;
     342              :     }
     343              : 
     344            0 :     m_contentWidth = contentWidth;
     345            0 :     emit contentWidthChanged();
     346              : }
     347              : 
     348            0 : int WindowView::contentHeight() const
     349              : {
     350            0 :     return m_contentHeight;
     351              : }
     352              : 
     353            0 : void WindowView::setContentHeight(int contentHeight)
     354              : {
     355            0 :     if (m_contentHeight == contentHeight) {
     356            0 :         return;
     357              :     }
     358              : 
     359            0 :     m_contentHeight = contentHeight;
     360            0 :     emit contentHeightChanged();
     361              : }
     362              : 
     363            0 : QWindow* WindowView::window() const
     364              : {
     365            0 :     return m_view;
     366              : }
     367              : 
     368            0 : QRect WindowView::geometry() const
     369              : {
     370            0 :     return m_view ? m_view->geometry() : QRect();
     371              : }
     372              : 
     373            0 : bool WindowView::hasActiveFocus()
     374              : {
     375            0 :     return m_view && m_view->activeFocusItem() != nullptr;
     376              : }
     377              : 
     378            0 : void WindowView::forceActiveFocus()
     379              : {
     380            0 :     if (!m_view) {
     381            0 :         return;
     382              :     }
     383              : 
     384            0 :     m_view->setFlag(Qt::WindowDoesNotAcceptFocus, false);
     385            0 :     m_view->requestActivate();
     386              : 
     387            0 :     QQuickItem* rootObject = m_view->rootObject();
     388            0 :     if (!rootObject) {
     389            0 :         return;
     390              :     }
     391              : 
     392            0 :     if (!rootObject->hasActiveFocus()) {
     393            0 :         rootObject->forceActiveFocus();
     394              :     }
     395              : }
     396              : 
     397            0 : QWindow* WindowView::parentWindow() const
     398              : {
     399            0 :     return m_parentWindow;
     400              : }
     401              : 
     402            0 : void WindowView::setParentWindow(QWindow* window)
     403              : {
     404            0 :     if (m_parentWindow == window) {
     405            0 :         return;
     406              :     }
     407              : 
     408            0 :     m_parentWindow = window;
     409            0 :     emit parentWindowChanged();
     410              : }
     411              : 
     412            0 : void WindowView::resolveParentWindow()
     413              : {
     414            0 :     if (m_parentWindow) {
     415            0 :         return;
     416              :     }
     417              : 
     418            0 :     if (QQuickItem* parent = parentItem()) {
     419            0 :         if (QWindow* window = parent->window()) {
     420            0 :             setParentWindow(window);
     421            0 :             return;
     422              :         }
     423              :     }
     424              : 
     425            0 :     setParentWindow(interactive()->topWindow());
     426              : }
     427              : 
     428            0 : QScreen* WindowView::resolveScreen() const
     429              : {
     430            0 :     QScreen* screen = m_parentWindow ? m_parentWindow->screen() : nullptr;
     431            0 :     if (!screen) {
     432            0 :         screen = QGuiApplication::primaryScreen();
     433              :     }
     434            0 :     return screen;
     435              : }
     436              : 
     437            0 : QRect WindowView::currentScreenGeometry() const
     438              : {
     439            0 :     QScreen* screen = resolveScreen();
     440              : 
     441            0 :     QRect geometry;
     442              : 
     443              :     // See PR #10558
     444              : #ifndef Q_OS_MAC
     445            0 :     geometry = mainWindow()->isFullScreen() ? screen->geometry() : screen->availableGeometry();
     446              : #else
     447              :     geometry = screen->availableGeometry();
     448              : #endif
     449              : 
     450            0 :     return geometry;
     451              : }
     452              : 
     453            0 : QRect WindowView::viewGeometry() const
     454              : {
     455            0 :     return QRect(m_globalPos.toPoint(), contentItem()->size().toSize());
     456              : }
     457              : 
     458            0 : void WindowView::updateSize(const QSize& newSize)
     459              : {
     460            0 :     if (!m_view) {
     461            0 :         return;
     462              :     }
     463              : 
     464            0 :     if (m_resizable) {
     465            0 :         m_view->setMinimumSize(newSize);
     466            0 :         m_view->setMaximumSize(QSize(16777215, 16777215));
     467            0 :         m_view->resize(m_view->size().expandedTo(newSize));
     468              :     } else {
     469            0 :         m_view->setMinimumSize(newSize);
     470            0 :         m_view->setMaximumSize(newSize);
     471            0 :         m_view->resize(newSize);
     472              :     }
     473              : }
     474              : 
     475            0 : WindowView::OpenPolicies WindowView::openPolicies() const
     476              : {
     477            0 :     return m_openPolicies;
     478              : }
     479              : 
     480            0 : void WindowView::setOpenPolicies(WindowView::OpenPolicies openPolicies)
     481              : {
     482            0 :     if (m_openPolicies == openPolicies) {
     483            0 :         return;
     484              :     }
     485              : 
     486            0 :     m_openPolicies = openPolicies;
     487            0 :     emit openPoliciesChanged(m_openPolicies);
     488              : }
     489              : 
     490            0 : WindowView::FocusPolicies WindowView::focusPolicies() const
     491              : {
     492            0 :     return m_focusPolicies;
     493              : }
     494              : 
     495            0 : void WindowView::setFocusPolicies(const FocusPolicies& policies)
     496              : {
     497            0 :     if (m_focusPolicies == policies) {
     498            0 :         return;
     499              :     }
     500              : 
     501            0 :     m_focusPolicies = policies;
     502            0 :     emit focusPoliciesChanged();
     503              : }
     504              : 
     505            0 : bool WindowView::activateParentOnClose() const
     506              : {
     507            0 :     return m_activateParentOnClose;
     508              : }
     509              : 
     510            0 : void WindowView::setActivateParentOnClose(bool activateParentOnClose)
     511              : {
     512            0 :     if (m_activateParentOnClose == activateParentOnClose) {
     513            0 :         return;
     514              :     }
     515              : 
     516            0 :     m_activateParentOnClose = activateParentOnClose;
     517            0 :     emit activateParentOnCloseChanged(m_activateParentOnClose);
     518              : }
     519              : 
     520            0 : muse::ui::INavigationControl* WindowView::navigationParentControl() const
     521              : {
     522            0 :     return m_navigationParentControl;
     523              : }
     524              : 
     525            0 : void WindowView::setNavigationParentControl(ui::INavigationControl* navigationParentControl)
     526              : {
     527            0 :     if (m_navigationParentControl == navigationParentControl) {
     528            0 :         return;
     529              :     }
     530              : 
     531            0 :     m_navigationParentControl = navigationParentControl;
     532            0 :     emit navigationParentControlChanged(m_navigationParentControl);
     533              : }
     534              : 
     535            0 : void WindowView::resolveNavigationParentControl()
     536              : {
     537            0 :     ui::INavigationControl* ctrl = navigationController()->activeControl();
     538            0 :     setNavigationParentControl(ctrl);
     539              : 
     540              :     //! NOTE At the moment we have only qml navigation controls
     541            0 :     QObject* qmlCtrl = dynamic_cast<QObject*>(ctrl);
     542              : 
     543            0 :     if (qmlCtrl) {
     544            0 :         connect(qmlCtrl, &QObject::destroyed, this, [this]() {
     545            0 :             setNavigationParentControl(nullptr);
     546            0 :         });
     547              :     }
     548            0 : }
     549              : 
     550            0 : void WindowView::activateNavigationParentControl()
     551              : {
     552            0 :     if (m_activateParentOnClose && m_navigationParentControl) {
     553            0 :         m_navigationParentControl->requestActive();
     554              :     }
     555            0 : }
     556              : 
     557            0 : bool WindowView::isContentReady() const
     558              : {
     559            0 :     return m_isContentReady;
     560              : }
     561              : 
     562            0 : void WindowView::setIsContentReady(bool ready)
     563              : {
     564            0 :     if (m_isContentReady == ready) {
     565            0 :         return;
     566              :     }
     567              : 
     568            0 :     m_isContentReady = ready;
     569            0 :     emit isContentReadyChanged();
     570              : }
        

Generated by: LCOV version 2.0-1