Skip to content

Commit

Permalink
Merge pull request #9484 from Eism/accessibility_orca_cancel_reading
Browse files Browse the repository at this point in the history
[MU4] Accessibility. Orca cancel reading
  • Loading branch information
Eism authored Oct 18, 2021
2 parents 647c8c6 + fad1cf4 commit 3599b63
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,11 @@ QAccessibleInterface* AccessibilityController::parentIface(const IAccessible* it
}

if (it.item->accessibleRole() == IAccessible::Role::Application) {
return QAccessible::queryAccessibleInterface(mainWindow()->topWindow());
if (!qApp->isQuitLockEnabled()) {
return QAccessible::queryAccessibleInterface(mainWindow()->topWindow());
} else {
return QAccessible::queryAccessibleInterface(qApp->focusWindow());
}
}

return it.iface;
Expand Down
5 changes: 5 additions & 0 deletions src/framework/global/iapplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@

#include "modularity/imoduleexport.h"

class QObject;
class QEvent;

namespace mu::framework {
class IApplication : MODULE_EXPORT_INTERFACE
{
Expand All @@ -39,6 +42,8 @@ class IApplication : MODULE_EXPORT_INTERFACE
virtual void setRunMode(const RunMode& mode) = 0;
virtual RunMode runMode() const = 0;
virtual bool noGui() const = 0;

virtual bool notify(QObject* object, QEvent* event) = 0;
};
}

Expand Down
7 changes: 7 additions & 0 deletions src/framework/global/internal/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
*/
#include "application.h"

#include <QApplication>

using namespace mu::framework;

void Application::setRunMode(const RunMode& mode)
Expand All @@ -41,3 +43,8 @@ bool Application::noGui() const
}
return false;
}

bool Application::notify(QObject* object, QEvent* event)
{
return qApp->notify(object, event);
}
2 changes: 2 additions & 0 deletions src/framework/global/internal/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class Application : public IApplication
RunMode runMode() const override;
bool noGui() const override;

bool notify(QObject* object, QEvent* event) override;

private:

RunMode m_runMode = RunMode::Editor;
Expand Down
1 change: 1 addition & 0 deletions src/framework/global/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set(MODULE_TEST_SRC
${CMAKE_CURRENT_LIST_DIR}/uri_tests.cpp
${CMAKE_CURRENT_LIST_DIR}/val_tests.cpp
${CMAKE_CURRENT_LIST_DIR}/logremover_tests.cpp
${CMAKE_CURRENT_LIST_DIR}/mocks/applicationmock.h
)

include(${PROJECT_SOURCE_DIR}/src/framework/testing/gtest.cmake)
Expand Down
41 changes: 41 additions & 0 deletions src/framework/global/tests/mocks/applicationmock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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_FRAMEWORK_APPLICATIONMOCK_H
#define MU_FRAMEWORK_APPLICATIONMOCK_H

#include <gmock/gmock.h>

#include "framework/global/iapplication.h"

namespace mu::framework {
class ApplicationMock : public IApplication
{
public:
MOCK_METHOD(void, setRunMode, (const RunMode&), (override));
MOCK_METHOD(RunMode, runMode, (), (const, override));
MOCK_METHOD(bool, noGui, (), (const, override));

MOCK_METHOD(bool, notify, (QObject*, QEvent*), (override));
};
}

#endif // MU_FRAMEWORK_APPLICATIONMOCK_H
7 changes: 7 additions & 0 deletions src/framework/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ set(MODULE_LINK
accessibility
)

if (OS_IS_LIN)
# it needed to send a spontaneous event by activating navigation
set(MODULE_INCLUDE
${Qt5Core_PRIVATE_INCLUDE_DIRS}
)
endif()

if (OS_IS_MAC)
find_library(AppKit NAMES AppKit)
set(MODULE_LINK ${MODULE_LINK} ${AppKit})
Expand Down
99 changes: 82 additions & 17 deletions src/framework/ui/internal/navigationcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@
*/
#include "navigationcontroller.h"

#include <QCoreApplication>
#include <algorithm>
#include <limits>
#include <utility>

#include <QCoreApplication>
#include <QWindow>
#include <QKeyEvent>

#ifdef Q_OS_LINUX
#include <private/qcoreapplication_p.h>
#endif

#include "diagnostics/diagnosticutils.h"
#include "async/async.h"
#include "log.h"
Expand Down Expand Up @@ -268,26 +275,26 @@ static T* findByIndex(const std::set<T*>& set, const INavigation::Index& idx)

void NavigationController::init()
{
dispatcher()->reg(this, "nav-next-section", this, &NavigationController::goToNextSection);
dispatcher()->reg(this, "nav-prev-section", [this]() { goToPrevSection(false); });
dispatcher()->reg(this, "nav-next-panel", this, &NavigationController::goToNextPanel);
dispatcher()->reg(this, "nav-prev-panel", this, &NavigationController::goToPrevPanel);
dispatcher()->reg(this, "nav-next-section", [this]() { navigateTo(NavigationType::NextSection); });
dispatcher()->reg(this, "nav-prev-section", [this]() { navigateTo(NavigationType::PrevSection); });
dispatcher()->reg(this, "nav-next-panel", [this]() { navigateTo(NavigationType::NextPanel); });
dispatcher()->reg(this, "nav-prev-panel", [this]() { navigateTo(NavigationType::PrevPanel); });
//! NOTE Same as panel at the moment
dispatcher()->reg(this, "nav-next-tab", this, &NavigationController::goToNextPanel);
dispatcher()->reg(this, "nav-prev-tab", this, &NavigationController::goToPrevPanel);
dispatcher()->reg(this, "nav-next-tab", [this]() { navigateTo(NavigationType::NextPanel); });
dispatcher()->reg(this, "nav-prev-tab", [this]() { navigateTo(NavigationType::PrevPanel); });

dispatcher()->reg(this, "nav-trigger-control", this, &NavigationController::doTriggerControl);
dispatcher()->reg(this, "nav-trigger-control", [this]() { navigateTo(NavigationType::TriggerControl); });

dispatcher()->reg(this, "nav-right", this, &NavigationController::onRight);
dispatcher()->reg(this, "nav-left", this, &NavigationController::onLeft);
dispatcher()->reg(this, "nav-up", this, &NavigationController::onUp);
dispatcher()->reg(this, "nav-down", this, &NavigationController::onDown);
dispatcher()->reg(this, "nav-escape", this, &NavigationController::onEscape);
dispatcher()->reg(this, "nav-right", [this]() { navigateTo(NavigationType::Right); });
dispatcher()->reg(this, "nav-left", [this]() { navigateTo(NavigationType::Left); });
dispatcher()->reg(this, "nav-up", [this]() { navigateTo(NavigationType::Up); });
dispatcher()->reg(this, "nav-down", [this]() { navigateTo(NavigationType::Down); });
dispatcher()->reg(this, "nav-escape", [this]() { navigateTo(NavigationType::Escape); });

dispatcher()->reg(this, "nav-first-control", this, &NavigationController::goToFirstControl); // typically Home key
dispatcher()->reg(this, "nav-last-control", this, &NavigationController::goToLastControl); // typically End key
dispatcher()->reg(this, "nav-nextrow-control", this, &NavigationController::goToNextRowControl); // typically PageDown key
dispatcher()->reg(this, "nav-prevrow-control", this, &NavigationController::goToPrevRowControl); // typically PageUp key
dispatcher()->reg(this, "nav-first-control", [this]() { navigateTo(NavigationType::FirstControl); }); // typically Home key
dispatcher()->reg(this, "nav-last-control", [this]() { navigateTo(NavigationType::LastControl); }); // typically End key
dispatcher()->reg(this, "nav-nextrow-control", [this]() { navigateTo(NavigationType::NextRowControl); }); // typically PageDown key
dispatcher()->reg(this, "nav-prevrow-control", [this]() { navigateTo(NavigationType::PrevRowControl); }); // typically PageUp key

qApp->installEventFilter(this);
}
Expand Down Expand Up @@ -344,6 +351,64 @@ bool NavigationController::eventFilter(QObject* watched, QEvent* event)
return QObject::eventFilter(watched, event);
}

void NavigationController::navigateTo(NavigationController::NavigationType type)
{
#ifdef Q_OS_LINUX
//! HACK: it needs for canceling reading the name of previous control on accessibility
QKeyEvent* keyEvent = new QKeyEvent(QEvent::Type::KeyPress, Qt::Key_Cancel, Qt::KeyboardModifier::NoModifier, 0, 1, 0);
QCoreApplicationPrivate::setEventSpontaneous(keyEvent, true);
application()->notify(mainWindow()->qWindow(), keyEvent);
#endif

switch (type) {
case NavigationType::NextSection:
goToNextSection();
break;
case NavigationType::PrevSection:
goToPrevSection(false);
break;
case NavigationType::PrevSectionActiveLastPanel:
goToPrevSection(true);
break;
case NavigationType::NextPanel:
goToNextPanel();
break;
case NavigationType::PrevPanel:
goToPrevPanel();
break;
case NavigationType::Left:
onLeft();
break;
case NavigationType::Right:
onRight();
break;
case NavigationType::Up:
onUp();
break;
case NavigationType::Down:
onDown();
break;
case NavigationType::Escape:
onEscape();
break;
case NavigationType::TriggerControl:
doTriggerControl();
break;
case NavigationType::FirstControl:
goToFirstControl();
break;
case NavigationType::LastControl:
goToLastControl();
break;
case NavigationType::NextRowControl:
goToNextRowControl();
break;
case NavigationType::PrevRowControl:
goToPrevRowControl();
break;
}
}

void NavigationController::resetActive()
{
MYLOG() << "===";
Expand Down
24 changes: 24 additions & 0 deletions src/framework/ui/internal/navigationcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@
#include "actions/actionable.h"
#include "async/asyncable.h"
#include "global/iinteractive.h"
#include "global/iapplication.h"
#include "../imainwindow.h"

namespace mu::ui {
class NavigationController : public QObject, public INavigationController, public actions::Actionable, public async::Asyncable
{
INJECT(ui, actions::IActionsDispatcher, dispatcher)
INJECT(ui, framework::IInteractive, interactive)
INJECT(ui, framework::IApplication, application)
INJECT(ui, IMainWindow, mainWindow)

public:
NavigationController() = default;
Expand Down Expand Up @@ -69,8 +73,28 @@ class NavigationController : public QObject, public INavigationController, publi

private:

enum class NavigationType {
NextSection,
PrevSection,
PrevSectionActiveLastPanel,
NextPanel,
PrevPanel,
Left,
Right,
Up,
Down,
Escape,
TriggerControl,
FirstControl,
LastControl,
NextRowControl,
PrevRowControl
};

bool eventFilter(QObject* watched, QEvent* event) override;

void navigateTo(NavigationType type);

void goToNextSection();
void goToPrevSection(bool isActivateLastPanel = false);
void goToNextPanel();
Expand Down
1 change: 1 addition & 0 deletions src/framework/ui/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set(MODULE_TEST_SRC
${CMAKE_CURRENT_LIST_DIR}/environment.cpp
${CMAKE_CURRENT_LIST_DIR}/navigationcontroller_tests.cpp
${CMAKE_CURRENT_LIST_DIR}/mocks/navigationmocks.h
${CMAKE_CURRENT_LIST_DIR}/mocks/mainwindowprovidermock.h
)

set(MODULE_TEST_LINK
Expand Down
49 changes: 49 additions & 0 deletions src/framework/ui/tests/mocks/mainwindowprovidermock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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_UI_MAINWINDOWPROVIDERMOCK_H
#define MU_UI_MAINWINDOWPROVIDERMOCK_H

#include <gmock/gmock.h>

#include "framework/ui/imainwindow.h"

namespace mu::ui {
class MainWindowProviderMock : public IMainWindow
{
public:

MOCK_METHOD(QWindow*, qWindow, (), (const, override));

MOCK_METHOD(QWindow*, topWindow, (), (const, override));
MOCK_METHOD(void, pushWindow, (QWindow*), (override));
MOCK_METHOD(void, popWindow, (QWindow*), (override));

MOCK_METHOD(void, requestShowOnBack, (), (override));
MOCK_METHOD(void, requestShowOnFront, (), (override));

MOCK_METHOD(bool, isFullScreen, (), (const, override));
MOCK_METHOD(void, toggleFullScreen, (), (override));
MOCK_METHOD(const QScreen*, screen, (), (const, override));
};
}

#endif // MU_UI_MAINWINDOWPROVIDERMOCK_H
Loading

0 comments on commit 3599b63

Please sign in to comment.