Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwindowsuiatextrangeprovider.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtGui/qtguiglobal.h>
5#if QT_CONFIG(accessibility)
6
9#include "qwindowsuiautils.h"
10#include "qwindowscontext.h"
11
12#include <QtGui/qaccessible.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qstring.h>
15#include <QtCore/qvarlengtharray.h>
16
18
19using namespace QWindowsUiAutomation;
20
21
22QWindowsUiaTextRangeProvider::QWindowsUiaTextRangeProvider(QAccessible::Id id, int startOffset, int endOffset) :
23 QWindowsUiaBaseProvider(id),
24 m_startOffset(startOffset),
25 m_endOffset(endOffset)
26{
27 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this << startOffset << endOffset;
28}
29
30QWindowsUiaTextRangeProvider::~QWindowsUiaTextRangeProvider()
31{
32 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
33}
34
35HRESULT QWindowsUiaTextRangeProvider::AddToSelection()
36{
37 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
38 return Select();
39}
40
41HRESULT QWindowsUiaTextRangeProvider::Clone(ITextRangeProvider **pRetVal)
42{
43 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
44
45 if (!pRetVal)
46 return E_INVALIDARG;
47
48 *pRetVal = new QWindowsUiaTextRangeProvider(id(), m_startOffset, m_endOffset);
49 return S_OK;
50}
51
52// Two ranges are considered equal if their start/end points are the same.
53HRESULT QWindowsUiaTextRangeProvider::Compare(ITextRangeProvider *range, BOOL *pRetVal)
54{
55 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
56
57 if (!range || !pRetVal)
58 return E_INVALIDARG;
59
60 auto *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(range);
61 *pRetVal = ((targetProvider->m_startOffset == m_startOffset) && (targetProvider->m_endOffset == m_endOffset));
62 return S_OK;
63}
64
65// Compare different endpoinds between two providers.
66HRESULT QWindowsUiaTextRangeProvider::CompareEndpoints(TextPatternRangeEndpoint endpoint,
67 ITextRangeProvider *targetRange,
68 TextPatternRangeEndpoint targetEndpoint,
69 int *pRetVal)
70{
71 qCDebug(lcQpaUiAutomation) << __FUNCTION__
72 << "endpoint=" << endpoint << "targetRange=" << targetRange
73 << "targetEndpoint=" << targetEndpoint << "this: " << this;
74
75 if (!targetRange || !pRetVal)
76 return E_INVALIDARG;
77
78 auto *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
79
80 int point = (endpoint == TextPatternRangeEndpoint_Start) ? m_startOffset : m_endOffset;
81 int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ?
82 targetProvider->m_startOffset : targetProvider->m_endOffset;
83 *pRetVal = point - targetPoint;
84 return S_OK;
85}
86
87// Expands/normalizes the range for a given text unit.
88HRESULT QWindowsUiaTextRangeProvider::ExpandToEnclosingUnit(TextUnit unit)
89{
90 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "unit=" << unit << "this: " << this;
91
92 QAccessibleInterface *accessible = accessibleInterface();
93 if (!accessible)
95
96 QAccessibleTextInterface *textInterface = accessible->textInterface();
97 if (!textInterface)
99
100 int len = textInterface->characterCount();
101 if (len < 1) {
102 m_startOffset = 0;
103 m_endOffset = 0;
104 } else {
105 if (unit == TextUnit_Character) {
106 m_startOffset = qBound(0, m_startOffset, len - 1);
107 m_endOffset = m_startOffset + 1;
108 } else {
109 QString text = textInterface->text(0, len);
110 const int start = m_startOffset >= 0 && m_startOffset < len
111 ? m_startOffset : len - 1;
112 for (int t = start; t >= 0; --t) {
113 if (!isTextUnitSeparator(unit, text[t]) && ((t == 0) || isTextUnitSeparator(unit, text[t - 1]))) {
114 m_startOffset = t;
115 break;
116 }
117 }
118 for (int t = m_startOffset; t < len; ++t) {
119 if ((t == len - 1) || (isTextUnitSeparator(unit, text[t]) && ((unit == TextUnit_Word) || !isTextUnitSeparator(unit, text[t + 1])))) {
120 m_endOffset = t + 1;
121 break;
122 }
123 }
124 }
125 }
126 return S_OK;
127}
128
129// Not supported.
130HRESULT QWindowsUiaTextRangeProvider::FindAttribute(TEXTATTRIBUTEID /* attributeId */,
131 VARIANT /* val */, BOOL /* backward */,
132 ITextRangeProvider **pRetVal)
133{
134 if (!pRetVal)
135 return E_INVALIDARG;
136 *pRetVal = nullptr;
137 return S_OK;
138}
139
140// Returns the value of a given attribute.
141HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTATTRIBUTEID attributeId,
142 VARIANT *pRetVal)
143{
144 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "attributeId=" << attributeId << "this: " << this;
145
146 if (!pRetVal)
147 return E_INVALIDARG;
148 clearVariant(pRetVal);
149
150 QAccessibleInterface *accessible = accessibleInterface();
151 if (!accessible)
153
154 QAccessibleTextInterface *textInterface = accessible->textInterface();
155 if (!textInterface)
157
158 switch (attributeId) {
160 setVariantBool(accessible->state().readOnly, pRetVal);
161 break;
163 if (textInterface->cursorPosition() == 0)
165 else if (textInterface->cursorPosition() == textInterface->characterCount())
166 setVariantI4(CaretPosition_EndOfLine, pRetVal);
167 else
168 setVariantI4(CaretPosition_Unknown, pRetVal);
169 break;
170 default:
171 break;
172 }
173 return S_OK;
174}
175
176// Returns an array of bounding rectangles for text lines within the range.
177HRESULT QWindowsUiaTextRangeProvider::GetBoundingRectangles(SAFEARRAY **pRetVal)
178{
179 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
180
181 if (!pRetVal)
182 return E_INVALIDARG;
183
184 QAccessibleInterface *accessible = accessibleInterface();
185 if (!accessible)
187
188 QAccessibleTextInterface *textInterface = accessible->textInterface();
189 if (!textInterface)
191
192 QWindow *window = windowForAccessible(accessible);
193 if (!window)
195
196 int len = textInterface->characterCount();
197 QVarLengthArray<QRect> rectList;
198
199 if ((m_startOffset >= 0) && (m_endOffset <= len) && (m_startOffset < m_endOffset)) {
200 int start, end;
201 textInterface->textAtOffset(m_startOffset, QAccessible::LineBoundary, &start, &end);
202 while ((start >= 0) && (end >= 0)) {
203 int startRange = qMax(start, m_startOffset);
204 int endRange = qMin(end, m_endOffset);
205 if (startRange < endRange) {
206 // Calculates a bounding rectangle for the line and adds it to the list.
207 const QRect startRect = textInterface->characterRect(startRange);
208 const QRect endRect = textInterface->characterRect(endRange - 1);
209 const QRect lineRect(qMin(startRect.x(), endRect.x()),
210 qMin(startRect.y(), endRect.y()),
211 qMax(startRect.x() + startRect.width(), endRect.x() + endRect.width()) - qMin(startRect.x(), endRect.x()),
212 qMax(startRect.y() + startRect.height(), endRect.y() + endRect.height()) - qMin(startRect.y(), endRect.y()));
213 rectList.append(lineRect);
214 }
215 if (end >= len) break;
216 textInterface->textAfterOffset(end + 1, QAccessible::LineBoundary, &start, &end);
217 }
218 }
219
220 if ((*pRetVal = SafeArrayCreateVector(VT_R8, 0, 4 * rectList.size()))) {
221 for (int i = 0; i < rectList.size(); ++i) {
222 // Scale rect for high DPI screens.
223 UiaRect uiaRect;
224 rectToNativeUiaRect(rectList[i], window, &uiaRect);
225 double coords[4] = { uiaRect.left, uiaRect.top, uiaRect.width, uiaRect.height };
226 for (int j = 0; j < 4; ++j) {
227 LONG idx = 4 * i + j;
228 SafeArrayPutElement(*pRetVal, &idx, &coords[j]);
229 }
230 }
231 }
232 return S_OK;
233}
234
235// Returns an array of children elements embedded within the range.
236HRESULT QWindowsUiaTextRangeProvider::GetChildren(SAFEARRAY **pRetVal)
237{
238 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
239
240 if (!pRetVal)
241 return E_INVALIDARG;
242 // Not supporting any children.
243 *pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 0);
244 return S_OK;
245}
246
247// Returns a provider for the enclosing element (text to which the range belongs).
248HRESULT QWindowsUiaTextRangeProvider::GetEnclosingElement(IRawElementProviderSimple **pRetVal)
249{
250 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
251
252 if (!pRetVal)
253 return E_INVALIDARG;
254
255 QAccessibleInterface *accessible = accessibleInterface();
256 if (!accessible)
258
259 *pRetVal = QWindowsUiaMainProvider::providerForAccessible(accessible);
260 return S_OK;
261}
262
263// Gets the text within the range.
264HRESULT QWindowsUiaTextRangeProvider::GetText(int maxLength, BSTR *pRetVal)
265{
266 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << maxLength << "this: " << this;
267
268 if (!pRetVal)
269 return E_INVALIDARG;
270 *pRetVal = nullptr;
271
272 QAccessibleInterface *accessible = accessibleInterface();
273 if (!accessible)
275
276 QAccessibleTextInterface *textInterface = accessible->textInterface();
277 if (!textInterface)
279
280 int len = textInterface->characterCount();
281 QString rangeText;
282 if ((m_startOffset >= 0) && (m_endOffset <= len) && (m_startOffset < m_endOffset))
283 rangeText = textInterface->text(m_startOffset, m_endOffset);
284
285 if ((maxLength > -1) && (rangeText.size() > maxLength))
286 rangeText.truncate(maxLength);
287 *pRetVal = bStrFromQString(rangeText);
288 return S_OK;
289}
290
291// Moves the range a specified number of units (and normalizes it).
292HRESULT QWindowsUiaTextRangeProvider::Move(TextUnit unit, int count, int *pRetVal)
293{
294 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "unit=" << unit << "count=" << count << "this: " << this;
295
296 if (!pRetVal)
297 return E_INVALIDARG;
298 *pRetVal = 0;
299
300 QAccessibleInterface *accessible = accessibleInterface();
301 if (!accessible)
303
304 QAccessibleTextInterface *textInterface = accessible->textInterface();
305 if (!textInterface)
307
308 int len = textInterface->characterCount();
309
310 if (len < 1)
311 return S_OK;
312
313 if (unit == TextUnit_Character) {
314 // Moves the start point, ensuring it lies within the bounds.
315 int start = qBound(0, m_startOffset + count, len - 1);
316 // If range was initially empty, leaves it as is; otherwise, normalizes it to one char.
317 m_endOffset = (m_endOffset > m_startOffset) ? start + 1 : start;
318 *pRetVal = start - m_startOffset; // Returns the actually moved distance.
319 m_startOffset = start;
320 } else {
321 if (count > 0) {
324 } else {
327 }
328 }
329 return S_OK;
330}
331
332// Copies the value of an end point from one range to another.
333HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByRange(TextPatternRangeEndpoint endpoint,
334 ITextRangeProvider *targetRange,
335 TextPatternRangeEndpoint targetEndpoint)
336{
337 if (!targetRange)
338 return E_INVALIDARG;
339
340 qCDebug(lcQpaUiAutomation) << __FUNCTION__
341 << "endpoint=" << endpoint << "targetRange=" << targetRange << "targetEndpoint=" << targetEndpoint << "this: " << this;
342
343 auto *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
344
345 int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ?
346 targetProvider->m_startOffset : targetProvider->m_endOffset;
347
348 // If the moved endpoint crosses the other endpoint, that one is moved too.
349 if (endpoint == TextPatternRangeEndpoint_Start) {
350 m_startOffset = targetPoint;
351 if (m_endOffset < m_startOffset)
352 m_endOffset = m_startOffset;
353 } else {
354 m_endOffset = targetPoint;
355 if (m_endOffset < m_startOffset)
356 m_startOffset = m_endOffset;
357 }
358 return S_OK;
359}
360
361// Moves an endpoint an specific number of units.
362HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByUnit(TextPatternRangeEndpoint endpoint,
363 TextUnit unit, int count,
364 int *pRetVal)
365{
366 qCDebug(lcQpaUiAutomation) << __FUNCTION__
367 << "endpoint=" << endpoint << "unit=" << unit << "count=" << count << "this: " << this;
368
369 if (!pRetVal)
370 return E_INVALIDARG;
371 *pRetVal = 0;
372
373 QAccessibleInterface *accessible = accessibleInterface();
374 if (!accessible)
376
377 QAccessibleTextInterface *textInterface = accessible->textInterface();
378 if (!textInterface)
380
381 int len = textInterface->characterCount();
382
383 if (len < 1)
384 return S_OK;
385
386 if (unit == TextUnit_Character) {
387 if (endpoint == TextPatternRangeEndpoint_Start) {
388 int boundedValue = qBound(0, m_startOffset + count, len - 1);
389 *pRetVal = boundedValue - m_startOffset;
390 m_startOffset = boundedValue;
391 m_endOffset = qBound(m_startOffset, m_endOffset, len);
392 } else {
393 int boundedValue = qBound(0, m_endOffset + count, len);
394 *pRetVal = boundedValue - m_endOffset;
395 m_endOffset = boundedValue;
396 m_startOffset = qBound(0, m_startOffset, m_endOffset);
397 }
398 } else {
399 QString text = textInterface->text(0, len);
400 int moved = 0;
401
402 if (endpoint == TextPatternRangeEndpoint_Start) {
403 if (count > 0) {
404 for (int t = m_startOffset; (t < len - 1) && (moved < count); ++t) {
405 if (isTextUnitSeparator(unit, text[t]) && !isTextUnitSeparator(unit, text[t + 1])) {
406 m_startOffset = t + 1;
407 ++moved;
408 }
409 }
410 m_endOffset = qBound(m_startOffset, m_endOffset, len);
411 } else {
412 const int start = m_startOffset >= 0 && m_startOffset <= len
413 ? m_startOffset : len;
414 for (int t = start - 1; (t >= 0) && (moved > count); --t) {
415 if (!isTextUnitSeparator(unit, text[t]) && ((t == 0) || isTextUnitSeparator(unit, text[t - 1]))) {
416 m_startOffset = t;
417 --moved;
418 }
419 }
420 }
421 } else {
422 if (count > 0) {
423 for (int t = m_endOffset; (t < len) && (moved < count); ++t) {
424 if ((t == len - 1) || (isTextUnitSeparator(unit, text[t]) && ((unit == TextUnit_Word) || !isTextUnitSeparator(unit, text[t + 1])))) {
425 m_endOffset = t + 1;
426 ++moved;
427 }
428 }
429 } else {
430 int end = 0;
431 for (int t = m_endOffset - 2; (t > 0) && (moved > count); --t) {
432 if (isTextUnitSeparator(unit, text[t]) && ((unit == TextUnit_Word) || !isTextUnitSeparator(unit, text[t + 1]))) {
433 end = t + 1;
434 --moved;
435 }
436 }
437 m_endOffset = end;
438 m_startOffset = qBound(0, m_startOffset, m_endOffset);
439 }
440 }
441 *pRetVal = moved;
442 }
443 return S_OK;
444}
445
446HRESULT QWindowsUiaTextRangeProvider::RemoveFromSelection()
447{
448 qCDebug(lcQpaUiAutomation) << __FUNCTION__;
449 // unselects all
450 return unselect();
451}
452
453// Scrolls the range into view.
454HRESULT QWindowsUiaTextRangeProvider::ScrollIntoView(BOOL alignToTop)
455{
456 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "alignToTop=" << alignToTop << "this: " << this;
457
458 QAccessibleInterface *accessible = accessibleInterface();
459 if (!accessible)
461
462 QAccessibleTextInterface *textInterface = accessible->textInterface();
463 if (!textInterface)
465
466 textInterface->scrollToSubstring(m_startOffset, m_endOffset);
467 return S_OK;
468}
469
470// Selects the range.
471HRESULT QWindowsUiaTextRangeProvider::Select()
472{
473 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
474
475 QAccessibleInterface *accessible = accessibleInterface();
476 if (!accessible)
478
479 QAccessibleTextInterface *textInterface = accessible->textInterface();
480 if (!textInterface)
482
483 // unselects all and adds a new selection
484 unselect();
485 textInterface->addSelection(m_startOffset, m_endOffset);
486 return S_OK;
487}
488
489// Not supported.
490HRESULT QWindowsUiaTextRangeProvider::FindText(BSTR /* text */, BOOL /* backward */,
491 BOOL /* ignoreCase */,
492 ITextRangeProvider **pRetVal)
493{
494 if (!pRetVal)
495 return E_INVALIDARG;
496 *pRetVal = nullptr;
497 return S_OK;
498}
499
500// Removes all selected ranges from the text element.
501HRESULT QWindowsUiaTextRangeProvider::unselect()
502{
503 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
504
505 QAccessibleInterface *accessible = accessibleInterface();
506 if (!accessible)
508
509 QAccessibleTextInterface *textInterface = accessible->textInterface();
510 if (!textInterface)
512
513 int selCount = textInterface->selectionCount();
514
515 for (int i = selCount - 1; i >= 0; --i)
516 textInterface->removeSelection(i);
517 return S_OK;
518}
519
521
522#endif // QT_CONFIG(accessibility)
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:238
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:184
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:235
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:187
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
void truncate(qsizetype pos)
Truncates the string at the given position index.
Definition qstring.cpp:6159
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
constexpr size_type size() const noexcept
void append(const T &t)
\inmodule QtGui
Definition qwindow.h:63
QString text
Combined button and popup list for selecting options.
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint GLuint end
GLsizei const GLubyte GLsizei GLenum const void * coords
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLsizei range
GLuint start
GLsizei maxLength
GLenum GLsizei len
GLdouble GLdouble t
Definition qopenglext.h:243
long HRESULT
aWidget window() -> setWindowTitle("New Window Title")
[2]
double left
Definition uiatypes_p.h:146
double height
Definition uiatypes_p.h:149
double width
Definition uiatypes_p.h:148
double top
Definition uiatypes_p.h:147
#define UIA_IsReadOnlyAttributeId
#define UIA_CaretPositionAttributeId
#define UIA_E_ELEMENTNOTAVAILABLE
virtual HRESULT STDMETHODCALLTYPE Select()=0
IRawElementProviderFragment __RPC__deref_out_opt IRawElementProviderFragment ** pRetVal
virtual HRESULT STDMETHODCALLTYPE MoveEndpointByUnit(enum TextPatternRangeEndpoint endpoint, enum TextUnit unit, int count, __RPC__out int *pRetVal)=0
int TEXTATTRIBUTEID
Definition uiatypes_p.h:21
TextPatternRangeEndpoint
Definition uiatypes_p.h:64
@ TextPatternRangeEndpoint_End
Definition uiatypes_p.h:66
@ TextPatternRangeEndpoint_Start
Definition uiatypes_p.h:65
TextUnit
Definition uiatypes_p.h:54
@ TextUnit_Character
Definition uiatypes_p.h:55
@ TextUnit_Word
Definition uiatypes_p.h:57
@ CaretPosition_Unknown
Definition uiatypes_p.h:70
@ CaretPosition_BeginningOfLine
Definition uiatypes_p.h:72
@ CaretPosition_EndOfLine
Definition uiatypes_p.h:71