Warning, file /pim/kleopatra/src/utils/accessibility.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* utils/accessibility.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2022 g10 Code GmbH 0005 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <config-kleopatra.h> 0011 0012 #include "accessibility.h" 0013 0014 #include <KLocalizedString> 0015 0016 #include <QAction> 0017 #ifdef Q_OS_WIN 0018 #include <QApplication> 0019 #include <QDesktopWidget> 0020 #endif 0021 #include <QLabel> 0022 #include <QTextDocument> 0023 #include <QToolTip> 0024 0025 #include <algorithm> 0026 #include <chrono> 0027 0028 #include "kleopatra_debug.h" 0029 0030 using namespace Kleo; 0031 0032 static const char *accessibleNameProperty = "_kleo_accessibleName"; 0033 static const char *accessibleValueProperty = "_kleo_accessibleValue"; 0034 0035 static const char *useAccessibleValueLabelProperty = "_kleo_useAccessibleValueLabel"; 0036 0037 namespace 0038 { 0039 QString getAccessibleText(QWidget *widget, QAccessible::Text t) 0040 { 0041 QString name; 0042 if (const auto *const iface = QAccessible::queryAccessibleInterface(widget)) { 0043 name = iface->text(t); 0044 } 0045 return name; 0046 } 0047 } 0048 0049 QString Kleo::getAccessibleName(QWidget *widget) 0050 { 0051 return getAccessibleText(widget, QAccessible::Name); 0052 } 0053 0054 QString Kleo::getAccessibleDescription(QWidget *widget) 0055 { 0056 return getAccessibleText(widget, QAccessible::Description); 0057 } 0058 0059 void Kleo::setAccessibleName(QAction *action, const QString &name) 0060 { 0061 action->setProperty(accessibleNameProperty, name); 0062 } 0063 0064 QString Kleo::getAccessibleName(const QAction *action) 0065 { 0066 return action->property(accessibleNameProperty).toString(); 0067 } 0068 0069 void Kleo::setAccessibleValue(QWidget *widget, const QString &value) 0070 { 0071 widget->setProperty(accessibleValueProperty, value); 0072 } 0073 0074 QString Kleo::getAccessibleValue(const QWidget *widget) 0075 { 0076 return widget->property(accessibleValueProperty).toString(); 0077 } 0078 0079 void Kleo::setRepresentAsAccessibleValueWidget(QWidget *widget, bool flag) 0080 { 0081 widget->setProperty(useAccessibleValueLabelProperty, flag ? flag : QVariant{}); 0082 } 0083 0084 bool Kleo::representAsAccessibleValueWidget(const QWidget *widget) 0085 { 0086 return widget->property(useAccessibleValueLabelProperty).toBool(); 0087 } 0088 0089 QString Kleo::invalidEntryText() 0090 { 0091 return i18nc( 0092 "text for screen readers to indicate that the associated object, " 0093 "such as a form field, has an error", 0094 "invalid entry"); 0095 } 0096 0097 QString Kleo::requiredText() 0098 { 0099 return i18nc( 0100 "text for screen readers to indicate that the associated object, " 0101 "such as a form field must be filled out", 0102 "required"); 0103 } 0104 0105 void Kleo::selectLabelText(QLabel *label) 0106 { 0107 if (!label || label->text().isEmpty()) { 0108 return; 0109 } 0110 if (label->textFormat() == Qt::PlainText) { 0111 label->setSelection(0, label->text().size()); 0112 } else if (label->textFormat() == Qt::RichText) { 0113 // unfortunately, there is no selectAll(); therefore, we need 0114 // to determine the "visual" length of the text by stripping 0115 // the label's text of all formatting information 0116 QTextDocument temp; 0117 temp.setHtml(label->text()); 0118 label->setSelection(0, temp.toRawText().size()); 0119 } else { 0120 qCDebug(KLEOPATRA_LOG) << "Label with unsupported text format" << label->textFormat() << "got focus"; 0121 } 0122 } 0123 0124 namespace 0125 { 0126 static void notifyAccessibilityClientsAboutToolTip(const QPoint &pos, QWidget *parent) 0127 { 0128 #ifdef Q_OS_WIN 0129 // On Windows, the tool tip's parent widget is a desktop screen widget (see implementation of QToolTip::showText) 0130 QT_WARNING_PUSH 0131 QT_WARNING_DISABLE_DEPRECATED 0132 const auto desktop = QApplication::desktop(); 0133 const int screenNumber = desktop->isVirtualDesktop() ? desktop->screenNumber(pos) : desktop->screenNumber(parent); 0134 parent = desktop->screen(screenNumber); 0135 QT_WARNING_POP 0136 #else 0137 Q_UNUSED(pos); 0138 #endif 0139 if (!parent) { 0140 return; 0141 } 0142 if (auto toolTipLabel = parent->findChild<QLabel *>(QStringLiteral("qtooltip_label"))) { 0143 // Qt explicitly does not notify accessibility clients about the tool tip being shown because 0144 // "Tooltips are read aloud twice in MS narrator." 0145 // The problem is that they are not read out by orca (on Linux) if the notification is omitted. 0146 // Therefore, we take care of notifying the accessibility clients. 0147 #ifndef QT_NO_ACCESSIBILITY 0148 QAccessibleEvent event(toolTipLabel, QAccessible::ObjectShow); 0149 QAccessible::updateAccessibility(&event); 0150 #endif 0151 } 0152 } 0153 } 0154 0155 void Kleo::showToolTip(const QPoint &pos, const QString &text, QWidget *w) 0156 { 0157 using namespace std::chrono_literals; 0158 static const std::chrono::milliseconds timeout = 24h; 0159 QToolTip::showText(pos, text, w, {}, timeout.count()); 0160 notifyAccessibilityClientsAboutToolTip(pos, w); 0161 } 0162 0163 LabelHelper::LabelHelper() 0164 { 0165 QAccessible::installActivationObserver(this); 0166 } 0167 0168 LabelHelper::~LabelHelper() 0169 { 0170 QAccessible::removeActivationObserver(this); 0171 } 0172 0173 void LabelHelper::addLabel(QLabel *label) 0174 { 0175 mLabels.push_back(label); 0176 accessibilityActiveChanged(QAccessible::isActive()); 0177 } 0178 0179 void LabelHelper::accessibilityActiveChanged(bool active) 0180 { 0181 // Allow text labels to get focus if accessibility is active 0182 const auto focusPolicy = active ? Qt::StrongFocus : Qt::ClickFocus; 0183 std::for_each(std::cbegin(mLabels), std::cend(mLabels), [focusPolicy](const auto &label) { 0184 if (label) { 0185 label->setFocusPolicy(focusPolicy); 0186 } 0187 }); 0188 }