05fc3aef53
Replace the current license disclaimer in files by a SPDX-License-Identifier. Files that have to be modified by hand are modified. License files are organized under LICENSES directory. Task-number: QTBUG-67283 Change-Id: Id880c92784c40f3bbde861c0d93f58151c18b9f1 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Jörg Bornemann <joerg.bornemann@qt.io>
445 lines
14 KiB
C++
445 lines
14 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "accessibilityscenemanager.h"
|
|
|
|
AccessibilitySceneManager::AccessibilitySceneManager()
|
|
{
|
|
m_window = 0;
|
|
m_view = 0;
|
|
m_scene = 0;
|
|
m_rootItem = 0;
|
|
m_optionsWidget = 0;
|
|
m_selectedObject = 0;
|
|
}
|
|
|
|
void AccessibilitySceneManager::populateAccessibilityScene()
|
|
{
|
|
m_scene->clear();
|
|
m_graphicsItems.clear();
|
|
|
|
QAccessibleInterface * rootInterface = m_window->accessibleRoot();
|
|
if (!rootInterface)
|
|
return;
|
|
|
|
populateAccessibilityScene(rootInterface, m_scene);
|
|
}
|
|
|
|
void AccessibilitySceneManager::updateAccessibilitySceneItemFlags()
|
|
{
|
|
qDebug() << "update";
|
|
foreach (QObject *object, m_graphicsItems.keys()) {
|
|
if (!object)
|
|
continue;
|
|
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
|
|
if (!interface)
|
|
continue;
|
|
updateItemFlags(m_graphicsItems.value(object), interface);
|
|
}
|
|
}
|
|
|
|
void AccessibilitySceneManager::populateAccessibilityTreeScene()
|
|
{
|
|
m_treeScene->clear();
|
|
QAccessibleInterface * rootInterface = m_window->accessibleRoot();
|
|
if (!rootInterface) {
|
|
qWarning("QWindow::accessibleRoot returned 0");
|
|
return;
|
|
}
|
|
|
|
populateAccessibilityTreeScene(rootInterface);
|
|
}
|
|
|
|
void AccessibilitySceneManager::handleUpdate(QAccessibleEvent *event)
|
|
{
|
|
QObject *object = event->object();
|
|
QAccessible::Event type = event->type();
|
|
|
|
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
|
|
if (!interface)
|
|
return;
|
|
|
|
QString name = interface->text(QAccessible::Name);
|
|
|
|
if (type == QAccessible::ObjectCreated) {
|
|
// qDebug() << "ObjectCreated" << object << name;
|
|
populateAccessibilityScene(interface, m_scene);
|
|
}
|
|
|
|
QGraphicsRectItem *item = m_graphicsItems.value(object);
|
|
|
|
if (!item) {
|
|
// qDebug() << "populateAccessibilityScene failed for" << object;
|
|
return;
|
|
}
|
|
|
|
if (type == QAccessible::LocationChanged) {
|
|
|
|
//if (name.startsWith("List"))
|
|
qDebug() << "locationChange" << object << name << interface->rect();
|
|
|
|
updateItem(item, interface);
|
|
for (int i = 0; i < interface->childCount(); ++i) {
|
|
QAccessibleInterface *child = interface->child(i);
|
|
if (child) {
|
|
updateItem(m_graphicsItems.value(child->object()), child);
|
|
}
|
|
}
|
|
|
|
} else if (type == QAccessible::ObjectDestroyed) {
|
|
// qDebug() << "ObjectDestroyed" << object << name;
|
|
delete m_graphicsItems.value(object);
|
|
m_graphicsItems.remove(object);
|
|
m_animatedObjects.remove(object);
|
|
if (object == m_selectedObject) {
|
|
m_selectedObject = 0;
|
|
}
|
|
} else if (type == QAccessible::ObjectHide) {
|
|
// qDebug() << "ObjectCreated Hide" << object;
|
|
updateItemFlags(item, interface);
|
|
} else if (type == QAccessible::ObjectShow) {
|
|
// qDebug() << "ObjectCreated Show" << object;
|
|
updateItemFlags(item, interface);
|
|
} else if (type == QAccessible::ScrollingStart) {
|
|
qDebug() << "ObjectCreated ScrollingStart" << object;
|
|
for (int i = 0; i < interface->childCount(); ++i) {
|
|
QAccessibleInterface *child = interface->child(i);
|
|
if (child) {
|
|
m_animatedObjects.insert(child->object());
|
|
}
|
|
}
|
|
} else if (type == QAccessible::ScrollingEnd) {
|
|
// qDebug() << "ObjectCreated ScrollingEnd" << object;
|
|
foreach (QObject *object, m_animatedObjects) {
|
|
updateItem(m_graphicsItems.value(object), interface);
|
|
}
|
|
m_animatedObjects.clear();
|
|
|
|
} else {
|
|
// qDebug() << "other update" << object;
|
|
}
|
|
}
|
|
|
|
void AccessibilitySceneManager::setSelected(QObject *object)
|
|
{
|
|
m_scene->update(); // scedule update
|
|
|
|
// clear existing selection
|
|
if (m_selectedObject) {
|
|
QObject *previousSelectedObject = m_selectedObject;
|
|
m_selectedObject = 0;
|
|
updateItem(previousSelectedObject);
|
|
}
|
|
|
|
m_selectedObject = object;
|
|
updateItem(object);
|
|
|
|
populateAccessibilityTreeScene();
|
|
}
|
|
|
|
void AccessibilitySceneManager::changeScale(int)
|
|
{
|
|
// No QGraphicsView::setScale :(
|
|
|
|
//m_view->scale(scale / 10.0, scale / 10.0);
|
|
//if (m_rootItem)
|
|
// m_view->ensureVisible(m_rootItem);
|
|
}
|
|
|
|
void AccessibilitySceneManager::updateItems(QObject *root)
|
|
{
|
|
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(root);
|
|
if (!interface)
|
|
return;
|
|
updateItem(m_graphicsItems.value(root), interface);
|
|
|
|
for (int i = 0; i < interface->childCount(); ++i) {
|
|
QAccessibleInterface *child = interface->child(i);
|
|
updateItems(child->object());
|
|
}
|
|
}
|
|
|
|
void AccessibilitySceneManager::updateItem(QObject *object)
|
|
{
|
|
if (!object)
|
|
return;
|
|
|
|
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
|
|
if (!interface)
|
|
return;
|
|
|
|
updateItem(m_graphicsItems.value(object), interface);
|
|
}
|
|
|
|
void AccessibilitySceneManager::updateItem(QGraphicsRectItem *item, QAccessibleInterface *interface)
|
|
{
|
|
if (!item)
|
|
return;
|
|
|
|
QRect rect = interface->rect();
|
|
item->setPos(rect.topLeft());
|
|
item->setRect(QRect(QPoint(0,0), rect.size()));
|
|
|
|
updateItemFlags(item, interface);
|
|
}
|
|
|
|
void AccessibilitySceneManager::updateItemFlags(QGraphicsRectItem *item, QAccessibleInterface *interface)
|
|
{
|
|
// qDebug() << "udpateItemFlags" << interface << interface->object();
|
|
|
|
bool shouldShow = true;
|
|
|
|
if (m_optionsWidget->hideInvisibleItems()) {
|
|
if (isHidden(interface)) {
|
|
shouldShow = false;
|
|
}
|
|
}
|
|
|
|
if (m_optionsWidget->hideOffscreenItems()) {
|
|
if (interface->state().offscreen) {
|
|
shouldShow = false;
|
|
}
|
|
}
|
|
|
|
if (m_optionsWidget->hidePaneItems()) {
|
|
if (interface->role() & QAccessible::Pane) {
|
|
shouldShow = false;
|
|
}
|
|
}
|
|
|
|
if (m_optionsWidget->hideNullObjectItems()) {
|
|
if (interface->object() == 0) {
|
|
shouldShow = false;
|
|
}
|
|
}
|
|
|
|
if (m_optionsWidget->hideNullRectItems()) {
|
|
if (interface->rect().isNull()) {
|
|
shouldShow = false;
|
|
}
|
|
}
|
|
|
|
item->setVisible(shouldShow);
|
|
|
|
if (interface->object() && interface->object() == m_selectedObject)
|
|
item->setBrush(QColor(Qt::yellow));
|
|
else
|
|
item->setBrush(QColor(Qt::white));
|
|
|
|
m_view->update();
|
|
}
|
|
|
|
QGraphicsRectItem * AccessibilitySceneManager::processInterface(QAccessibleInterface * interface, QGraphicsScene *scene)
|
|
{
|
|
// Process this interface
|
|
|
|
QGraphicsRectItem * item = new QGraphicsRectItem();
|
|
scene->addItem(item);
|
|
if (!m_rootItem)
|
|
m_rootItem = item;
|
|
|
|
QString name = interface->text(QAccessible::Name);
|
|
QString description; // = interface->text(QAccessibleInterface::Description, child);
|
|
QString role = translateRole(interface->role());
|
|
int childCount = interface->childCount();
|
|
|
|
/* qDebug() << "name:" << name << "local pos" <<
|
|
interface->rect(0) << "description" << description << "childCount" << childCount;
|
|
*/
|
|
|
|
updateItem(item, interface);
|
|
|
|
QGraphicsSimpleTextItem * textItem = new QGraphicsSimpleTextItem();
|
|
textItem->setParentItem(item);
|
|
textItem->setPos(QPoint(5, 5));
|
|
|
|
QString text;
|
|
text.append("Name: " + name + " ");
|
|
if (!description.isEmpty())
|
|
text.append("Description: " + description + " ");
|
|
text.append("Role: " + role + " ");
|
|
if (childCount > 0)
|
|
text.append("ChildCount: " + QString::number(childCount) + " ");
|
|
textItem->setText(text);
|
|
|
|
QFont font;
|
|
font.setPointSize(10);
|
|
// font.setPointSize(14);
|
|
textItem->setFont(font);
|
|
|
|
return item;
|
|
}
|
|
|
|
void AccessibilitySceneManager::populateAccessibilityScene(QAccessibleInterface * interface, QGraphicsScene *scene)
|
|
{
|
|
if (!interface)
|
|
return;
|
|
|
|
QGraphicsRectItem *item = processInterface(interface, scene);
|
|
|
|
QObject *object = interface->object();
|
|
if (object) {
|
|
m_graphicsItems.insert(object, item);
|
|
}
|
|
|
|
for (int i = 0; i < interface->childCount(); ++i) {
|
|
QAccessibleInterface *child = interface->child(i);
|
|
updateItems(child->object());
|
|
populateAccessibilityScene(child, scene);
|
|
}
|
|
}
|
|
|
|
AccessibilitySceneManager::TreeItem AccessibilitySceneManager::computeLevels(QAccessibleInterface * interface, int level)
|
|
{
|
|
if (interface == 0)
|
|
return TreeItem();
|
|
|
|
TreeItem currentLevel;
|
|
|
|
int usedChildren = 0;
|
|
for (int i = 0; i < interface->childCount(); ++i) {
|
|
QAccessibleInterface *child = interface->child(i);
|
|
if (child != 0) {
|
|
++usedChildren;
|
|
TreeItem childLevel = computeLevels(child, level + 1);
|
|
currentLevel.children.append(childLevel);
|
|
currentLevel.width += childLevel.width + m_treeItemHorizontalPadding;
|
|
}
|
|
}
|
|
|
|
// leaf node case
|
|
if (usedChildren == 0) {
|
|
currentLevel.width = m_treeItemWidth + m_treeItemHorizontalPadding;
|
|
}
|
|
|
|
// capture information:
|
|
currentLevel.name = interface->text(QAccessible::Name);
|
|
currentLevel.description += interface->text(QAccessible::DebugDescription);
|
|
currentLevel.role = translateRole(interface->role());
|
|
currentLevel.rect = interface->rect();
|
|
currentLevel.state = interface->state();
|
|
currentLevel.object = interface->object();
|
|
|
|
return currentLevel;
|
|
}
|
|
|
|
void AccessibilitySceneManager::populateAccessibilityTreeScene(QAccessibleInterface * interface)
|
|
{
|
|
if (!interface)
|
|
return;
|
|
|
|
// set some layout metrics:
|
|
m_treeItemWidth = 90;
|
|
m_treeItemHorizontalPadding = 10;
|
|
m_treeItemHeight = 60;
|
|
m_treeItemVerticalPadding = 30;
|
|
|
|
// We want to draw the accessibility hiearchy as a vertical
|
|
// tree, growing from the root node at the top.
|
|
|
|
// First, figure out the number of levels and the width of each level:
|
|
m_rootTreeItem = computeLevels(interface, 0);
|
|
|
|
// create graphics items for each tree item
|
|
addGraphicsItems(m_rootTreeItem, 0, 0);
|
|
}
|
|
|
|
void AccessibilitySceneManager::addGraphicsItems(AccessibilitySceneManager::TreeItem item, int row, int xPos)
|
|
{
|
|
//qDebug() << "add graphics item" << row << item.name << item.role << xPos << item.width << item.children.count();
|
|
|
|
int yPos = row * (m_treeItemHeight + m_treeItemVerticalPadding);
|
|
|
|
// Process this interface
|
|
QGraphicsRectItem * graphicsItem = new QGraphicsRectItem();
|
|
graphicsItem->setPos(xPos, yPos);
|
|
graphicsItem->setRect(0, 0, m_treeItemWidth, m_treeItemHeight);
|
|
graphicsItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
|
|
|
|
if (item.object && item.object == m_selectedObject)
|
|
graphicsItem->setBrush(QColor(Qt::yellow));
|
|
else
|
|
graphicsItem->setBrush(QColor(Qt::white));
|
|
|
|
if (item.state.offscreen) {
|
|
QPen linePen;
|
|
linePen.setStyle(Qt::DashLine);
|
|
graphicsItem->setPen(linePen);
|
|
}
|
|
|
|
m_treeScene->addItem(graphicsItem);
|
|
|
|
QGraphicsTextItem * textItem = new QGraphicsTextItem();
|
|
textItem->setParentItem(graphicsItem);
|
|
textItem->setPos(QPoint(0, 0));
|
|
|
|
QFont font;
|
|
font.setPointSize(8);
|
|
textItem->setFont(font);
|
|
|
|
QString text;
|
|
text += item.name + "\n";
|
|
text += item.role + "\n";
|
|
text += item.description.split(QLatin1Char(' '), Qt::SkipEmptyParts).join("\n") + "\n";
|
|
text += "P:" + QString::number(item.rect.x()) + " " + QString::number(item.rect.y()) + " ";
|
|
text += "S:" + QString::number(item.rect.width()) + " " + QString::number(item.rect.height()) + "\n";
|
|
|
|
textItem->setPlainText(text);
|
|
|
|
// recurse to children
|
|
int childIndex = 0;
|
|
int childCount = item.children.count();
|
|
int segmentSize = item.width / qMax(1, childCount);
|
|
int segmentCenterOffset = segmentSize / 2;
|
|
int segmentsStart = xPos - (item.width / 2);
|
|
foreach (TreeItem child, item.children) {
|
|
// spread the children out, covering the width, centered on xPos
|
|
int segmentPosition = segmentsStart + (segmentSize * childIndex) + segmentCenterOffset;
|
|
addGraphicsItems(child, row + 1, segmentPosition);
|
|
++childIndex;
|
|
}
|
|
|
|
// add lines from parents to kids
|
|
int boxBottom = yPos + m_treeItemHeight;
|
|
int boxMiddleX = xPos + m_treeItemWidth / 2;
|
|
int yBottomMiddle = boxBottom + m_treeItemVerticalPadding / 2;
|
|
int boxTop = yPos;
|
|
int yTopMiddle = boxTop - m_treeItemVerticalPadding / 2;
|
|
|
|
if (row > 0) {
|
|
QGraphicsLineItem *childVerticalStem = new QGraphicsLineItem();
|
|
childVerticalStem->setLine(boxMiddleX, yTopMiddle, boxMiddleX, boxTop);
|
|
m_treeScene->addItem(childVerticalStem);
|
|
}
|
|
|
|
if (childCount > 0) {
|
|
QGraphicsLineItem *parentVerticalStem = new QGraphicsLineItem();
|
|
parentVerticalStem->setLine(boxMiddleX, boxBottom, boxMiddleX, yBottomMiddle);
|
|
m_treeScene->addItem(parentVerticalStem);
|
|
}
|
|
|
|
if (childCount > 1) {
|
|
QGraphicsLineItem *horizontalStem = new QGraphicsLineItem();
|
|
// match the end points with the horizontal lines
|
|
int lineStartX = segmentsStart + segmentCenterOffset + m_treeItemWidth / 2;
|
|
int lineStopX = segmentsStart + segmentSize * (childCount -1) + segmentCenterOffset + m_treeItemWidth / 2;
|
|
horizontalStem->setLine(lineStartX, yBottomMiddle, lineStopX , yBottomMiddle);
|
|
m_treeScene->addItem(horizontalStem);
|
|
}
|
|
}
|
|
|
|
bool AccessibilitySceneManager::isHidden(QAccessibleInterface *interface)
|
|
{
|
|
QAccessibleInterface *current = interface;
|
|
while (current) {
|
|
|
|
if (current->state().invisible) {
|
|
return true;
|
|
}
|
|
|
|
current = current->parent();
|
|
}
|
|
|
|
return false;
|
|
}
|