a11y atspi: Support text interface's GetStringAtOffset

The GetStringAtOffset method was added to the
AT-SPI Text interface in at-spi2-core commmit [1]:

    commit 56220f05bc8b7683911658e1a8aff4a1ab3cab8d
    Author: Mike Gorse <mgorse@suse.com>
    Date:   Mon Aug 19 16:43:02 2013 -0500

        Add atspi_text_get_string_at_offset

As compared to GetTextAtOffset, which is very similar and
already supported in Qt's AT-SPI adapter, the new method
uses a new enum, AtspiTextGranularity, to specify the region
of text that should be returned.

Other than AtspiTextBoundaryType (that is used by
GetTextAtOffset), AtspiTextGranularity now also has
a value for the paragraph granularity
(ATSPI_TEXT_GRANULARITY_PARAGRAPH).

While AtspiTextBoundaryType has two enum values for the
word/sentence/line boundaries (ATSPI_TEXT_BOUNDARY_WORD_START and
ATSPI_TEXT_BOUNDARY_WORD_END, etc.), AtspiTextGranularity
doesn't have this any more, but ATSPI_TEXT_GRANULARITY_WORD
etc. seem to correspond to what the variants ending on "_START"
do when used with GetTextAtOffset, since the documentation says that
the range is "defined by the boundaries of a word
starting at the beginning of the current word and finishing
at the beginning of the following one, if present." (and likewise
for ATSPI_TEXT_GRANULARITY_SENTENCE, ATSPI_TEXT_GRANULARITY_LINE,
ATSPI_TEXT_GRANULARITY_PARAGRAPH).

In order to support GetStringAtOffset, this
adds a new mapping from the AtspiTextGranularity enum values
to the corresponding QAccessible::TextBoundaryType, similar
to what's done for the AtspiTextBoundaryType in the
handling of the GetTextAtOffset method.

The existing AtSpiAdaptor::qAccessibleBoundaryType
method is renamed to
AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiBoundaryType
to clearer distinguish it from the newly introduced
AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiTextGranularity.

No similar new alternatives have been added
to AT-SPI for GetTextBeforeOffset and GetTextAfterOffset.

[1] 56220f05bc

Fixes: QTBUG-105811
Change-Id: I674a760e80c349baea89dcb4ac7aecdef9b2b45f
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Michael Weghorn 2022-08-18 15:21:38 +02:00
parent 45121669f5
commit 9ab11c726e
2 changed files with 56 additions and 5 deletions

View File

@ -438,6 +438,13 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <interface name=\"org.a11y.atspi.Text\">\n"
" <property access=\"read\" type=\"i\" name=\"CharacterCount\"/>\n"
" <property access=\"read\" type=\"i\" name=\"CaretOffset\"/>\n"
" <method name=\"GetStringAtOffset\">\n"
" <arg direction=\"in\" name=\"offset\" type=\"i\"/>\n"
" <arg direction=\"in\" name=\"granularity\" type=\"u\"/>\n"
" <arg direction=\"out\" type=\"s\"/>\n"
" <arg direction=\"out\" name=\"startOffset\" type=\"i\"/>\n"
" <arg direction=\"out\" name=\"endOffset\" type=\"i\"/>\n"
" </method>\n"
" <method name=\"GetText\">\n"
" <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
@ -1836,6 +1843,16 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
QVariantList sel;
sel << start << end;
connection.send(message.createReply(sel));
} else if (function == "GetStringAtOffset"_L1) {
int offset = message.arguments().at(0).toInt();
uint granularity = message.arguments().at(1).toUInt();
if (!isValidAtspiTextGranularity(granularity))
return false;
int startOffset, endOffset;
QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiTextGranularity(granularity), &startOffset, &endOffset);
QVariantList ret;
ret << text << startOffset << endOffset;
connection.send(message.createReply(ret));
} else if (function == "GetText"_L1) {
int startOffset = message.arguments().at(0).toInt();
int endOffset = message.arguments().at(1).toInt();
@ -1846,7 +1863,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
int type = message.arguments().at(1).toUInt();
int startOffset, endOffset;
QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
QVariantList ret;
ret << text << startOffset << endOffset;
connection.send(message.createReply(ret));
@ -1854,7 +1871,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
int type = message.arguments().at(1).toUInt();
int startOffset, endOffset;
QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
QVariantList ret;
ret << text << startOffset << endOffset;
connection.send(message.createReply(ret));
@ -1862,7 +1879,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
int type = message.arguments().at(1).toUInt();
int startOffset, endOffset;
QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
QVariantList ret;
ret << text << startOffset << endOffset;
connection.send(message.createReply(ret));
@ -1894,7 +1911,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
return true;
}
QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const
QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiBoundaryType(int atspiTextBoundaryType)
{
switch (atspiTextBoundaryType) {
case ATSPI_TEXT_BOUNDARY_CHAR:
@ -1913,6 +1930,38 @@ QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTex
return QAccessible::CharBoundary;
}
bool AtSpiAdaptor::isValidAtspiTextGranularity(uint atspiTextGranularity)
{
if (atspiTextGranularity == ATSPI_TEXT_GRANULARITY_CHAR
|| atspiTextGranularity == ATSPI_TEXT_GRANULARITY_WORD
|| atspiTextGranularity == ATSPI_TEXT_GRANULARITY_SENTENCE
|| atspiTextGranularity == ATSPI_TEXT_GRANULARITY_LINE
|| atspiTextGranularity == ATSPI_TEXT_GRANULARITY_PARAGRAPH)
return true;
qCWarning(lcAccessibilityAtspi) << "Unknown value" << atspiTextGranularity << "for AT-SPI text granularity type";
return false;
}
QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiTextGranularity(uint atspiTextGranularity)
{
Q_ASSERT(isValidAtspiTextGranularity(atspiTextGranularity));
switch (atspiTextGranularity) {
case ATSPI_TEXT_GRANULARITY_CHAR:
return QAccessible::CharBoundary;
case ATSPI_TEXT_GRANULARITY_WORD:
return QAccessible::WordBoundary;
case ATSPI_TEXT_GRANULARITY_SENTENCE:
return QAccessible::SentenceBoundary;
case ATSPI_TEXT_GRANULARITY_LINE:
return QAccessible::LineBoundary;
case ATSPI_TEXT_GRANULARITY_PARAGRAPH:
return QAccessible::ParagraphBoundary;
}
return QAccessible::CharBoundary;
}
namespace
{
struct AtSpiAttribute {

View File

@ -106,7 +106,9 @@ private:
QString getAttributeValue(QAccessibleInterface *, int offset, const QString &attributeName) const;
QList<QVariant> getCharacterExtents(QAccessibleInterface *, int offset, uint coordType) const;
QList<QVariant> getRangeExtents(QAccessibleInterface *, int startOffset, int endOffset, uint coordType) const;
QAccessible::TextBoundaryType qAccessibleBoundaryType(int atspiTextBoundaryType) const;
static QAccessible::TextBoundaryType qAccessibleBoundaryTypeFromAtspiBoundaryType(int atspiTextBoundaryType);
static bool isValidAtspiTextGranularity(uint coordType);
static QAccessible::TextBoundaryType qAccessibleBoundaryTypeFromAtspiTextGranularity(uint atspiTextGranularity);
static bool inheritsQAction(QObject *object);
// private vars