Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
132 lines
3.0 KiB
C++
132 lines
3.0 KiB
C++
// This file is part of Desktop App Toolkit,
|
|
// a set of libraries for developing nice desktop applications.
|
|
//
|
|
// For license and copyright information please follow this link:
|
|
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
|
//
|
|
#include "ui/accessible/ui_accessible_widget.h"
|
|
|
|
#include "base/debug_log.h"
|
|
#include "base/integration.h"
|
|
#include "base/screen_reader_state.h"
|
|
#include "base/timer.h"
|
|
#include "ui/rp_widget.h"
|
|
|
|
namespace Ui::Accessible {
|
|
namespace {
|
|
|
|
constexpr auto kCleanupDelay = 5 * crl::time(1000);
|
|
|
|
class FocusManager final {
|
|
public:
|
|
FocusManager();
|
|
|
|
void registerWidget(not_null<RpWidget*> widget);
|
|
|
|
private:
|
|
void cleanup();
|
|
|
|
std::vector<QPointer<RpWidget>> _widgets;
|
|
base::Timer _cleanupTimer;
|
|
bool _active = false;
|
|
|
|
rpl::lifetime _lifetime;
|
|
|
|
};
|
|
|
|
FocusManager::FocusManager() : _cleanupTimer([=] { cleanup(); }) {
|
|
base::ScreenReaderState::Instance()->activeValue(
|
|
) | rpl::on_next([=](bool active) {
|
|
_active = active;
|
|
LOG(("Screen Reader: %1").arg(active ? "active" : "inactive"));
|
|
|
|
cleanup();
|
|
for (const auto &widget : _widgets) {
|
|
widget->setFocusPolicy(active ? Qt::TabFocus : Qt::NoFocus);
|
|
}
|
|
}, _lifetime);
|
|
}
|
|
|
|
void FocusManager::registerWidget(not_null<RpWidget*> widget) {
|
|
const auto role = widget->accessibilityRole();
|
|
if (role != QAccessible::Role::Button
|
|
&& role != QAccessible::Role::Link
|
|
&& role != QAccessible::Role::CheckBox
|
|
&& role != QAccessible::Role::Slider) {
|
|
return;
|
|
}
|
|
if (_active) {
|
|
widget->setFocusPolicy(Qt::TabFocus);
|
|
}
|
|
_widgets.push_back(widget.get());
|
|
if (!_cleanupTimer.isActive()) {
|
|
_cleanupTimer.callOnce(kCleanupDelay);
|
|
}
|
|
}
|
|
|
|
void FocusManager::cleanup() {
|
|
_widgets.erase(
|
|
ranges::remove_if(
|
|
_widgets,
|
|
[](const QPointer<RpWidget> &widget) { return !widget; }),
|
|
end(_widgets));
|
|
}
|
|
|
|
[[nodiscard]] FocusManager &Manager() {
|
|
static FocusManager Instance;
|
|
return Instance;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Widget::Widget(not_null<RpWidget*> widget) : QAccessibleWidget(widget) {
|
|
Manager().registerWidget(widget);
|
|
}
|
|
|
|
[[nodiscard]] not_null<RpWidget*> Widget::rp() const {
|
|
return static_cast<RpWidget*>(widget());
|
|
}
|
|
|
|
QAccessible::Role Widget::role() const {
|
|
return rp()->accessibilityRole();
|
|
}
|
|
|
|
QAccessible::State Widget::state() const {
|
|
auto result = QAccessibleWidget::state();
|
|
rp()->accessibilityState().writeTo(result);
|
|
return result;
|
|
}
|
|
|
|
QStringList Widget::actionNames() const {
|
|
return QAccessibleWidget::actionNames()
|
|
+ rp()->accessibilityActionNames();
|
|
}
|
|
|
|
void Widget::doAction(const QString &actionName) {
|
|
QAccessibleWidget::doAction(actionName);
|
|
base::Integration::Instance().enterFromEventLoop([&] {
|
|
rp()->accessibilityDoAction(actionName);
|
|
});
|
|
}
|
|
|
|
QString Widget::text(QAccessible::Text t) const {
|
|
const auto result = QAccessibleWidget::text(t);
|
|
if (!result.isEmpty()) {
|
|
return result;
|
|
}
|
|
switch (t) {
|
|
case QAccessible::Name: {
|
|
return rp()->accessibilityName();
|
|
}
|
|
case QAccessible::Description: {
|
|
return rp()->accessibilityDescription();
|
|
}
|
|
case QAccessible::Value: {
|
|
return rp()->accessibilityValue();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace Ui::Accessible
|