]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/accessibility/kitemlistdelegateaccessible.cpp
Overhaul main view accessibility
[dolphin.git] / src / kitemviews / accessibility / kitemlistdelegateaccessible.cpp
1 /*
2 * SPDX-FileCopyrightText: 2012 Amandeep Singh <aman.dedman@gmail.com>
3 * SPDX-FileCopyrightText: 2024 Felix Ernst <felixernst@kde.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "kitemlistdelegateaccessible.h"
9 #include "kitemviews/kfileitemlistwidget.h"
10 #include "kitemviews/kfileitemmodel.h"
11 #include "kitemviews/kitemlistcontroller.h"
12 #include "kitemviews/kitemlistselectionmanager.h"
13 #include "kitemviews/kitemlistview.h"
14 #include "kitemviews/private/kitemlistviewlayouter.h"
15
16 #include <KLocalizedString>
17
18 #include <QGraphicsScene>
19 #include <QGraphicsView>
20
21 KItemListDelegateAccessible::KItemListDelegateAccessible(KItemListView *view, int index)
22 : m_view(view)
23 , m_index(index)
24 {
25 Q_ASSERT(index >= 0 && index < view->model()->count());
26 }
27
28 void *KItemListDelegateAccessible::interface_cast(QAccessible::InterfaceType type)
29 {
30 if (type == QAccessible::TableCellInterface) {
31 return static_cast<QAccessibleTableCellInterface *>(this);
32 }
33 return nullptr;
34 }
35
36 int KItemListDelegateAccessible::columnExtent() const
37 {
38 return 1;
39 }
40
41 int KItemListDelegateAccessible::rowExtent() const
42 {
43 return 1;
44 }
45
46 QList<QAccessibleInterface *> KItemListDelegateAccessible::rowHeaderCells() const
47 {
48 return QList<QAccessibleInterface *>();
49 }
50
51 QList<QAccessibleInterface *> KItemListDelegateAccessible::columnHeaderCells() const
52 {
53 return QList<QAccessibleInterface *>();
54 }
55
56 int KItemListDelegateAccessible::columnIndex() const
57 {
58 return m_view->m_layouter->itemColumn(m_index);
59 }
60
61 int KItemListDelegateAccessible::rowIndex() const
62 {
63 return m_view->m_layouter->itemRow(m_index);
64 }
65
66 bool KItemListDelegateAccessible::isSelected() const
67 {
68 return m_view->controller()->selectionManager()->isSelected(m_index);
69 }
70
71 QAccessibleInterface *KItemListDelegateAccessible::table() const
72 {
73 return QAccessible::queryAccessibleInterface(m_view);
74 }
75
76 QAccessible::Role KItemListDelegateAccessible::role() const
77 {
78 return QAccessible::ListItem; // We could also return "Cell" here which would then announce the exact row and column of the item. However, different from
79 // applications that actually have a strong cell workflow -- like LibreOfficeCalc -- we have no advantage of announcing the row or column aside from us
80 // generally being interested in announcing that users in Icon View mode need to use the Left and Right arrow keys to arrive at every item. There are ways
81 // for users to figure this out regardless by paying attention to the index that is being announced for each list item. In KitemListViewAccessible in icon
82 // view mode it is also mentioned that the items are positioned in a grid, so the two-dimensionality should be clear enough.
83 }
84
85 QAccessible::State KItemListDelegateAccessible::state() const
86 {
87 QAccessible::State state;
88
89 state.selectable = true;
90 if (isSelected()) {
91 state.selected = true;
92 }
93
94 state.focusable = true;
95 if (m_view->controller()->selectionManager()->currentItem() == m_index) {
96 state.focused = true;
97 state.active = true;
98 }
99
100 if (m_view->controller()->selectionBehavior() == KItemListController::MultiSelection) {
101 state.multiSelectable = true;
102 }
103
104 if (m_view->supportsItemExpanding() && m_view->model()->isExpandable(m_index)) {
105 state.expandable = true;
106 state.expanded = m_view->model()->isExpanded(m_index);
107 state.collapsed = !state.expanded;
108 }
109
110 return state;
111 }
112
113 bool KItemListDelegateAccessible::isExpandable() const
114 {
115 return m_view->model()->isExpandable(m_index);
116 }
117
118 QRect KItemListDelegateAccessible::rect() const
119 {
120 QRect rect = m_view->itemRect(m_index).toRect();
121
122 if (rect.isNull()) {
123 return QRect();
124 }
125
126 rect.translate(m_view->mapToScene(QPointF(0.0, 0.0)).toPoint());
127 rect.translate(m_view->scene()->views()[0]->mapToGlobal(QPoint(0, 0)));
128 return rect;
129 }
130
131 QString KItemListDelegateAccessible::text(QAccessible::Text t) const
132 {
133 const QHash<QByteArray, QVariant> data = m_view->model()->data(m_index);
134 switch (t) {
135 case QAccessible::Name: {
136 return data["text"].toString();
137 }
138 case QAccessible::Description: {
139 QString description;
140
141 if (data["isHidden"].toBool()) {
142 description += i18nc("@info", "hidden");
143 }
144
145 QString mimeType{data["type"].toString()};
146 if (mimeType.isEmpty()) {
147 const KFileItemModel *model = qobject_cast<KFileItemModel *>(m_view->model());
148 if (model) {
149 mimeType = model->fileItem(m_index).mimeComment();
150 }
151 Q_ASSERT_X(!mimeType.isEmpty(), "KItemListDelegateAccessible::text", "Unable to retrieve mime type.");
152 }
153
154 if (data["isLink"].toBool()) {
155 QString linkDestination{data["destination"].toString()};
156 if (linkDestination.isEmpty()) {
157 const KFileItemModel *model = qobject_cast<KFileItemModel *>(m_view->model());
158 if (model) {
159 linkDestination = model->fileItem(m_index).linkDest();
160 }
161 Q_ASSERT_X(!linkDestination.isEmpty(), "KItemListDelegateAccessible::text", "Unable to retrieve link destination.");
162 }
163
164 description += i18nc("@info enumeration saying this is a link to $1, %1 is mimeType", ", link to %1 at %2", mimeType, linkDestination);
165 } else {
166 description += i18nc("@info enumeration, %1 is mimeType", ", %1", mimeType);
167 }
168 const QList<QByteArray> additionallyShownInformation{m_view->visibleRoles()};
169 const KItemModelBase *model = m_view->model();
170 for (const auto &roleInformation : additionallyShownInformation) {
171 if (roleInformation == "text") {
172 continue;
173 }
174 KFileItemListWidgetInformant informant;
175 const auto roleText{informant.roleText(roleInformation, data, KFileItemListWidgetInformant::ForUsageAs::SpokenText)};
176 if (roleText.isEmpty()) {
177 continue; // No need to announce roles which are empty for this item.
178 }
179 description +=
180 // i18n: The text starts with a comma because multiple occurences of this text can follow after each others as an enumeration.
181 // Normally it would make sense to have a colon between property and value to make the relation between the property and its property value
182 // clear, however this is accessible text that will be read out by screen readers. That's why there is only a space between the two here,
183 // because screen readers would read the colon literally as "colon", which is just a waste of time for users who might go through a list of
184 // hundreds of items. So, if you want to add any more punctation there to improve structure, try to make sure that it will not lead to annoying
185 // announcements when read out by a screen reader.
186 i18nc("@info accessibility enumeration, %1 is property, %2 is value", ", %1 %2", model->roleDescription(roleInformation), roleText);
187 }
188 return description;
189 }
190 default:
191 break;
192 }
193
194 return QString();
195 }
196
197 void KItemListDelegateAccessible::setText(QAccessible::Text, const QString &)
198 {
199 }
200
201 QAccessibleInterface *KItemListDelegateAccessible::child(int) const
202 {
203 return nullptr;
204 }
205
206 bool KItemListDelegateAccessible::isValid() const
207 {
208 return m_view && (m_index >= 0) && (m_index < m_view->model()->count());
209 }
210
211 QAccessibleInterface *KItemListDelegateAccessible::childAt(int, int) const
212 {
213 return nullptr;
214 }
215
216 int KItemListDelegateAccessible::childCount() const
217 {
218 return 0;
219 }
220
221 int KItemListDelegateAccessible::indexOfChild(const QAccessibleInterface *child) const
222 {
223 Q_UNUSED(child)
224 return -1;
225 }
226
227 QAccessibleInterface *KItemListDelegateAccessible::parent() const
228 {
229 return QAccessible::queryAccessibleInterface(m_view);
230 }
231
232 int KItemListDelegateAccessible::index() const
233 {
234 return m_index;
235 }
236
237 QObject *KItemListDelegateAccessible::object() const
238 {
239 return nullptr;
240 }