Implemented PopupViewCloseController

This commit is contained in:
Eism 2022-05-16 18:58:14 +03:00 committed by Elnur Ismailzada
parent c1a055f6ce
commit a5cfb7a02a
8 changed files with 308 additions and 100 deletions

View file

@ -68,6 +68,8 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/view/abstractmenumodel.h
${CMAKE_CURRENT_LIST_DIR}/view/menuitem.cpp
${CMAKE_CURRENT_LIST_DIR}/view/menuitem.h
${CMAKE_CURRENT_LIST_DIR}/view/popupviewclosecontroller.cpp
${CMAKE_CURRENT_LIST_DIR}/view/popupviewclosecontroller.h
${CMAKE_CURRENT_LIST_DIR}/view/widgets/radiobuttongroupbox.cpp
${CMAKE_CURRENT_LIST_DIR}/view/widgets/radiobuttongroupbox.h
${CMAKE_CURRENT_LIST_DIR}/view/selectmultipledirectoriesmodel.cpp

View file

@ -97,6 +97,8 @@ void UiComponentsModule::registerUiTypes()
qmlRegisterType<DropdownView>("MuseScore.UiComponents", 1, 0, "DropdownView");
qmlRegisterType<MenuView>("MuseScore.UiComponents", 1, 0, "MenuView");
qRegisterMetaType<PopupViewCloseController::ClosePolicy>("PopupView::ClosePolicy");
qmlRegisterType<FilePickerModel>("MuseScore.UiComponents", 1, 0, "FilePickerModel");
qmlRegisterType<ItemMultiSelectionModel>("MuseScore.UiComponents", 1, 0, "ItemMultiSelectionModel");

View file

@ -34,7 +34,7 @@ DialogView::DialogView(QQuickItem* parent)
: PopupView(parent)
{
setObjectName("DialogView");
m_closePolicy = NoAutoClose;
setClosePolicy(PopupViewCloseController::NoAutoClose);
}
bool DialogView::isDialog() const

View file

@ -29,7 +29,6 @@
#include <QUrl>
#include <QQmlContext>
#include <QApplication>
#include <QMainWindow>
#include <QTimer>
#include <QScreen>
@ -49,7 +48,12 @@ PopupView::PopupView(QQuickItem* parent)
setPadding(12);
setShowArrow(true);
connect(qApp, &QApplication::applicationStateChanged, this, &PopupView::onApplicationStateChanged);
m_closeController = new PopupViewCloseController();
m_closeController->init();
m_closeController->closeNotification().onNotify(this, [this]() {
close();
});
}
QQuickItem* PopupView::parentItem() const
@ -107,35 +111,15 @@ void PopupView::componentComplete()
//connect(m_window, &IPopupWindow::aboutToClose, this, &PopupView::aboutToClose);
connect(m_window, SIGNAL(aboutToClose(QQuickCloseEvent*)), this, SIGNAL(aboutToClose(QQuickCloseEvent*)));
connect(parentItem(), &QQuickItem::visibleChanged, this, [this]() {
if (!parentItem() || !parentItem()->isVisible()) {
close();
}
});
emit windowChanged();
}
bool PopupView::eventFilter(QObject* watched, QEvent* event)
{
if (QEvent::Close == event->type() && watched == mainWindow()->qWindow()) {
close();
} else if (QEvent::UpdateRequest == event->type()) {
if (QEvent::UpdateRequest == event->type()) {
repositionWindowIfNeed();
}
if (m_openPolicy == OpenPolicy::NoActivateFocus) {
if (QEvent::MouseButtonPress == event->type()) {
doFocusOut();
} else if (QEvent::Move == event->type() && watched == mainWindow()->qWindow()) {
windowMoveEvent();
}
} else {
if (QEvent::FocusOut == event->type() && watched == window()) {
doFocusOut();
}
}
return QObject::eventFilter(watched, event);
}
@ -185,6 +169,10 @@ void PopupView::open()
m_globalPos = QPointF(); // invalidate
m_closeController->setParentItem(parentItem());
m_closeController->setWindow(window());
m_closeController->setActive(true);
qApp->installEventFilter(this);
emit isOpenedChanged();
@ -207,6 +195,8 @@ void PopupView::close()
return;
}
m_closeController->setActive(false);
qApp->removeEventFilter(this);
m_window->close();
@ -238,9 +228,9 @@ PopupView::OpenPolicy PopupView::openPolicy() const
return m_openPolicy;
}
PopupView::ClosePolicy PopupView::closePolicy() const
PopupViewCloseController::ClosePolicy PopupView::closePolicy() const
{
return m_closePolicy;
return m_closeController ? m_closeController->closePolicy() : PopupViewCloseController::ClosePolicy::CloseOnPressOutsideParent;
}
bool PopupView::activateParentOnClose() const
@ -329,6 +319,11 @@ void PopupView::setOpenPolicy(PopupView::OpenPolicy openPolicy)
}
m_openPolicy = openPolicy;
if (m_closeController) {
m_closeController->setPopupHasFocus(m_openPolicy != OpenPolicy::NoActivateFocus);
}
emit openPolicyChanged(m_openPolicy);
}
@ -343,67 +338,12 @@ void PopupView::repositionWindowIfNeed()
}
}
void PopupView::setClosePolicy(ClosePolicy closePolicy)
void PopupView::setClosePolicy(PopupViewCloseController::ClosePolicy closePolicy)
{
if (m_closePolicy == closePolicy) {
return;
}
m_closePolicy = closePolicy;
m_closeController->setClosePolicy(closePolicy);
emit closePolicyChanged(closePolicy);
}
void PopupView::onApplicationStateChanged(Qt::ApplicationState state)
{
if (m_closePolicy == NoAutoClose) {
return;
}
if (state != Qt::ApplicationActive) {
close();
}
}
void PopupView::doFocusOut()
{
if (!isOpened()) {
return;
}
if (m_closePolicy == ClosePolicy::CloseOnPressOutsideParent) {
if (!isMouseWithinBoundaries(QCursor::pos())) {
close();
}
}
}
void PopupView::windowMoveEvent()
{
if (!isOpened()) {
return;
}
if (m_closePolicy == ClosePolicy::CloseOnPressOutsideParent) {
close();
}
}
bool PopupView::isMouseWithinBoundaries(const QPoint& mousePos) const
{
QRect viewRect = m_window->geometry();
bool contains = viewRect.contains(mousePos);
if (!contains) {
//! NOTE We also check the parent because often clicking on the parent should toggle the popup,
//! but if we don't check a parent here, the popup will be closed and reopened.
QQuickItem* prn = parentItem();
QPointF localPos = prn->mapFromGlobal(mousePos);
QRectF parentRect = QRectF(0, 0, prn->width(), prn->height());
contains = parentRect.contains(localPos);
}
return contains;
}
void PopupView::setObjectId(QString objectId)
{
if (m_objectId == objectId) {

View file

@ -27,17 +27,21 @@
#include <QQmlParserStatus>
#include "ret.h"
#include "async/asyncable.h"
#include "modularity/ioc.h"
#include "ui/imainwindow.h"
#include "ui/iuiconfiguration.h"
#include "ui/inavigationcontroller.h"
#include "ui/view/navigationcontrol.h"
#include "popupwindow/ipopupwindow.h"
#include "popupviewclosecontroller.h"
class QQuickCloseEvent;
namespace mu::uicomponents {
class PopupView : public QObject, public QQmlParserStatus
class PopupView : public QObject, public QQmlParserStatus, async::Asyncable
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@ -60,7 +64,8 @@ class PopupView : public QObject, public QQmlParserStatus
Q_PROPERTY(bool isOpened READ isOpened NOTIFY isOpenedChanged)
Q_PROPERTY(OpenPolicy openPolicy READ openPolicy WRITE setOpenPolicy NOTIFY openPolicyChanged)
Q_PROPERTY(ClosePolicy closePolicy READ closePolicy WRITE setClosePolicy NOTIFY closePolicyChanged)
Q_PROPERTY(
mu::uicomponents::PopupViewCloseController::ClosePolicy closePolicy READ closePolicy WRITE setClosePolicy NOTIFY closePolicyChanged)
Q_PROPERTY(
bool activateParentOnClose READ activateParentOnClose WRITE setActivateParentOnClose NOTIFY activateParentOnCloseChanged)
@ -73,7 +78,6 @@ class PopupView : public QObject, public QQmlParserStatus
Q_PROPERTY(QVariantMap ret READ ret WRITE setRet NOTIFY retChanged)
Q_ENUMS(OpenPolicy)
Q_ENUMS(ClosePolicy)
INJECT(uicomponents, ui::IMainWindow, mainWindow)
INJECT(uicomponents, ui::IUiConfiguration, uiConfiguration)
@ -89,11 +93,6 @@ public:
NoActivateFocus
};
enum ClosePolicy {
NoAutoClose = 0,
CloseOnPressOutsideParent
};
QQuickItem* parentItem() const;
QQuickItem* contentItem() const;
@ -112,7 +111,7 @@ public:
Q_INVOKABLE void setParentWindow(QWindow* window);
OpenPolicy openPolicy() const;
ClosePolicy closePolicy() const;
PopupViewCloseController::ClosePolicy closePolicy() const;
bool activateParentOnClose() const;
@ -138,7 +137,7 @@ public slots:
void setLocalX(qreal x);
void setLocalY(qreal y);
void setOpenPolicy(OpenPolicy openPolicy);
void setClosePolicy(ClosePolicy closePolicy);
void setClosePolicy(PopupViewCloseController::ClosePolicy closePolicy);
void setNavigationParentControl(ui::INavigationControl* parentNavigationControl);
void setObjectId(QString objectId);
void setTitle(QString title);
@ -161,7 +160,7 @@ signals:
void xChanged(qreal x);
void yChanged(qreal y);
void openPolicyChanged(OpenPolicy openPolicy);
void closePolicyChanged(ClosePolicy closePolicy);
void closePolicyChanged(PopupViewCloseController::ClosePolicy closePolicy);
void navigationParentControlChanged(ui::INavigationControl* navigationParentControl);
void objectIdChanged(QString objectId);
void titleChanged(QString title);
@ -182,9 +181,6 @@ signals:
void activateParentOnCloseChanged(bool activateParentOnClose);
private slots:
void onApplicationStateChanged(Qt::ApplicationState state);
protected:
virtual bool isDialog() const;
void classBegin() override;
@ -223,7 +219,6 @@ protected:
QPointF m_localPos;
QPointF m_globalPos;
OpenPolicy m_openPolicy = OpenPolicy::Default;
ClosePolicy m_closePolicy = ClosePolicy::CloseOnPressOutsideParent;
bool m_activateParentOnClose = true;
ui::INavigationControl* m_navigationParentControl = nullptr;
QString m_objectId;
@ -235,6 +230,8 @@ protected:
int m_arrowX = 0;
int m_padding = 0;
bool m_showArrow = false;
PopupViewCloseController* m_closeController = nullptr;
};
}

View file

@ -0,0 +1,171 @@
/*
* 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 "popupviewclosecontroller.h"
#include <QApplication>
using namespace mu::uicomponents;
PopupViewCloseController::PopupViewCloseController(QQuickItem* parent)
: QObject(parent)
{
}
void PopupViewCloseController::init()
{
connect(qApp, &QApplication::applicationStateChanged, this, &PopupViewCloseController::onApplicationStateChanged);
}
PopupViewCloseController::ClosePolicy PopupViewCloseController::closePolicy() const
{
return m_closePolicy;
}
void PopupViewCloseController::setClosePolicy(ClosePolicy closePolicy)
{
if (m_closePolicy == closePolicy) {
return;
}
m_closePolicy = closePolicy;
}
void PopupViewCloseController::setActive(bool active)
{
m_active = active;
if (active) {
qApp->installEventFilter(this);
} else {
qApp->removeEventFilter(this);
}
}
QQuickItem* PopupViewCloseController::parentItem() const
{
return m_parentItem;
}
void PopupViewCloseController::setParentItem(QQuickItem* parentItem)
{
m_parentItem = parentItem;
connect(m_parentItem, &QQuickItem::visibleChanged, this, [this]() {
if (!m_parentItem || !m_parentItem->isVisible()) {
notifyAboutClose();
}
});
}
void PopupViewCloseController::setWindow(QWindow* window)
{
m_popupWindow = window;
}
void PopupViewCloseController::setPopupHasFocus(bool hasFocus)
{
if (m_popupHasFocus == hasFocus) {
return;
}
m_popupHasFocus = hasFocus;
}
mu::async::Notification PopupViewCloseController::closeNotification() const
{
return m_closeNotification;
}
bool PopupViewCloseController::eventFilter(QObject* watched, QEvent* event)
{
if (QEvent::Close == event->type() && watched == mainWindow()->qWindow()) {
notifyAboutClose();
}
if (!m_popupHasFocus) {
if (QEvent::MouseButtonPress == event->type()) {
doFocusOut();
}
} else {
if (QEvent::FocusOut == event->type() && watched == popupWindow()) {
doFocusOut();
}
}
return QObject::eventFilter(watched, event);
}
void PopupViewCloseController::onApplicationStateChanged(Qt::ApplicationState state)
{
if (m_closePolicy == NoAutoClose) {
return;
}
if (state != Qt::ApplicationActive) {
notifyAboutClose();
}
}
void PopupViewCloseController::doFocusOut()
{
if (m_closePolicy == ClosePolicy::CloseOnPressOutsideParent) {
if (!isMouseWithinBoundaries(QCursor::pos())) {
notifyAboutClose();
}
}
}
bool PopupViewCloseController::isMouseWithinBoundaries(const QPoint& mousePos) const
{
QWindow* window = popupWindow();
if (!window) {
return false;
}
QRect viewRect = window->geometry();
bool contains = viewRect.contains(mousePos);
if (!contains) {
//! NOTE We also check the parent because often clicking on the parent should toggle the popup,
//! but if we don't check a parent here, the popup will be closed and reopened.
QQuickItem* parent = parentItem();
QPointF localPos = parent->mapFromGlobal(mousePos);
QRectF parentRect = QRectF(0, 0, parent->width(), parent->height());
contains = parentRect.contains(localPos);
}
return contains;
}
void PopupViewCloseController::notifyAboutClose()
{
if (!m_active) {
return;
}
m_closeNotification.notify();
}
QWindow* PopupViewCloseController::popupWindow() const
{
return m_popupWindow;
}

View file

@ -0,0 +1,96 @@
/*
* 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_UICOMPONENTS_POPUPVIEWCLOSECONTROLLER_H
#define MU_UICOMPONENTS_POPUPVIEWCLOSECONTROLLER_H
#include <QObject>
#include <QQuickItem>
#include "async/notification.h"
#include "modularity/ioc.h"
#include "ui/imainwindow.h"
namespace mu::uicomponents {
class PopupViewCloseController : public QObject
{
Q_OBJECT
INJECT(uicomponents, ui::IMainWindow, mainWindow)
Q_ENUMS(ClosePolicy)
public:
explicit PopupViewCloseController(QQuickItem* parent = nullptr);
~PopupViewCloseController() override = default;
enum ClosePolicy {
NoAutoClose = 0,
CloseOnPressOutsideParent
};
void init();
ClosePolicy closePolicy() const;
void setClosePolicy(ClosePolicy closePolicy);
void setActive(bool active);
QQuickItem* parentItem() const;
void setParentItem(QQuickItem* parentItem);
QWindow* popupWindow() const;
void setWindow(QWindow* window);
void setPopupHasFocus(bool hasFocus);
async::Notification closeNotification() const;
private slots:
void onApplicationStateChanged(Qt::ApplicationState state);
protected:
bool eventFilter(QObject* watched, QEvent* event) override;
void doFocusOut();
bool isMouseWithinBoundaries(const QPoint& mousePos) const;
private:
void notifyAboutClose();
bool m_active = false;
ClosePolicy m_closePolicy = ClosePolicy::CloseOnPressOutsideParent;
QQuickItem* m_parentItem = nullptr;
QWindow* m_popupWindow = nullptr;
bool m_popupHasFocus = true;
async::Notification m_closeNotification;
};
}
Q_DECLARE_METATYPE(mu::uicomponents::PopupViewCloseController::ClosePolicy);
#endif // MU_UICOMPONENTS_POPUPVIEWCLOSECONTROLLER_H

View file

@ -24,12 +24,13 @@
#include <QObject>
#include <QQuickView>
#include "ipopupwindow.h"
#include "modularity/ioc.h"
#include "ui/iinteractiveprovider.h"
#include "ui/imainwindow.h"
#include "ipopupwindow.h"
namespace mu::uicomponents {
class PopupWindow_QQuickView : public IPopupWindow
{
@ -68,7 +69,6 @@ public:
void setOnHidden(const std::function<void()>& callback) override;
private:
bool eventFilter(QObject* watched, QEvent* event) override;
void updateSize(const QSize& newSize);