terminal/src/interactivity/win32/UiaTextRange.hpp

477 lines
18 KiB
C++
Raw Normal View History

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- UiaTextRange.hpp
Abstract:
- This module provides UI Automation access to the text of the console
window to support both automation tests and accessibility (screen
reading) applications.
Author(s):
- Austin Diviness (AustDi) 2017
--*/
#pragma once
#include "precomp.h"
#include "../inc/IConsoleWindow.hpp"
#include "../types/inc/viewport.hpp"
#include "../../buffer/out/cursor.h"
#include <deque>
#include <tuple>
#ifdef UNIT_TESTING
class UiaTextRangeTests;
#endif
// The UiaTextRange deals with several data structures that have
// similar semantics. In order to keep the information from these data
// structures separated, each structure has its own naming for a
// row.
//
// There is the generic Row, which does not know which data structure
// the row came from.
//
// There is the ViewportRow, which is a 0-indexed row value from the
// viewport. The top row of the viewport is at 0, rows below the top
// row increase in value and rows above the top row get increasingly
// negative.
//
// ScreenInfoRow is a row from the screen info data structure. They
// start at 0 at the top of screen info buffer. Their positions do not
// change but their associated row in the text buffer does change each
// time a new line is written.
//
// TextBufferRow is a row from the text buffer. It is not a ROW
// struct, but rather the index of a row. This is also 0-indexed. A
// TextBufferRow with a value of 0 does not necessarily refer to the
// top row of the console.
typedef int Row;
typedef int ViewportRow;
typedef unsigned int ScreenInfoRow;
typedef unsigned int TextBufferRow;
typedef unsigned long long IdType;
// A Column is a row agnostic value that refers to the column an
// endpoint is equivalent to. It is 0-indexed.
typedef unsigned int Column;
// an endpoint is a char location in the text buffer. endpoint 0 is
// the first char of the 0th row in the text buffer row array.
typedef unsigned int Endpoint;
constexpr IdType InvalidId = 0;
namespace Microsoft::Console::Interactivity::Win32
{
class UiaTextRange final : public ITextRangeProvider
{
private:
static IdType id;
protected:
// indicates which direction a movement operation
// is going
enum class MovementDirection
{
Forward,
Backward
};
// valid increment amounts for forward and
// backward movement
enum class MovementIncrement
{
Forward = 1,
Backward = -1
};
// common information used by the variety of
// movement operations
struct MoveState
{
// screen/column position of _start
ScreenInfoRow StartScreenInfoRow;
Column StartColumn;
// screen/column position of _end
ScreenInfoRow EndScreenInfoRow;
Column EndColumn;
// last row in the direction being moved
ScreenInfoRow LimitingRow;
// first column in the direction being moved
Column FirstColumnInRow;
// last column in the direction being moved
Column LastColumnInRow;
// increment amount
MovementIncrement Increment;
// direction moving
MovementDirection Direction;
MoveState(const UiaTextRange& range,
const MovementDirection direction);
private:
MoveState(const ScreenInfoRow startScreenInfoRow,
const Column startColumn,
const ScreenInfoRow endScreenInfoRow,
const Column endColumn,
const ScreenInfoRow limitingRow,
const Column firstColumnInRow,
const Column lastColumnInRow,
const MovementIncrement increment,
const MovementDirection direction);
#ifdef UNIT_TESTING
friend class ::UiaTextRangeTests;
#endif
};
public:
static std::deque<UiaTextRange*> GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider);
// degenerate range
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider);
// degenerate range at cursor position
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor);
// specific endpoint range
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider,
const Endpoint start,
const Endpoint end,
const bool degenerate);
// range from a UiaPoint
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider,
const UiaPoint point);
~UiaTextRange();
const IdType GetId() const;
const Endpoint GetStart() const;
const Endpoint GetEnd() const;
const bool IsDegenerate() const;
// IUnknown methods
IFACEMETHODIMP_(ULONG)
AddRef();
IFACEMETHODIMP_(ULONG)
Release();
IFACEMETHODIMP QueryInterface(_In_ REFIID riid,
_COM_Outptr_result_maybenull_ void** ppInterface);
// ITextRangeProvider methods
IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal);
IFACEMETHODIMP Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal);
IFACEMETHODIMP CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint,
_In_ ITextRangeProvider* pTargetRange,
_In_ TextPatternRangeEndpoint targetEndpoint,
_Out_ int* pRetVal);
IFACEMETHODIMP ExpandToEnclosingUnit(_In_ TextUnit unit);
IFACEMETHODIMP FindAttribute(_In_ TEXTATTRIBUTEID textAttributeId,
_In_ VARIANT val,
_In_ BOOL searchBackward,
_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal);
IFACEMETHODIMP FindText(_In_ BSTR text,
_In_ BOOL searchBackward,
_In_ BOOL ignoreCase,
_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal);
IFACEMETHODIMP GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId,
_Out_ VARIANT* pRetVal);
IFACEMETHODIMP GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal);
IFACEMETHODIMP GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal);
IFACEMETHODIMP GetText(_In_ int maxLength,
_Out_ BSTR* pRetVal);
IFACEMETHODIMP Move(_In_ TextUnit unit,
_In_ int count,
_Out_ int* pRetVal);
IFACEMETHODIMP MoveEndpointByUnit(_In_ TextPatternRangeEndpoint endpoint,
_In_ TextUnit unit,
_In_ int count,
_Out_ int* pRetVal);
IFACEMETHODIMP MoveEndpointByRange(_In_ TextPatternRangeEndpoint endpoint,
_In_ ITextRangeProvider* pTargetRange,
_In_ TextPatternRangeEndpoint targetEndpoint);
IFACEMETHODIMP Select();
IFACEMETHODIMP AddToSelection();
IFACEMETHODIMP RemoveFromSelection();
IFACEMETHODIMP ScrollIntoView(_In_ BOOL alignToTop);
IFACEMETHODIMP GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal);
protected:
#if _DEBUG
void _outputRowConversions();
void _outputObjectState();
#endif
IRawElementProviderSimple* const _pProvider;
private:
// degenerate range
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider);
// degenerate range at cursor position
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor);
// specific endpoint range
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider,
const Endpoint start,
const Endpoint end,
const bool degenerate);
// range from a UiaPoint
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider,
const UiaPoint point);
UiaTextRange(const UiaTextRange& a);
// used to debug objects passed back and forth
// between the provider and the client
IdType _id;
// Ref counter for COM object
ULONG _cRefs;
// measure units in the form [_start, _end]. _start
// may be a bigger number than _end if the range
// wraps around the end of the text buffer.
//
// In this scenario, _start <= _end
// 0 ............... N (text buffer line indices)
// s-----e (_start to _end)
//
// In this scenario, _start >= end
// 0 ............... N (text buffer line indices)
// ---e s----- (_start to _end)
//
Endpoint _start;
Endpoint _end;
// The msdn documentation (and hence this class) talks a bunch about a
// degenerate range. A range is degenerate if it contains
// no text (both the start and end endpoints are the same). Note that
// a degenerate range may have a position in the text. We indicate a
// degenerate range internally with a bool. If a range is degenerate
// then both endpoints will contain the same value.
bool _degenerate;
static const Microsoft::Console::Types::Viewport& _getViewport();
static HWND _getWindowHandle();
static IConsoleWindow* const _getIConsoleWindow();
static SCREEN_INFORMATION& _getScreenInfo();
static TextBuffer& _getTextBuffer();
static const COORD _getScreenBufferCoords();
static const unsigned int _getTotalRows();
static const unsigned int _getRowWidth();
static const unsigned int _getFirstScreenInfoRowIndex();
static const unsigned int _getLastScreenInfoRowIndex();
static const Column _getFirstColumnIndex();
static const Column _getLastColumnIndex();
const unsigned int _rowCountInRange() const;
static const TextBufferRow _endpointToTextBufferRow(const Endpoint endpoint);
static const ScreenInfoRow _textBufferRowToScreenInfoRow(const TextBufferRow row);
static const TextBufferRow _screenInfoRowToTextBufferRow(const ScreenInfoRow row);
static const Endpoint _textBufferRowToEndpoint(const TextBufferRow row);
static const ScreenInfoRow _endpointToScreenInfoRow(const Endpoint endpoint);
static const Endpoint _screenInfoRowToEndpoint(const ScreenInfoRow row);
static COORD _endpointToCoord(const Endpoint endpoint);
static Endpoint _coordToEndpoint(const COORD coord);
static const Column _endpointToColumn(const Endpoint endpoint);
static const Row _normalizeRow(const Row row);
static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row);
static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row,
const SMALL_RECT viewport);
static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row);
static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row,
const SMALL_RECT viewport);
static const unsigned int _getViewportHeight(const SMALL_RECT viewport);
static const unsigned int _getViewportWidth(const SMALL_RECT viewport);
void _addScreenInfoRowBoundaries(const ScreenInfoRow screenInfoRow,
_Inout_ std::vector<double>& coords) const;
static const int _compareScreenCoords(const ScreenInfoRow rowA,
const Column colA,
const ScreenInfoRow rowB,
const Column colB);
static std::pair<Endpoint, Endpoint> _moveByCharacter(const int moveCount,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::pair<Endpoint, Endpoint> _moveByCharacterForward(const int moveCount,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::pair<Endpoint, Endpoint> _moveByCharacterBackward(const int moveCount,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::pair<Endpoint, Endpoint> _moveByLine(const int moveCount,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::pair<Endpoint, Endpoint> _moveByDocument(const int moveCount,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::tuple<Endpoint, Endpoint, bool>
_moveEndpointByUnitCharacter(const int moveCount,
const TextPatternRangeEndpoint endpoint,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::tuple<Endpoint, Endpoint, bool>
_moveEndpointByUnitCharacterForward(const int moveCount,
const TextPatternRangeEndpoint endpoint,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::tuple<Endpoint, Endpoint, bool>
_moveEndpointByUnitCharacterBackward(const int moveCount,
const TextPatternRangeEndpoint endpoint,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::tuple<Endpoint, Endpoint, bool>
_moveEndpointByUnitLine(const int moveCount,
const TextPatternRangeEndpoint endpoint,
const MoveState moveState,
_Out_ int* const pAmountMoved);
static std::tuple<Endpoint, Endpoint, bool>
_moveEndpointByUnitDocument(const int moveCount,
const TextPatternRangeEndpoint endpoint,
const MoveState moveState,
_Out_ int* const pAmountMoved);
#ifdef UNIT_TESTING
friend class ::UiaTextRangeTests;
#endif
};
namespace UiaTextRangeTracing
{
enum class ApiCall
{
Constructor,
AddRef,
Release,
QueryInterface,
Clone,
Compare,
CompareEndpoints,
ExpandToEnclosingUnit,
FindAttribute,
FindText,
GetAttributeValue,
GetBoundingRectangles,
GetEnclosingElement,
GetText,
Move,
MoveEndpointByUnit,
MoveEndpointByRange,
Select,
AddToSelection,
RemoveFromSelection,
ScrollIntoView,
GetChildren
};
struct IApiMsg
{
};
struct ApiMsgConstructor : public IApiMsg
{
IdType Id;
};
struct ApiMsgClone : public IApiMsg
{
IdType CloneId;
};
struct ApiMsgCompare : public IApiMsg
{
IdType OtherId;
bool Equal;
};
struct ApiMsgCompareEndpoints : public IApiMsg
{
IdType OtherId;
TextPatternRangeEndpoint Endpoint;
TextPatternRangeEndpoint TargetEndpoint;
int Result;
};
struct ApiMsgExpandToEnclosingUnit : public IApiMsg
{
TextUnit Unit;
Endpoint OriginalStart;
Endpoint OriginalEnd;
};
struct ApiMsgGetText : IApiMsg
{
const wchar_t* Text;
};
struct ApiMsgMove : IApiMsg
{
Endpoint OriginalStart;
Endpoint OriginalEnd;
TextUnit Unit;
int RequestedCount;
int MovedCount;
};
struct ApiMsgMoveEndpointByUnit : IApiMsg
{
Endpoint OriginalStart;
Endpoint OriginalEnd;
TextPatternRangeEndpoint Endpoint;
TextUnit Unit;
int RequestedCount;
int MovedCount;
};
struct ApiMsgMoveEndpointByRange : IApiMsg
{
Endpoint OriginalStart;
Endpoint OriginalEnd;
TextPatternRangeEndpoint Endpoint;
TextPatternRangeEndpoint TargetEndpoint;
IdType OtherId;
};
struct ApiMsgScrollIntoView : IApiMsg
{
bool AlignToTop;
};
}
}