Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2026 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 "accessiblewindowinterface.h"
24 :
25 : #include <QWindow>
26 :
27 : #include "accessibilitycontroller.h"
28 :
29 : #include "modularity/ioc.h"
30 : #include "../iaccessibleapprootobject.h"
31 :
32 : #include "translation.h"
33 : #include "log.h"
34 :
35 : //#define MUSE_ACCESSIBILITY_TRACE
36 :
37 : #undef MYLOG
38 : #ifdef MUSE_ACCESSIBILITY_TRACE
39 : #define MYLOG() LOGI()
40 : #else
41 : #define MYLOG() LOGN()
42 : #endif
43 :
44 : using namespace muse::accessibility;
45 :
46 0 : AccessibleWindowInterface::AccessibleWindowInterface(QObject* window)
47 : {
48 0 : m_window = qobject_cast<QWindow*>(window);
49 0 : }
50 :
51 0 : AccessibleObject* AccessibleWindowInterface::resolveWindowRoot() const
52 : {
53 0 : if (!m_window) {
54 0 : return nullptr;
55 : }
56 :
57 0 : auto appRoot = muse::modularity::globalIoc()->resolve<IAccessibleAppRootObject>("accessibility");
58 0 : if (!appRoot) {
59 0 : return nullptr;
60 : }
61 :
62 : #ifdef Q_OS_LINUX
63 : // On Linux do not walk transientParent to prevent Orca frow re-interperting
64 : // the whole window tree, that results in VO delay.
65 0 : return appRoot->windowRoot(m_window);
66 : #else
67 : QWindow* w = m_window;
68 : while (w) {
69 : if (AccessibleObject* root = appRoot->windowRoot(w)) {
70 : return root;
71 : }
72 : w = w->transientParent();
73 : }
74 : return nullptr;
75 : #endif
76 0 : }
77 :
78 0 : bool AccessibleWindowInterface::isValid() const
79 : {
80 0 : return m_window != nullptr;
81 : }
82 :
83 0 : QObject* AccessibleWindowInterface::object() const
84 : {
85 0 : return m_window;
86 : }
87 :
88 0 : QWindow* AccessibleWindowInterface::window() const
89 : {
90 0 : return m_window;
91 : }
92 :
93 0 : QRect AccessibleWindowInterface::rect() const
94 : {
95 0 : if (!m_window) {
96 0 : return {};
97 : }
98 0 : return QRect(m_window->x(), m_window->y(), m_window->width(), m_window->height());
99 : }
100 :
101 0 : QAccessibleInterface* AccessibleWindowInterface::parent() const
102 : {
103 0 : auto appRoot = muse::modularity::globalIoc()->resolve<IAccessibleAppRootObject>("accessibility");
104 0 : if (!appRoot) {
105 0 : return nullptr;
106 : }
107 0 : return QAccessible::queryAccessibleInterface(appRoot->asQObject());
108 0 : }
109 :
110 0 : int AccessibleWindowInterface::childCount() const
111 : {
112 0 : AccessibleObject* root = resolveWindowRoot();
113 0 : if (!root) {
114 0 : return 0;
115 : }
116 :
117 0 : auto controller = root->controller().lock();
118 0 : if (!controller) {
119 0 : return 0;
120 : }
121 :
122 0 : int count = controller->childCount(root->item());
123 : MYLOG() << "item: " << root->item()->accessibleName() << ", childCount: " << count;
124 0 : return count;
125 0 : }
126 :
127 0 : QAccessibleInterface* AccessibleWindowInterface::child(int index) const
128 : {
129 0 : AccessibleObject* root = resolveWindowRoot();
130 0 : if (!root) {
131 0 : return nullptr;
132 : }
133 :
134 0 : auto controller = root->controller().lock();
135 0 : if (!controller) {
136 0 : return nullptr;
137 : }
138 :
139 0 : QAccessibleInterface* iface = controller->child(root->item(), index);
140 : MYLOG() << "item: " << root->item()->accessibleName() << ", child: " << index << " " <<
141 : (iface ? iface->text(QAccessible::Name) : "null");
142 0 : return iface;
143 0 : }
144 :
145 0 : int AccessibleWindowInterface::indexOfChild(const QAccessibleInterface* iface) const
146 : {
147 0 : AccessibleObject* root = resolveWindowRoot();
148 0 : if (!root) {
149 0 : return -1;
150 : }
151 :
152 0 : auto controller = root->controller().lock();
153 0 : if (!controller) {
154 0 : return -1;
155 : }
156 :
157 0 : int idx = controller->indexOfChild(root->item(), iface);
158 : MYLOG() << "item: " << root->item()->accessibleName() << ", indexOfChild: " <<
159 : (iface ? iface->text(QAccessible::Name) : "null") << " = " << idx;
160 0 : return idx;
161 0 : }
162 :
163 0 : QAccessibleInterface* AccessibleWindowInterface::childAt(int, int) const
164 : {
165 0 : NOT_IMPLEMENTED;
166 0 : return nullptr;
167 : }
168 :
169 0 : QAccessibleInterface* AccessibleWindowInterface::focusChild() const
170 : {
171 0 : AccessibleObject* root = resolveWindowRoot();
172 0 : if (!root) {
173 0 : return nullptr;
174 : }
175 :
176 0 : auto controller = root->controller().lock();
177 0 : if (!controller) {
178 0 : return nullptr;
179 : }
180 :
181 0 : QAccessibleInterface* child = controller->focusedChild(root->item());
182 : MYLOG() << "item: " << root->item()->accessibleName() << ", focused child: " << (child ? child->text(QAccessible::Name) : "null");
183 0 : return child;
184 0 : }
185 :
186 0 : QAccessible::State AccessibleWindowInterface::state() const
187 : {
188 0 : QAccessible::State st;
189 0 : st.active = true;
190 0 : return st;
191 : }
192 :
193 0 : QAccessible::Role AccessibleWindowInterface::role() const
194 : {
195 0 : return QAccessible::Window;
196 : }
197 :
198 0 : QString AccessibleWindowInterface::text(QAccessible::Text) const
199 : {
200 0 : if (!m_window) {
201 0 : return {};
202 : }
203 0 : return m_window->title();
204 : }
205 :
206 0 : void AccessibleWindowInterface::setText(QAccessible::Text, const QString&)
207 : {
208 0 : NOT_IMPLEMENTED;
209 0 : }
210 :
211 0 : void* AccessibleWindowInterface::interface_cast(QAccessible::InterfaceType)
212 : {
213 : //! NOTE Not implemented
214 0 : return nullptr;
215 : }
|