Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaccessiblebridgeutils_p.h"
#include <QtCore/qmath.h>
#include <QtGui/qwindow.h>
QT_BEGIN_NAMESPACE
namespace QAccessibleBridgeUtils {
static bool performAction(QAccessibleInterface *iface, const QString &actionName)
{
if (QAccessibleActionInterface *actionIface = iface->actionInterface()) {
if (actionIface->actionNames().contains(actionName)) {
actionIface->doAction(actionName);
return true;
}
}
return false;
}
QStringList effectiveActionNames(QAccessibleInterface *iface)
{
QStringList actions;
if (QAccessibleActionInterface *actionIface = iface->actionInterface())
actions = actionIface->actionNames();
if (iface->valueInterface()) {
if (!actions.contains(QAccessibleActionInterface::increaseAction()))
actions << QAccessibleActionInterface::increaseAction();
if (!actions.contains(QAccessibleActionInterface::decreaseAction()))
actions << QAccessibleActionInterface::decreaseAction();
}
return actions;
}
bool performEffectiveAction(QAccessibleInterface *iface, const QString &actionName)
{
if (!iface)
return false;
if (performAction(iface, actionName))
return true;
if (actionName != QAccessibleActionInterface::increaseAction()
&& actionName != QAccessibleActionInterface::decreaseAction())
return false;
QAccessibleValueInterface *valueIface = iface->valueInterface();
if (!valueIface)
return false;
bool success;
const QVariant currentVariant = valueIface->currentValue();
double stepSize = valueIface->minimumStepSize().toDouble(&success);
if (!success || qFuzzyIsNull(stepSize)) {
const double min = valueIface->minimumValue().toDouble(&success);
if (!success)
return false;
const double max = valueIface->maximumValue().toDouble(&success);
if (!success)
return false;
stepSize = (max - min) / 10; // this is pretty arbitrary, we just need to provide something
const int typ = currentVariant.userType();
if (typ != QMetaType::Float && typ != QMetaType::Double) {
// currentValue is an integer. Round it up to ensure stepping in case it was below 1
stepSize = qCeil(stepSize);
}
}
const double current = currentVariant.toDouble(&success);
if (!success)
return false;
if (actionName == QAccessibleActionInterface::decreaseAction())
stepSize = -stepSize;
valueIface->setCurrentValue(current + stepSize);
return true;
}
QString accessibleId(QAccessibleInterface *accessible) {
QString result;
if (!accessible)
return result;
result = accessible->text(QAccessible::Identifier);
if (!result.isEmpty())
return result;
while (accessible) {
if (!result.isEmpty())
result.prepend(u'.');
if (auto obj = accessible->object()) {
const QString name = obj->objectName();
if (!name.isEmpty())
result.prepend(name);
else
result.prepend(QString::fromUtf8(obj->metaObject()->className()));
}
accessible = accessible->parent();
}
return result;
}
/*
Returns the window whose native handle is responsible for \a iface.
Not every interface implements window(), so we walk up the ancestors until
one does, as the documentation asks of the backend. The first ancestor to
answer gives the closest window, since a window's root interface sits on this
chain and reports its own window.
A caller that already knows the window of an ancestor passes the ancestor as
\a ancestorInterface and its window as \a ancestorWindow. The walk then stops
at that ancestor and answers with \a ancestorWindow. The full walk ends up at
the same window, so the two arguments only skip the part the caller has
already walked.
The two arguments also let us reject an answer that can not be true. An
interface below \a ancestorInterface can not live in a window that contains
\a ancestorWindow. A bridge that substituted the native view of such a window
would hand back a view that contains \a ancestorWindow's own view, and the
native accessibility tree would loop. An interface that answers window() with
the top level instead of the window containing it gives such an answer, so we
discard it and keep walking.
We do however accept a window that is not a descendant of \a ancestorWindow. A
QWindowContainer inside a child QWindow hands its contained window to the top
level, and the two windows are then siblings. Move the container further down
the QWindow hierarchy and \a ancestorWindow becomes a descendant of a sibling
of the window we return.
Callers that have specific demands on the accepted window hierarchy beyond
the loop detection must handle those cases at the call site.
*/
QWindow *windowFor(const QAccessibleInterface *iface,
const QAccessibleInterface *ancestorInterface,
QWindow *ancestorWindow)
{
Q_ASSERT(bool(ancestorInterface) == bool(ancestorWindow));
for (const auto *candidate = iface; candidate && candidate != ancestorInterface;
candidate = candidate->parent()) {
QWindow *window = candidate->window();
if (!window)
continue;
// We found a window before reaching ancestorInterface. It can only
// represent the interface if it does not contain ancestorWindow,
// as otherwise the native accessibility tree would loop.
if (ancestorWindow && window->isAncestorOf(ancestorWindow, QWindow::ExcludeTransients))
continue;
return window;
}
return ancestorWindow;
}
} //namespace
QT_END_NAMESPACE