Enable Word Navigation in UiaTextRange (#3659)
Enables support for word navigation when using an automation client (i.e.: Narrator, etc...). Specifically, adds this functionality to the UiaTextRange class. The only delimiter used is whitespace because that's how words are separated in English. # General "Word Movement" Expectations The resulting text range should include any word break characters that are present at the end of the word, but before the start of the next word. (Source) If you already are on a word, getting the "next word" means you skip the word you are on, and highlight the upcoming word appropriately. (similar idea when moving backwards) # Word Expansion Since word selection is supposed to detect word delimiters already, I figured I'd reuse that code. I moved it from TerminalCore to the TextBuffer. Then I built on top of it by adding an optional additional parameter that decides if you want to include... - the delimiter run when moving forward - the character run when moving backwards It defaults to false so that we don't have to care when using it in selection. But we change it to true when using it in our UiaTextRange # UiaTextRange The code is based on character movement. This allows us to actually work with boundary conditions. The main thing to remember here is that each text range is recorded as a MoveState. The text range is most easily defined when you think about the start Endpoint and the end Endpoint. An Endpoint is just a linear 1-dimensional indexing of the text buffer. Examples: - Endpoint 0 --> (0,0) - Endpoint 79 --> (79,0) (when the buffer width is 80) - Endpoint 80 -->(0,1) (when the buffer width is 80) - When moving forward, the strategy is to focus on moving the end Endpoint. That way, we properly get the indexing for the "next" word (this also fixes a wrapping issue). Then, we update the start Endpoint. (This is reversed for moving backwards). - When moving a specific Endpoint, we just have a few extra if statements to properly adjust for moving start vs end. # Hooking it up All we really had to do is add an enum. This part was super easy :) I originally wanted the delimiters to be able to be defined. I'm not so sure about that anymore. Either way, I hardcoded our delimiter into a variable so if we ever want to expand on it or make that customizable, we just modify that variable. # Defining your own word delimiters - Import a word delimiter into the constructor of the ScreenInfoUiaProvider (SIUP) - This defines a word delimiter for all the UiaTextRanges (UTR) created by in this context - import a word delimiter into the UTR directly - this provides more control over what a "word" is - this can be useful if you have an idea of what text a particular UTR will encounter and you want to customize the word navigation for it (i.e consider adding / or \\ for file paths) The default param of " " is scattered throughout because this is the word delimiter used in the English language.
This commit is contained in:
parent
8d9f657d43
commit
4b48f74f5f
|
@ -951,6 +951,118 @@ Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcep
|
|||
return _renderTarget;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the COORD for the beginning of the word you are on
|
||||
// Arguments:
|
||||
// - target - a COORD on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - includeCharacterRun - include the character run located at the beginning of the word
|
||||
// Return Value:
|
||||
// - The COORD for the first character on the "word" (inclusive)
|
||||
const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool includeCharacterRun) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
COORD result = target;
|
||||
|
||||
// can't expand left
|
||||
if (target.X == bufferSize.Left())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto bufferIterator = GetTextDataAt(result);
|
||||
const auto initialDelimiter = _GetDelimiterClass(*bufferIterator, wordDelimiters);
|
||||
while (result.X > bufferSize.Left() && (_GetDelimiterClass(*bufferIterator, wordDelimiters) == initialDelimiter))
|
||||
{
|
||||
bufferSize.DecrementInBounds(result);
|
||||
--bufferIterator;
|
||||
}
|
||||
|
||||
if (includeCharacterRun)
|
||||
{
|
||||
// include character run for readable word
|
||||
if (_GetDelimiterClass(*bufferIterator, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
result = GetWordStart(result, wordDelimiters);
|
||||
}
|
||||
}
|
||||
else if (_GetDelimiterClass(*bufferIterator, wordDelimiters) != initialDelimiter)
|
||||
{
|
||||
// move off of delimiter
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the COORD for the end of the word you are on
|
||||
// Arguments:
|
||||
// - target - a COORD on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - includeDelimiterRun - include the delimiter runs located at the end of the word
|
||||
// Return Value:
|
||||
// - The COORD for the last character on the "word" (inclusive)
|
||||
const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool includeDelimiterRun) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
COORD result = target;
|
||||
|
||||
// can't expand right
|
||||
if (target.X == bufferSize.RightInclusive())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto bufferIterator = GetTextDataAt(result);
|
||||
const auto initialDelimiter = _GetDelimiterClass(*bufferIterator, wordDelimiters);
|
||||
while (result.X < bufferSize.RightInclusive() && (_GetDelimiterClass(*bufferIterator, wordDelimiters) == initialDelimiter))
|
||||
{
|
||||
bufferSize.IncrementInBounds(result);
|
||||
++bufferIterator;
|
||||
}
|
||||
|
||||
if (includeDelimiterRun)
|
||||
{
|
||||
// include delimiter run after word
|
||||
if (_GetDelimiterClass(*bufferIterator, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
result = GetWordEnd(result, wordDelimiters);
|
||||
}
|
||||
}
|
||||
else if (_GetDelimiterClass(*bufferIterator, wordDelimiters) != initialDelimiter)
|
||||
{
|
||||
// move off of delimiter
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - get delimiter class for buffer cell data
|
||||
// - used for double click selection and uia word navigation
|
||||
// Arguments:
|
||||
// - cellChar: the char saved to the buffer cell under observation
|
||||
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
|
||||
// Return Value:
|
||||
// - the delimiter class for the given char
|
||||
TextBuffer::DelimiterClass TextBuffer::_GetDelimiterClass(const std::wstring_view cellChar, const std::wstring_view wordDelimiters) const noexcept
|
||||
{
|
||||
if (cellChar.at(0) <= UNICODE_SPACE)
|
||||
{
|
||||
return DelimiterClass::ControlChar;
|
||||
}
|
||||
else if (wordDelimiters.find(cellChar) != std::wstring_view::npos)
|
||||
{
|
||||
return DelimiterClass::DelimiterChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DelimiterClass::RegularChar;
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves the text data from the selected region and presents it in a clipboard-ready format (given little post-processing).
|
||||
// Arguments:
|
||||
|
|
|
@ -130,6 +130,9 @@ public:
|
|||
|
||||
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
|
||||
|
||||
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool includeCharacterRun = false) const;
|
||||
const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool includeDelimiterRun = false) const;
|
||||
|
||||
class TextAndColor
|
||||
{
|
||||
public:
|
||||
|
@ -186,6 +189,14 @@ private:
|
|||
ROW& _GetFirstRow();
|
||||
ROW& _GetPrevRowNoWrap(const ROW& row);
|
||||
|
||||
enum class DelimiterClass
|
||||
{
|
||||
ControlChar,
|
||||
DelimiterChar,
|
||||
RegularChar
|
||||
};
|
||||
DelimiterClass _GetDelimiterClass(const std::wstring_view cellChar, const std::wstring_view wordDelimiters) const noexcept;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
friend class UiaTextRangeTests;
|
||||
|
|
|
@ -95,7 +95,7 @@ const winrt::Windows::UI::Xaml::Thickness TermControlUiaProvider::GetPadding() c
|
|||
return _termControl->GetPadding();
|
||||
}
|
||||
|
||||
HRESULT TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, _Out_ std::deque<ComPtr<UiaTextRangeBase>>& result)
|
||||
HRESULT TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<ComPtr<UiaTextRangeBase>>& result)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ HRESULT TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimpl
|
|||
typename std::remove_reference<decltype(result)>::type temporaryResult;
|
||||
|
||||
std::deque<ComPtr<UiaTextRange>> ranges;
|
||||
RETURN_IF_FAILED(UiaTextRange::GetSelectionRanges(_pData, pProvider, ranges));
|
||||
RETURN_IF_FAILED(UiaTextRange::GetSelectionRanges(_pData, pProvider, wordDelimiters, ranges));
|
||||
|
||||
while (!ranges.empty())
|
||||
{
|
||||
|
@ -117,24 +117,25 @@ HRESULT TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimpl
|
|||
CATCH_RETURN();
|
||||
}
|
||||
|
||||
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, cursor));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -143,24 +144,26 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
|
|||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, degenerate));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, degenerate, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, point));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -47,14 +47,15 @@ namespace Microsoft::Terminal
|
|||
const winrt::Windows::UI::Xaml::Thickness GetPadding() const;
|
||||
|
||||
protected:
|
||||
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, _Out_ std::deque<WRL::ComPtr<Microsoft::Console::Types::UiaTextRangeBase>>& selectionRanges) override;
|
||||
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<WRL::ComPtr<Microsoft::Console::Types::UiaTextRangeBase>>& selectionRanges) override;
|
||||
|
||||
// degenerate range
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
// degenerate range at cursor position
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
// specific endpoint range
|
||||
|
@ -62,11 +63,13 @@ namespace Microsoft::Terminal
|
|||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
// range from a UiaPoint
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -11,6 +11,7 @@ using namespace Microsoft::WRL;
|
|||
|
||||
HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* pProvider,
|
||||
_In_ const std::wstring_view wordDelimiters,
|
||||
_Out_ std::deque<ComPtr<UiaTextRange>>& ranges)
|
||||
{
|
||||
try
|
||||
|
@ -28,7 +29,7 @@ HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
|
|||
Endpoint end = _screenInfoRowToEndpoint(pData, currentRow) + rect.RightInclusive();
|
||||
|
||||
ComPtr<UiaTextRange> range;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&range, pData, pProvider, start, end, false));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&range, pData, pProvider, start, end, false, wordDelimiters));
|
||||
temporaryResult.emplace_back(std::move(range));
|
||||
}
|
||||
std::swap(temporaryResult, ranges);
|
||||
|
@ -38,33 +39,36 @@ HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
|
|||
}
|
||||
|
||||
// degenerate range constructor.
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider)
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters)
|
||||
{
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider);
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters);
|
||||
}
|
||||
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor)
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters)
|
||||
{
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor);
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters);
|
||||
}
|
||||
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate)
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters)
|
||||
{
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, degenerate);
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, degenerate, wordDelimiters);
|
||||
}
|
||||
|
||||
// returns a degenerate text range of the start of the row closest to the y value of point
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point)
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters)
|
||||
{
|
||||
RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider));
|
||||
RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters));
|
||||
Initialize(point);
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -25,30 +25,35 @@ namespace Microsoft::Terminal
|
|||
public:
|
||||
static HRESULT GetSelectionRanges(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* pProvider,
|
||||
_In_ const std::wstring_view wordDelimiters,
|
||||
_Out_ std::deque<WRL::ComPtr<UiaTextRange>>& ranges);
|
||||
|
||||
UiaTextRange() = default;
|
||||
|
||||
// degenerate range
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider);
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
// degenerate range at cursor position
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor);
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
// specific endpoint range
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate);
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
// range from a UiaPoint
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point);
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
HRESULT RuntimeClassInitialize(const UiaTextRange& a);
|
||||
|
||||
|
|
|
@ -178,12 +178,6 @@ private:
|
|||
Word,
|
||||
Line
|
||||
};
|
||||
enum class DelimiterClass
|
||||
{
|
||||
ControlChar,
|
||||
DelimiterChar,
|
||||
RegularChar
|
||||
};
|
||||
COORD _selectionAnchor;
|
||||
COORD _endSelectionPosition;
|
||||
bool _boxSelection;
|
||||
|
@ -241,7 +235,6 @@ private:
|
|||
SHORT _ExpandWideGlyphSelectionRight(const SHORT xPos, const SHORT yPos) const;
|
||||
COORD _ExpandDoubleClickSelectionLeft(const COORD position) const;
|
||||
COORD _ExpandDoubleClickSelectionRight(const COORD position) const;
|
||||
DelimiterClass _GetDelimiterClass(const std::wstring_view cellChar) const noexcept;
|
||||
COORD _ConvertToBufferCell(const COORD viewportPos) const;
|
||||
const bool _IsSingleCellSelection() const noexcept;
|
||||
std::tuple<COORD, COORD> _PreprocessSelectionCoords() const;
|
||||
|
|
|
@ -360,33 +360,11 @@ const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool tri
|
|||
// - updated copy of "position" to new expanded location (with vertical offset)
|
||||
COORD Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) const
|
||||
{
|
||||
const auto bufferViewport = _buffer->GetSize();
|
||||
|
||||
// force position to be within bounds
|
||||
COORD positionWithOffsets = position;
|
||||
bufferViewport.Clamp(positionWithOffsets);
|
||||
_buffer->GetSize().Clamp(positionWithOffsets);
|
||||
|
||||
// can't expand left
|
||||
if (position.X == bufferViewport.Left())
|
||||
{
|
||||
return positionWithOffsets;
|
||||
}
|
||||
|
||||
auto bufferIterator = _buffer->GetTextDataAt(positionWithOffsets);
|
||||
const auto startedOnDelimiter = _GetDelimiterClass(*bufferIterator);
|
||||
while (positionWithOffsets.X > bufferViewport.Left() && (_GetDelimiterClass(*bufferIterator) == startedOnDelimiter))
|
||||
{
|
||||
bufferViewport.DecrementInBounds(positionWithOffsets);
|
||||
--bufferIterator;
|
||||
}
|
||||
|
||||
if (_GetDelimiterClass(*bufferIterator) != startedOnDelimiter)
|
||||
{
|
||||
// move off of delimiter to highlight properly
|
||||
bufferViewport.IncrementInBounds(positionWithOffsets);
|
||||
}
|
||||
|
||||
return positionWithOffsets;
|
||||
return _buffer->GetWordStart(positionWithOffsets, _wordDelimiters);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -398,56 +376,11 @@ COORD Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) const
|
|||
// - updated copy of "position" to new expanded location (with vertical offset)
|
||||
COORD Terminal::_ExpandDoubleClickSelectionRight(const COORD position) const
|
||||
{
|
||||
const auto bufferViewport = _buffer->GetSize();
|
||||
|
||||
// force position to be within bounds
|
||||
COORD positionWithOffsets = position;
|
||||
bufferViewport.Clamp(positionWithOffsets);
|
||||
_buffer->GetSize().Clamp(positionWithOffsets);
|
||||
|
||||
// can't expand right
|
||||
if (position.X == bufferViewport.RightInclusive())
|
||||
{
|
||||
return positionWithOffsets;
|
||||
}
|
||||
|
||||
auto bufferIterator = _buffer->GetTextDataAt(positionWithOffsets);
|
||||
const auto startedOnDelimiter = _GetDelimiterClass(*bufferIterator);
|
||||
while (positionWithOffsets.X < bufferViewport.RightInclusive() && (_GetDelimiterClass(*bufferIterator) == startedOnDelimiter))
|
||||
{
|
||||
bufferViewport.IncrementInBounds(positionWithOffsets);
|
||||
++bufferIterator;
|
||||
}
|
||||
|
||||
if (_GetDelimiterClass(*bufferIterator) != startedOnDelimiter)
|
||||
{
|
||||
// move off of delimiter to highlight properly
|
||||
bufferViewport.DecrementInBounds(positionWithOffsets);
|
||||
}
|
||||
|
||||
return positionWithOffsets;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - get delimiter class for buffer cell data
|
||||
// - used for double click selection
|
||||
// Arguments:
|
||||
// - cellChar: the char saved to the buffer cell under observation
|
||||
// Return Value:
|
||||
// - the delimiter class for the given char
|
||||
Terminal::DelimiterClass Terminal::_GetDelimiterClass(const std::wstring_view cellChar) const noexcept
|
||||
{
|
||||
if (cellChar[0] <= UNICODE_SPACE)
|
||||
{
|
||||
return DelimiterClass::ControlChar;
|
||||
}
|
||||
else if (_wordDelimiters.find(cellChar) != std::wstring_view::npos)
|
||||
{
|
||||
return DelimiterClass::DelimiterChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DelimiterClass::RegularChar;
|
||||
}
|
||||
return _buffer->GetWordEnd(positionWithOffsets, _wordDelimiters);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -97,7 +97,7 @@ void ScreenInfoUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
|
|||
_pUiaParent->ChangeViewport(NewWindow);
|
||||
}
|
||||
|
||||
HRESULT ScreenInfoUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, _Out_ std::deque<ComPtr<UiaTextRangeBase>>& result)
|
||||
HRESULT ScreenInfoUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<ComPtr<UiaTextRangeBase>>& result)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -105,7 +105,7 @@ HRESULT ScreenInfoUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple
|
|||
typename std::remove_reference<decltype(result)>::type temporaryResult;
|
||||
|
||||
std::deque<ComPtr<UiaTextRange>> ranges;
|
||||
RETURN_IF_FAILED(UiaTextRange::GetSelectionRanges(_pData, pProvider, ranges));
|
||||
RETURN_IF_FAILED(UiaTextRange::GetSelectionRanges(_pData, pProvider, wordDelimiters, ranges));
|
||||
|
||||
while (!ranges.empty())
|
||||
{
|
||||
|
@ -119,24 +119,25 @@ HRESULT ScreenInfoUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple
|
|||
CATCH_RETURN();
|
||||
}
|
||||
|
||||
HRESULT ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
HRESULT ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, cursor));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -145,24 +146,26 @@ HRESULT ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* c
|
|||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, degenerate));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, degenerate, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
UiaTextRange* result = nullptr;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, point));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
|
||||
*ppUtr = result;
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -43,14 +43,15 @@ namespace Microsoft::Console::Interactivity::Win32
|
|||
void ChangeViewport(const SMALL_RECT NewWindow);
|
||||
|
||||
protected:
|
||||
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, _Out_ std::deque<WRL::ComPtr<Microsoft::Console::Types::UiaTextRangeBase>>& selectionRanges) override;
|
||||
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<WRL::ComPtr<Microsoft::Console::Types::UiaTextRangeBase>>& selectionRanges) override;
|
||||
|
||||
// degenerate range
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
// degenerate range at cursor position
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
// specific endpoint range
|
||||
|
@ -58,11 +59,13 @@ namespace Microsoft::Console::Interactivity::Win32
|
|||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
// range from a UiaPoint
|
||||
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -15,6 +15,7 @@ using Microsoft::Console::Interactivity::ServiceLocator;
|
|||
|
||||
HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* pProvider,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ std::deque<ComPtr<UiaTextRange>>& ranges)
|
||||
{
|
||||
try
|
||||
|
@ -32,7 +33,7 @@ HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
|
|||
Endpoint end = _screenInfoRowToEndpoint(pData, currentRow) + rect.RightInclusive();
|
||||
|
||||
ComPtr<UiaTextRange> range;
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&range, pData, pProvider, start, end, false));
|
||||
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&range, pData, pProvider, start, end, false, wordDelimiters));
|
||||
temporaryResult.emplace_back(std::move(range));
|
||||
}
|
||||
std::swap(temporaryResult, ranges);
|
||||
|
@ -42,17 +43,18 @@ HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
|
|||
}
|
||||
|
||||
// degenerate range constructor.
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider)
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters)
|
||||
{
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider);
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters);
|
||||
}
|
||||
|
||||
// degenerate range at cursor position
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor)
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters)
|
||||
{
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor);
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters);
|
||||
}
|
||||
|
||||
// specific endpoint range
|
||||
|
@ -60,17 +62,19 @@ HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
|
|||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate)
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters)
|
||||
{
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, degenerate);
|
||||
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, degenerate, wordDelimiters);
|
||||
}
|
||||
|
||||
// returns a degenerate text range of the start of the row closest to the y value of point
|
||||
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point)
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters)
|
||||
{
|
||||
RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider));
|
||||
RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters));
|
||||
Initialize(point);
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -26,30 +26,35 @@ namespace Microsoft::Console::Interactivity::Win32
|
|||
public:
|
||||
static HRESULT GetSelectionRanges(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* pProvider,
|
||||
_In_ const std::wstring_view wordDelimiters,
|
||||
_Out_ std::deque<WRL::ComPtr<UiaTextRange>>& ranges);
|
||||
|
||||
UiaTextRange() = default;
|
||||
|
||||
// degenerate range
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider);
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
// degenerate range at cursor position
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor);
|
||||
const Cursor& cursor,
|
||||
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
// specific endpoint range
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate);
|
||||
const bool degenerate,
|
||||
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
// range from a UiaPoint
|
||||
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point);
|
||||
const UiaPoint point,
|
||||
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
|
||||
|
||||
HRESULT RuntimeClassInitialize(const UiaTextRange& a);
|
||||
|
||||
|
|
|
@ -473,6 +473,271 @@ class UiaTextRangeTests
|
|||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveByWord_EmptyBuffer)
|
||||
{
|
||||
const Column firstColumnIndex = 0;
|
||||
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||
const ScreenInfoRow topRow = 0;
|
||||
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
|
||||
|
||||
// clang-format off
|
||||
const std::vector<std::tuple<std::wstring,
|
||||
UiaTextRange::MoveState,
|
||||
int, // amount to move
|
||||
int, // amount actually moved
|
||||
Endpoint, // start
|
||||
Endpoint // end
|
||||
>> testData =
|
||||
{
|
||||
{
|
||||
L"can't move backward from (0, 0)",
|
||||
{
|
||||
0, 0,
|
||||
0, 2,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-1,
|
||||
0,
|
||||
0u,
|
||||
lastColumnIndex
|
||||
},
|
||||
|
||||
{
|
||||
L"can move backward within a row",
|
||||
{
|
||||
0, 1,
|
||||
0, 2,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-1,
|
||||
-1,
|
||||
0u,
|
||||
lastColumnIndex
|
||||
},
|
||||
|
||||
{
|
||||
L"can move forward in a row",
|
||||
{
|
||||
2, 1,
|
||||
4, 5,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
5,
|
||||
5,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 8),
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 8) + lastColumnIndex
|
||||
},
|
||||
|
||||
{
|
||||
L"can't move past the last column in the last row",
|
||||
{
|
||||
bottomRow, lastColumnIndex,
|
||||
bottomRow, lastColumnIndex,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
5,
|
||||
0,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow),
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex
|
||||
},
|
||||
|
||||
{
|
||||
L"can move to a new row when necessary when moving forward",
|
||||
{
|
||||
topRow, lastColumnIndex,
|
||||
topRow, lastColumnIndex,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
5,
|
||||
5,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5),
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + lastColumnIndex
|
||||
},
|
||||
|
||||
{
|
||||
L"can move to a new row when necessary when moving backward",
|
||||
{
|
||||
topRow + 1, firstColumnIndex,
|
||||
topRow + 1, lastColumnIndex,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-5,
|
||||
-2,
|
||||
0u,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex
|
||||
}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for (auto data : testData)
|
||||
{
|
||||
Log::Comment(std::get<0>(data).c_str());
|
||||
int amountMoved;
|
||||
std::pair<Endpoint, Endpoint> newEndpoints = UiaTextRange::_moveByWord(_pUiaData,
|
||||
std::get<2>(data),
|
||||
std::get<1>(data),
|
||||
L"",
|
||||
&amountMoved);
|
||||
|
||||
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
|
||||
VERIFY_ARE_EQUAL(std::get<4>(data), newEndpoints.first);
|
||||
VERIFY_ARE_EQUAL(std::get<5>(data), newEndpoints.second);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveByWord_NonEmptyBuffer)
|
||||
{
|
||||
const Column firstColumnIndex = 0;
|
||||
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||
const ScreenInfoRow topRow = 0;
|
||||
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
|
||||
|
||||
const std::wstring_view text[] = {
|
||||
L"word1 word2 word3",
|
||||
L"word4 word5 word6"
|
||||
};
|
||||
|
||||
for (auto i = 0; i < 2; i++)
|
||||
{
|
||||
_pTextBuffer->WriteLine(text[i], { 0, gsl::narrow<SHORT>(i) });
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
const std::vector<std::tuple<std::wstring,
|
||||
UiaTextRange::MoveState,
|
||||
int, // amount to move
|
||||
int, // amount actually moved
|
||||
Endpoint, // start
|
||||
Endpoint // end
|
||||
>> testData =
|
||||
{
|
||||
{
|
||||
L"move backwards on the word by (0,0)",
|
||||
{
|
||||
0, 1,
|
||||
0, 2,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-1,
|
||||
-1,
|
||||
0u,
|
||||
6
|
||||
},
|
||||
|
||||
{
|
||||
L"get next word while on first word",
|
||||
{
|
||||
0, 0,
|
||||
0, 0,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
6
|
||||
},
|
||||
|
||||
{
|
||||
L"get next word twice while on first word",
|
||||
{
|
||||
0, 0,
|
||||
0, 0,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
2,
|
||||
2,
|
||||
7,
|
||||
14
|
||||
},
|
||||
|
||||
{
|
||||
L"move forward to next row with word",
|
||||
{
|
||||
topRow, lastColumnIndex,
|
||||
topRow, lastColumnIndex,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
1,
|
||||
1,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1),
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + 6
|
||||
},
|
||||
|
||||
{
|
||||
L"move backwards to previous row with word",
|
||||
{
|
||||
topRow + 1, firstColumnIndex,
|
||||
topRow + 1, lastColumnIndex,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-1,
|
||||
-1,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 15,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex
|
||||
}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for (auto data : testData)
|
||||
{
|
||||
Log::Comment(std::get<0>(data).c_str());
|
||||
int amountMoved;
|
||||
std::pair<Endpoint, Endpoint> newEndpoints = UiaTextRange::_moveByWord(_pUiaData,
|
||||
std::get<2>(data),
|
||||
std::get<1>(data),
|
||||
L"",
|
||||
&amountMoved);
|
||||
|
||||
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
|
||||
VERIFY_ARE_EQUAL(std::get<4>(data), newEndpoints.first);
|
||||
VERIFY_ARE_EQUAL(std::get<5>(data), newEndpoints.second);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveByLine)
|
||||
{
|
||||
const Column firstColumnIndex = 0;
|
||||
|
@ -798,6 +1063,207 @@ class UiaTextRangeTests
|
|||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveEndpointByUnitWord)
|
||||
{
|
||||
const Column firstColumnIndex = 0;
|
||||
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||
const ScreenInfoRow topRow = 0;
|
||||
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
|
||||
|
||||
const std::wstring_view text[] = {
|
||||
L"word1 word2 word3",
|
||||
L"word4 word5 word6"
|
||||
};
|
||||
|
||||
for (auto i = 0; i < 2; i++)
|
||||
{
|
||||
_pTextBuffer->WriteLine(text[i], { 0, gsl::narrow<SHORT>(i) });
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
const std::vector<std::tuple<std::wstring,
|
||||
UiaTextRange::MoveState,
|
||||
int, // amount to move
|
||||
int, // amount actually moved
|
||||
TextPatternRangeEndpoint, // endpoint to move
|
||||
Endpoint, // start
|
||||
Endpoint, // end
|
||||
bool // degenerate
|
||||
>> testData =
|
||||
{
|
||||
{
|
||||
L"can't move _start past the beginning of the document when _start is positioned at the beginning",
|
||||
{
|
||||
topRow, firstColumnIndex,
|
||||
topRow, lastColumnIndex,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-1,
|
||||
0,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
|
||||
false
|
||||
},
|
||||
|
||||
{
|
||||
L"can partially move _start to the begining of the document when it is closer than the move count requested",
|
||||
{
|
||||
topRow, firstColumnIndex + 15,
|
||||
topRow, lastColumnIndex,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-5,
|
||||
-2,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
|
||||
false
|
||||
},
|
||||
|
||||
{
|
||||
L"can't move _end past the begining of the document",
|
||||
{
|
||||
topRow, firstColumnIndex,
|
||||
topRow, firstColumnIndex + 2,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-2,
|
||||
-1,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
|
||||
false
|
||||
},
|
||||
|
||||
{
|
||||
L"_start follows _end when passed during movement",
|
||||
{
|
||||
topRow + 1, firstColumnIndex + 2,
|
||||
topRow + 1, firstColumnIndex + 10,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Backward,
|
||||
UiaTextRange::MovementDirection::Backward
|
||||
},
|
||||
-4,
|
||||
-4,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 6,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 6,
|
||||
true
|
||||
},
|
||||
|
||||
{
|
||||
L"can't move _end past the beginning of the document when _end is positioned at the end",
|
||||
{
|
||||
bottomRow, firstColumnIndex,
|
||||
bottomRow, lastColumnIndex,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
1,
|
||||
0,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
|
||||
false
|
||||
},
|
||||
|
||||
{
|
||||
L"can partially move _end to the end of the document when it is closer than the move count requested",
|
||||
{
|
||||
topRow, firstColumnIndex,
|
||||
bottomRow, lastColumnIndex - 3,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
5,
|
||||
1,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
|
||||
false
|
||||
},
|
||||
|
||||
{
|
||||
L"can't move _start past the end of the document",
|
||||
{
|
||||
bottomRow, lastColumnIndex - 4,
|
||||
bottomRow, lastColumnIndex,
|
||||
bottomRow,
|
||||
firstColumnIndex,
|
||||
lastColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
5,
|
||||
1,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
|
||||
false
|
||||
},
|
||||
|
||||
{
|
||||
L"_end follows _start when passed during movement",
|
||||
{
|
||||
topRow, firstColumnIndex,
|
||||
topRow, firstColumnIndex + 3,
|
||||
topRow,
|
||||
lastColumnIndex,
|
||||
firstColumnIndex,
|
||||
UiaTextRange::MovementIncrement::Forward,
|
||||
UiaTextRange::MovementDirection::Forward
|
||||
},
|
||||
2,
|
||||
2,
|
||||
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow)+15,
|
||||
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow)+15,
|
||||
true
|
||||
},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for (auto data : testData)
|
||||
{
|
||||
Log::Comment(std::get<0>(data).c_str());
|
||||
std::tuple<Endpoint, Endpoint, bool> result;
|
||||
int amountMoved;
|
||||
result = UiaTextRange::_moveEndpointByUnitWord(_pUiaData,
|
||||
std::get<2>(data),
|
||||
std::get<4>(data),
|
||||
std::get<1>(data),
|
||||
L" ",
|
||||
&amountMoved);
|
||||
|
||||
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
|
||||
VERIFY_ARE_EQUAL(std::get<5>(data), std::get<0>(result));
|
||||
VERIFY_ARE_EQUAL(std::get<6>(data), std::get<1>(result));
|
||||
VERIFY_ARE_EQUAL(std::get<7>(data), std::get<2>(result));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CanMoveEndpointByUnitLine)
|
||||
{
|
||||
const Column firstColumnIndex = 0;
|
||||
|
|
|
@ -32,12 +32,15 @@ SAFEARRAY* BuildIntSafeArray(std::basic_string_view<int> data)
|
|||
}
|
||||
|
||||
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
|
||||
HRESULT ScreenInfoUiaProviderBase::RuntimeClassInitialize(_In_ IUiaData* pData) noexcept
|
||||
HRESULT ScreenInfoUiaProviderBase::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ std::wstring_view wordDelimiters) noexcept
|
||||
try
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pData);
|
||||
_pData = pData;
|
||||
_wordDelimiters = wordDelimiters;
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
[[nodiscard]] HRESULT ScreenInfoUiaProviderBase::Signal(_In_ EVENTID id)
|
||||
{
|
||||
|
@ -272,7 +275,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_
|
|||
}
|
||||
|
||||
WRL::ComPtr<UiaTextRangeBase> range;
|
||||
hr = CreateTextRange(this, cursor, &range);
|
||||
hr = CreateTextRange(this, cursor, _wordDelimiters, &range);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SafeArrayDestroy(*ppRetVal);
|
||||
|
@ -293,7 +296,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_
|
|||
{
|
||||
// get the selection ranges
|
||||
std::deque<WRL::ComPtr<UiaTextRangeBase>> ranges;
|
||||
RETURN_IF_FAILED(GetSelectionRanges(this, ranges));
|
||||
RETURN_IF_FAILED(GetSelectionRanges(this, _wordDelimiters, ranges));
|
||||
|
||||
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
|
||||
//apiMsg.AreaSelected = true;
|
||||
|
@ -363,6 +366,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben
|
|||
start,
|
||||
end,
|
||||
false,
|
||||
_wordDelimiters,
|
||||
&range);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
|
@ -393,7 +397,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromChild(_In_ IRawElementProvide
|
|||
*ppRetVal = nullptr;
|
||||
|
||||
WRL::ComPtr<UiaTextRangeBase> utr;
|
||||
RETURN_IF_FAILED(CreateTextRange(this, &utr));
|
||||
RETURN_IF_FAILED(CreateTextRange(this, _wordDelimiters, &utr));
|
||||
RETURN_IF_FAILED(utr.CopyTo(ppRetVal));
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -410,6 +414,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromPoint(_In_ UiaPoint point,
|
|||
WRL::ComPtr<UiaTextRangeBase> utr;
|
||||
RETURN_IF_FAILED(CreateTextRange(this,
|
||||
point,
|
||||
_wordDelimiters,
|
||||
&utr));
|
||||
RETURN_IF_FAILED(utr.CopyTo(ppRetVal));
|
||||
return S_OK;
|
||||
|
@ -424,7 +429,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_DocumentRange(_COM_Outptr_result_m
|
|||
*ppRetVal = nullptr;
|
||||
|
||||
WRL::ComPtr<UiaTextRangeBase> utr;
|
||||
RETURN_IF_FAILED(CreateTextRange(this, &utr));
|
||||
RETURN_IF_FAILED(CreateTextRange(this, _wordDelimiters, &utr));
|
||||
RETURN_IF_FAILED(utr->ExpandToEnclosingUnit(TextUnit::TextUnit_Document));
|
||||
RETURN_IF_FAILED(utr.CopyTo(ppRetVal));
|
||||
return S_OK;
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Microsoft::Console::Types
|
|||
public WRL::RuntimeClass<WRL::RuntimeClassFlags<WRL::ClassicCom | WRL::InhibitFtmBase>, IRawElementProviderSimple, IRawElementProviderFragment, ITextProvider>
|
||||
{
|
||||
public:
|
||||
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData) noexcept;
|
||||
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData, _In_ std::wstring_view wordDelimiters = UiaTextRangeBase::DefaultWordDelimiter) noexcept;
|
||||
|
||||
ScreenInfoUiaProviderBase(const ScreenInfoUiaProviderBase&) = default;
|
||||
ScreenInfoUiaProviderBase(ScreenInfoUiaProviderBase&&) = default;
|
||||
|
@ -77,14 +77,15 @@ namespace Microsoft::Console::Types
|
|||
protected:
|
||||
ScreenInfoUiaProviderBase() = default;
|
||||
|
||||
virtual HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, _Out_ std::deque<WRL::ComPtr<UiaTextRangeBase>>& selectionRanges) = 0;
|
||||
virtual HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<WRL::ComPtr<UiaTextRangeBase>>& selectionRanges) = 0;
|
||||
|
||||
// degenerate range
|
||||
virtual HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) = 0;
|
||||
virtual HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) = 0;
|
||||
|
||||
// degenerate range at cursor position
|
||||
virtual HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) = 0;
|
||||
|
||||
// specific endpoint range
|
||||
|
@ -92,16 +93,20 @@ namespace Microsoft::Console::Types
|
|||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) = 0;
|
||||
|
||||
// range from a UiaPoint
|
||||
virtual HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
||||
const UiaPoint point,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr) = 0;
|
||||
|
||||
// weak reference to IUiaData
|
||||
IUiaData* _pData;
|
||||
|
||||
std::wstring _wordDelimiters;
|
||||
|
||||
private:
|
||||
// this is used to prevent the object from
|
||||
// signaling an event while it is already in the
|
||||
|
|
|
@ -103,7 +103,8 @@ void UiaTextRangeBase::_outputObjectState()
|
|||
|
||||
// degenerate range constructor.
|
||||
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
|
||||
HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) noexcept
|
||||
HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ std::wstring_view wordDelimiters) noexcept
|
||||
try
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pProvider);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pData);
|
||||
|
@ -113,6 +114,7 @@ HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRaw
|
|||
_end = 0;
|
||||
_degenerate = true;
|
||||
_pData = pData;
|
||||
_wordDelimiters = wordDelimiters;
|
||||
|
||||
_id = id;
|
||||
++id;
|
||||
|
@ -125,13 +127,15 @@ HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRaw
|
|||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
|
||||
HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor) noexcept
|
||||
const Cursor& cursor,
|
||||
_In_ std::wstring_view wordDelimiters) noexcept
|
||||
{
|
||||
RETURN_IF_FAILED(RuntimeClassInitialize(pData, pProvider));
|
||||
RETURN_IF_FAILED(RuntimeClassInitialize(pData, pProvider, wordDelimiters));
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -156,9 +160,10 @@ HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData,
|
|||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate) noexcept
|
||||
const bool degenerate,
|
||||
_In_ std::wstring_view wordDelimiters) noexcept
|
||||
{
|
||||
RETURN_IF_FAILED(RuntimeClassInitialize(pData, pProvider));
|
||||
RETURN_IF_FAILED(RuntimeClassInitialize(pData, pProvider, wordDelimiters));
|
||||
RETURN_HR_IF(E_INVALIDARG, !degenerate && start > end);
|
||||
|
||||
_degenerate = degenerate;
|
||||
|
@ -341,6 +346,14 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit)
|
|||
{
|
||||
_end = _start;
|
||||
}
|
||||
else if (unit <= TextUnit::TextUnit_Word)
|
||||
{
|
||||
// expand to word
|
||||
const auto target = _start;
|
||||
_start = _wordBeginEndpoint(_pData, target, _wordDelimiters);
|
||||
_end = _wordEndEndpoint(_pData, target, _wordDelimiters);
|
||||
FAIL_FAST_IF(!(_start <= _end));
|
||||
}
|
||||
else if (unit <= TextUnit::TextUnit_Line)
|
||||
{
|
||||
// expand to line
|
||||
|
@ -601,11 +614,15 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
|
|||
_outputRowConversions();
|
||||
#endif
|
||||
|
||||
auto moveFunc = &_moveByDocument;
|
||||
std::function<std::pair<Endpoint, Endpoint>(gsl::not_null<IUiaData*>, const int, const MoveState, gsl::not_null<int*> const)> moveFunc = &_moveByDocument;
|
||||
if (unit == TextUnit::TextUnit_Character)
|
||||
{
|
||||
moveFunc = &_moveByCharacter;
|
||||
}
|
||||
else if (unit <= TextUnit::TextUnit_Word)
|
||||
{
|
||||
moveFunc = std::bind(&_moveByWord, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, _wordDelimiters, std::placeholders::_4);
|
||||
}
|
||||
else if (unit <= TextUnit::TextUnit_Line)
|
||||
{
|
||||
moveFunc = &_moveByLine;
|
||||
|
@ -677,11 +694,21 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin
|
|||
|
||||
const MovementDirection moveDirection = (count > 0) ? MovementDirection::Forward : MovementDirection::Backward;
|
||||
|
||||
auto moveFunc = &_moveEndpointByUnitDocument;
|
||||
std::function<decltype(_moveEndpointByUnitDocument)> moveFunc = &_moveEndpointByUnitDocument;
|
||||
if (unit == TextUnit::TextUnit_Character)
|
||||
{
|
||||
moveFunc = &_moveEndpointByUnitCharacter;
|
||||
}
|
||||
else if (unit <= TextUnit::TextUnit_Word)
|
||||
{
|
||||
// bind all params of this function, except put a _wordDelimiters in there
|
||||
// NOTE: using a lambda function here because...
|
||||
// - lambda is cheaper than std::bind
|
||||
// - _move* functions are static, but this particular consumer needs access to a member (may be fixed by TODO GH #1993)
|
||||
moveFunc = [=](auto&& pData, auto&& moveCount, auto&& endpoint, auto&& moveState, auto&& pAmountMoved) {
|
||||
return _moveEndpointByUnitWord(pData, moveCount, endpoint, moveState, _wordDelimiters, pAmountMoved);
|
||||
};
|
||||
}
|
||||
else if (unit <= TextUnit::TextUnit_Line)
|
||||
{
|
||||
moveFunc = &_moveEndpointByUnitLine;
|
||||
|
@ -965,7 +992,7 @@ const COORD UiaTextRangeBase::_getScreenFontSize() const
|
|||
// Routine Description:
|
||||
// - Gets the number of rows in the output text buffer.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// Return Value:
|
||||
// - The number of rows
|
||||
const unsigned int UiaTextRangeBase::_getTotalRows(gsl::not_null<IUiaData*> pData) noexcept
|
||||
|
@ -976,7 +1003,7 @@ const unsigned int UiaTextRangeBase::_getTotalRows(gsl::not_null<IUiaData*> pDat
|
|||
// Routine Description:
|
||||
// - Gets the width of the screen buffer rows
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// Return Value:
|
||||
// - The row width
|
||||
const unsigned int UiaTextRangeBase::_getRowWidth(gsl::not_null<IUiaData*> pData)
|
||||
|
@ -988,6 +1015,7 @@ const unsigned int UiaTextRangeBase::_getRowWidth(gsl::not_null<IUiaData*> pData
|
|||
// Routine Description:
|
||||
// - calculates the column refered to by the endpoint.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - endpoint - the endpoint to translate
|
||||
// Return Value:
|
||||
// - the column value
|
||||
|
@ -999,6 +1027,7 @@ const Column UiaTextRangeBase::_endpointToColumn(gsl::not_null<IUiaData*> pData,
|
|||
// Routine Description:
|
||||
// - converts an Endpoint into its equivalent text buffer row.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - endpoint - the endpoint to convert
|
||||
// Return Value:
|
||||
// - the text buffer row value
|
||||
|
@ -1012,7 +1041,7 @@ const TextBufferRow UiaTextRangeBase::_endpointToTextBufferRow(gsl::not_null<IUi
|
|||
// - counts the number of rows that are fully or partially part of the
|
||||
// range.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// Return Value:
|
||||
// - The number of rows in the range.
|
||||
const unsigned int UiaTextRangeBase::_rowCountInRange(gsl::not_null<IUiaData*> pData) const
|
||||
|
@ -1036,6 +1065,7 @@ const unsigned int UiaTextRangeBase::_rowCountInRange(gsl::not_null<IUiaData*> p
|
|||
// Routine Description:
|
||||
// - Converts a TextBufferRow to a ScreenInfoRow.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - row - the TextBufferRow to convert
|
||||
// Return Value:
|
||||
// - the equivalent ScreenInfoRow.
|
||||
|
@ -1050,6 +1080,7 @@ const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(gsl::not_nul
|
|||
// - Converts a ScreenInfoRow to a ViewportRow. Uses the default
|
||||
// viewport for the conversion.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - row - the ScreenInfoRow to convert
|
||||
// Return Value:
|
||||
// - the equivalent ViewportRow.
|
||||
|
@ -1064,7 +1095,8 @@ const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(gsl::not_null<IU
|
|||
// buffer. The output buffer stores the text in a circular buffer so
|
||||
// this method makes sure that we circle around gracefully.
|
||||
// Arguments:
|
||||
// - the non-normalized row index
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - row - the non-normalized row index
|
||||
// Return Value:
|
||||
// - the normalized row index
|
||||
const Row UiaTextRangeBase::_normalizeRow(gsl::not_null<IUiaData*> pData, const Row row) noexcept
|
||||
|
@ -1106,6 +1138,7 @@ const unsigned int UiaTextRangeBase::_getViewportWidth(const SMALL_RECT viewport
|
|||
// - checks if the row is currently visible in the viewport. Uses the
|
||||
// default viewport.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - row - the screen info row to check
|
||||
// Return Value:
|
||||
// - true if the row is within the bounds of the viewport
|
||||
|
@ -1133,6 +1166,7 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row,
|
|||
// Routine Description:
|
||||
// - Converts a ScreenInfoRow to a TextBufferRow.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - row - the ScreenInfoRow to convert
|
||||
// Return Value:
|
||||
// - the equivalent TextBufferRow.
|
||||
|
@ -1146,6 +1180,7 @@ const TextBufferRow UiaTextRangeBase::_screenInfoRowToTextBufferRow(gsl::not_nul
|
|||
// Routine Description:
|
||||
// - Converts a TextBufferRow to an Endpoint.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - row - the TextBufferRow to convert
|
||||
// Return Value:
|
||||
// - the equivalent Endpoint, starting at the beginning of the TextBufferRow.
|
||||
|
@ -1154,9 +1189,42 @@ const Endpoint UiaTextRangeBase::_textBufferRowToEndpoint(gsl::not_null<IUiaData
|
|||
return _getRowWidth(pData) * row;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Finds the beginning of the word (Endpoint) for the current target (Endpoint).
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - target - the Endpoint that is on the word we want to expand to
|
||||
// - wordDelimiters - characters to determine the separation between words
|
||||
// Return Value:
|
||||
// - the equivalent Endpoint, starting at the beginning of the word where _start is located.
|
||||
const Endpoint UiaTextRangeBase::_wordBeginEndpoint(gsl::not_null<IUiaData*> pData, Endpoint target, const std::wstring_view wordDelimiters)
|
||||
{
|
||||
auto coord = _endpointToCoord(pData, target);
|
||||
coord = pData->GetTextBuffer().GetWordStart(coord, wordDelimiters);
|
||||
return _coordToEndpoint(pData, coord);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Finds the end of the word (Endpoint) for the current target (Endpoint).
|
||||
// - The "end of the word" is defined to include the following:
|
||||
// any word break characters that are present at the end of the "word", but before the start of the next word.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - target - the Endpoint that is on the word we want to expand to
|
||||
// - wordDelimiters - characters to determine the separation between words
|
||||
// Return Value:
|
||||
// - the equivalent Endpoint, starting at the end of the word where _start is located.
|
||||
const Endpoint UiaTextRangeBase::_wordEndEndpoint(gsl::not_null<IUiaData*> pData, Endpoint target, const std::wstring_view wordDelimiters)
|
||||
{
|
||||
auto coord = _endpointToCoord(pData, target);
|
||||
coord = pData->GetTextBuffer().GetWordEnd(coord, wordDelimiters, true);
|
||||
return _coordToEndpoint(pData, coord);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Converts a ScreenInfoRow to an Endpoint.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - row - the ScreenInfoRow to convert
|
||||
// Return Value:
|
||||
// - the equivalent Endpoint.
|
||||
|
@ -1169,6 +1237,7 @@ const Endpoint UiaTextRangeBase::_screenInfoRowToEndpoint(gsl::not_null<IUiaData
|
|||
// Routine Description:
|
||||
// - Converts an Endpoint to an ScreenInfoRow.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - endpoint - the endpoint to convert
|
||||
// Return Value:
|
||||
// - the equivalent ScreenInfoRow.
|
||||
|
@ -1181,6 +1250,7 @@ const ScreenInfoRow UiaTextRangeBase::_endpointToScreenInfoRow(gsl::not_null<IUi
|
|||
// Routine Description:
|
||||
// - adds the relevant coordinate points from screenInfoRow to coords.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - screenInfoRow - row to calculate coordinate positions from
|
||||
// - coords - vector to add the calucated coords to
|
||||
// Return Value:
|
||||
|
@ -1252,7 +1322,7 @@ const unsigned int UiaTextRangeBase::_getFirstScreenInfoRowIndex() noexcept
|
|||
// Routine Description:
|
||||
// - returns the index of the last row of the screen info
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// Return Value:
|
||||
// - the index of the last row (0-indexed) of the screen info
|
||||
const unsigned int UiaTextRangeBase::_getLastScreenInfoRowIndex(gsl::not_null<IUiaData*> pData) noexcept
|
||||
|
@ -1274,7 +1344,7 @@ const Column UiaTextRangeBase::_getFirstColumnIndex() noexcept
|
|||
// Routine Description:
|
||||
// - returns the index of the last column of the screen info rows
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// Return Value:
|
||||
// - the index of the last column (0-indexed) of the screen info rows
|
||||
const Column UiaTextRangeBase::_getLastColumnIndex(gsl::not_null<IUiaData*> pData)
|
||||
|
@ -1285,6 +1355,7 @@ const Column UiaTextRangeBase::_getLastColumnIndex(gsl::not_null<IUiaData*> pDat
|
|||
// Routine Description:
|
||||
// - Compares two sets of screen info coordinates
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - rowA - the row index of the first position
|
||||
// - colA - the column index of the first position
|
||||
// - rowB - the row index of the second position
|
||||
|
@ -1336,6 +1407,7 @@ const int UiaTextRangeBase::_compareScreenCoords(gsl::not_null<IUiaData*> pData,
|
|||
// - calculates new Endpoints if they were to be moved moveCount times
|
||||
// by character.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
// move operation
|
||||
|
@ -1363,23 +1435,23 @@ std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByCharacterForward(gsl::not
|
|||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
*pAmountMoved = 0;
|
||||
const int count = moveCount;
|
||||
ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow;
|
||||
Column currentColumn = moveState.StartColumn;
|
||||
|
||||
for (int i = 0; i < abs(count); ++i)
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// get the current row's right
|
||||
const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow);
|
||||
const size_t right = row.GetCharRow().MeasureRight();
|
||||
const auto expectedColumn = gsl::narrow_cast<size_t>(currentColumn) + 1;
|
||||
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
gsl::narrow_cast<size_t>(currentColumn) + 1 >= right)
|
||||
expectedColumn >= right)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (gsl::narrow_cast<size_t>(currentColumn) + 1 >= right)
|
||||
else if (expectedColumn >= right)
|
||||
{
|
||||
// we're at the edge of a row and need to go to the next one
|
||||
currentColumn = moveState.FirstColumnInRow;
|
||||
|
@ -1409,11 +1481,10 @@ std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByCharacterBackward(gsl::no
|
|||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
*pAmountMoved = 0;
|
||||
const int count = moveCount;
|
||||
ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow;
|
||||
Column currentColumn = moveState.StartColumn;
|
||||
|
||||
for (int i = 0; i < abs(count); ++i)
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
|
@ -1450,10 +1521,208 @@ std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByCharacterBackward(gsl::no
|
|||
return std::make_pair<Endpoint, Endpoint>(std::move(start), std::move(end));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - calculates new Endpoints if they were to be moved moveCount times
|
||||
// by word.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
// move operation
|
||||
// - wordDelimiters - the list of characters that define a separation of different words
|
||||
// - pAmountMoved - the number of times that the return values are "moved"
|
||||
// Return Value:
|
||||
// - a pair of endpoints of the form <start, end>
|
||||
std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByWord(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
if (moveState.Direction == MovementDirection::Forward)
|
||||
{
|
||||
return _moveByWordForward(pData, moveCount, moveState, wordDelimiters, pAmountMoved);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _moveByWordBackward(pData, moveCount, moveState, wordDelimiters, pAmountMoved);
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - calculates new Endpoints if they were to be moved moveCount times in the forward direction
|
||||
// by word.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
// move operation
|
||||
// - wordDelimiters - the list of characters that define a separation of different words
|
||||
// - pAmountMoved - the number of times that the return values are "moved"
|
||||
// Return Value:
|
||||
// - a pair of endpoints of the form <start, end>
|
||||
std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByWordForward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
// STRATEGY:
|
||||
// - move the "end" Endpoint to the proper place (if need to move multiple times, do it here)
|
||||
// - find the "start" Endpoint based on that
|
||||
|
||||
*pAmountMoved = 0;
|
||||
ScreenInfoRow currentScreenInfoRow = moveState.EndScreenInfoRow;
|
||||
Column currentColumn = moveState.EndColumn;
|
||||
|
||||
auto& buffer = pData->GetTextBuffer();
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// get the current row's right
|
||||
const ROW& row = buffer.GetRowByOffset(currentScreenInfoRow);
|
||||
const size_t right = row.GetCharRow().MeasureRight();
|
||||
const auto expectedColumn = gsl::narrow_cast<size_t>(currentColumn) + 1;
|
||||
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
expectedColumn >= right)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (expectedColumn >= right)
|
||||
{
|
||||
// we're at the edge of a row and need to go to the next one
|
||||
currentColumn = moveState.FirstColumnInRow;
|
||||
currentScreenInfoRow += static_cast<int>(moveState.Increment);
|
||||
|
||||
const auto point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
|
||||
target = buffer.GetWordEnd(target, wordDelimiters, true);
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
// moving somewhere away from the edges of a row
|
||||
const Endpoint point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
buffer.GetSize().IncrementInBounds(target);
|
||||
|
||||
target = (moveState.Increment == MovementIncrement::Forward) ?
|
||||
buffer.GetWordEnd(target, wordDelimiters, true) :
|
||||
buffer.GetWordStart(target, wordDelimiters, true);
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
*pAmountMoved += static_cast<int>(moveState.Increment);
|
||||
|
||||
FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex()));
|
||||
FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData)));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex()));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData)));
|
||||
}
|
||||
|
||||
Endpoint end = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
|
||||
auto target = _endpointToCoord(pData, end);
|
||||
target = buffer.GetWordStart(target, wordDelimiters, true);
|
||||
Endpoint start = _coordToEndpoint(pData, target);
|
||||
|
||||
return std::make_pair<Endpoint, Endpoint>(std::move(start), std::move(end));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - calculates new Endpoints if they were to be moved moveCount times in the backwards direction
|
||||
// by word.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
// move operation
|
||||
// - wordDelimiters - the list of characters that define a separation of different words
|
||||
// - pAmountMoved - the number of times that the return values are "moved"
|
||||
// Return Value:
|
||||
// - a pair of endpoints of the form <start, end>
|
||||
std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByWordBackward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
// STRATEGY:
|
||||
// - move the "start" Endpoint to the proper place (if need to move multiple times, do it here)
|
||||
// - find the "end" Endpoint based on that
|
||||
|
||||
*pAmountMoved = 0;
|
||||
ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow;
|
||||
Column currentColumn = moveState.StartColumn;
|
||||
|
||||
auto& buffer = pData->GetTextBuffer();
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
currentColumn == moveState.LastColumnInRow)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (currentColumn == moveState.LastColumnInRow)
|
||||
{
|
||||
// we're at the edge of a row and need to go to the
|
||||
// previous one. move to the cell with the last non-whitespace charactor
|
||||
|
||||
currentScreenInfoRow += static_cast<int>(moveState.Increment);
|
||||
|
||||
// get the right-most char for the previous row
|
||||
const ROW& row = buffer.GetRowByOffset(currentScreenInfoRow);
|
||||
const size_t right = row.GetCharRow().MeasureRight();
|
||||
currentColumn = gsl::narrow<Column>((right == 0) ? 0 : right - 1);
|
||||
|
||||
// get the right-most word for the previous row
|
||||
const auto point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
|
||||
target = buffer.GetWordStart(target, wordDelimiters, true);
|
||||
buffer.GetSize().IncrementInBounds(target);
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
// moving somewhere away from the edges of a row
|
||||
const Endpoint point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
buffer.GetSize().DecrementInBounds(target);
|
||||
|
||||
target = (moveState.Increment == MovementIncrement::Forward) ?
|
||||
buffer.GetWordEnd(target, wordDelimiters, true) :
|
||||
buffer.GetWordStart(target, wordDelimiters, true);
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
*pAmountMoved += static_cast<int>(moveState.Increment);
|
||||
|
||||
FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex()));
|
||||
FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData)));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex()));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData)));
|
||||
}
|
||||
|
||||
Endpoint start = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
|
||||
auto target = _endpointToCoord(pData, start);
|
||||
target = buffer.GetWordEnd(target, wordDelimiters, true);
|
||||
Endpoint end = _coordToEndpoint(pData, target);
|
||||
|
||||
return std::make_pair<Endpoint, Endpoint>(std::move(start), std::move(end));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - calculates new Endpoints if they were to be moved moveCount times
|
||||
// by line.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
// move operation
|
||||
|
@ -1501,6 +1770,7 @@ std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByLine(gsl::not_null<IUiaDa
|
|||
// - calculates new Endpoints if they were to be moved moveCount times
|
||||
// by document.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
// move operation
|
||||
|
@ -1527,6 +1797,7 @@ std::pair<Endpoint, Endpoint> UiaTextRangeBase::_moveByDocument(gsl::not_null<IU
|
|||
// - calculates new Endpoints/degenerate state if the indicated
|
||||
// endpoint was moved moveCount times by character.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - endpoint - the endpoint to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
|
@ -1558,7 +1829,6 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(gsl::not_null<IUiaData*> p
|
|||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
*pAmountMoved = 0;
|
||||
const int count = moveCount;
|
||||
ScreenInfoRow currentScreenInfoRow = 0;
|
||||
Column currentColumn = 0;
|
||||
|
||||
|
@ -1574,19 +1844,20 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(gsl::not_null<IUiaData*> p
|
|||
currentColumn = moveState.EndColumn;
|
||||
}
|
||||
|
||||
for (int i = 0; i < abs(count); ++i)
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// get the current row's right
|
||||
const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow);
|
||||
const size_t right = row.GetCharRow().MeasureRight();
|
||||
const auto expectedColumn = gsl::narrow_cast<size_t>(currentColumn) + 1;
|
||||
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
gsl::narrow_cast<size_t>(currentColumn) + 1 >= right)
|
||||
expectedColumn >= right)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (gsl::narrow_cast<size_t>(currentColumn) + 1 >= right)
|
||||
else if (expectedColumn >= right)
|
||||
{
|
||||
// we're at the edge of a row and need to go to the next one
|
||||
currentColumn = moveState.FirstColumnInRow;
|
||||
|
@ -1647,7 +1918,6 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(gsl::not_null<IUiaData*>
|
|||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
*pAmountMoved = 0;
|
||||
const int count = moveCount;
|
||||
ScreenInfoRow currentScreenInfoRow = 0;
|
||||
Column currentColumn = 0;
|
||||
|
||||
|
@ -1663,7 +1933,7 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(gsl::not_null<IUiaData*>
|
|||
currentColumn = moveState.EndColumn;
|
||||
}
|
||||
|
||||
for (int i = 0; i < abs(count); ++i)
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
|
@ -1729,10 +1999,282 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(gsl::not_null<IUiaData*>
|
|||
return std::make_tuple(start, end, degenerate);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - calculates new Endpoints/degenerate state if the indicated
|
||||
// endpoint was moved moveCount times by word.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - endpoint - the endpoint to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
// move operation
|
||||
// - pAmountMoved - the number of times that the return values are "moved"
|
||||
// Return Value:
|
||||
// - A tuple of elements of the form <start, end, degenerate>
|
||||
std::tuple<Endpoint, Endpoint, bool> UiaTextRangeBase::_moveEndpointByUnitWord(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const TextPatternRangeEndpoint endpoint,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
if (moveState.Direction == MovementDirection::Forward)
|
||||
{
|
||||
return _moveEndpointByUnitWordForward(pData, moveCount, endpoint, moveState, wordDelimiters, pAmountMoved);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _moveEndpointByUnitWordBackward(pData, moveCount, endpoint, moveState, wordDelimiters, pAmountMoved);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<Endpoint, Endpoint, bool>
|
||||
UiaTextRangeBase::_moveEndpointByUnitWordForward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const TextPatternRangeEndpoint endpoint,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
*pAmountMoved = 0;
|
||||
ScreenInfoRow currentScreenInfoRow = 0;
|
||||
Column currentColumn = 0;
|
||||
|
||||
// set current location vars
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start)
|
||||
{
|
||||
currentScreenInfoRow = moveState.StartScreenInfoRow;
|
||||
currentColumn = moveState.StartColumn;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentScreenInfoRow = moveState.EndScreenInfoRow;
|
||||
currentColumn = moveState.EndColumn;
|
||||
}
|
||||
|
||||
auto& buffer = pData->GetTextBuffer();
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// get the current row's right
|
||||
const ROW& row = buffer.GetRowByOffset(currentScreenInfoRow);
|
||||
const size_t right = row.GetCharRow().MeasureRight();
|
||||
const auto expectedColumn = gsl::narrow_cast<size_t>(currentColumn) + 1;
|
||||
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
expectedColumn >= right)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (expectedColumn >= right)
|
||||
{
|
||||
// we're at the edge of a row and need to go to the next one
|
||||
currentColumn = moveState.FirstColumnInRow;
|
||||
currentScreenInfoRow += static_cast<int>(moveState.Increment);
|
||||
|
||||
// when moving end, we need to encompass the word
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_End)
|
||||
{
|
||||
const auto point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
|
||||
target = buffer.GetWordEnd(target, wordDelimiters, true);
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// moving somewhere away from the edges of a row
|
||||
const Endpoint point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
buffer.GetSize().IncrementInBounds(target);
|
||||
|
||||
target = (moveState.Increment == MovementIncrement::Forward) ?
|
||||
buffer.GetWordEnd(target, wordDelimiters, true) :
|
||||
buffer.GetWordStart(target, wordDelimiters, true);
|
||||
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start)
|
||||
{
|
||||
buffer.GetSize().IncrementInBounds(target);
|
||||
|
||||
if (static_cast<Column>(target.X) == moveState.FirstColumnInRow && currentScreenInfoRow != moveState.LimitingRow)
|
||||
{
|
||||
currentScreenInfoRow += static_cast<int>(moveState.Increment);
|
||||
}
|
||||
}
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
*pAmountMoved += static_cast<int>(moveState.Increment);
|
||||
|
||||
FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex()));
|
||||
FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData)));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex()));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData)));
|
||||
}
|
||||
|
||||
// translate the row back to an endpoint and handle any crossed endpoints
|
||||
const Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn;
|
||||
Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn;
|
||||
bool degenerate = false;
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start)
|
||||
{
|
||||
start = convertedEndpoint;
|
||||
if (_compareScreenCoords(pData,
|
||||
currentScreenInfoRow,
|
||||
currentColumn,
|
||||
moveState.EndScreenInfoRow,
|
||||
moveState.EndColumn) == 1)
|
||||
{
|
||||
end = start;
|
||||
degenerate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
end = convertedEndpoint;
|
||||
if (_compareScreenCoords(pData,
|
||||
currentScreenInfoRow,
|
||||
currentColumn,
|
||||
moveState.StartScreenInfoRow,
|
||||
moveState.StartColumn) == -1)
|
||||
{
|
||||
start = end;
|
||||
degenerate = true;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(start, end, degenerate);
|
||||
}
|
||||
|
||||
std::tuple<Endpoint, Endpoint, bool>
|
||||
UiaTextRangeBase::_moveEndpointByUnitWordBackward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const TextPatternRangeEndpoint endpoint,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved)
|
||||
{
|
||||
*pAmountMoved = 0;
|
||||
ScreenInfoRow currentScreenInfoRow = 0;
|
||||
Column currentColumn = 0;
|
||||
|
||||
// set current location vars
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start)
|
||||
{
|
||||
currentScreenInfoRow = moveState.StartScreenInfoRow;
|
||||
currentColumn = moveState.StartColumn;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentScreenInfoRow = moveState.EndScreenInfoRow;
|
||||
currentColumn = moveState.EndColumn;
|
||||
}
|
||||
|
||||
auto& buffer = pData->GetTextBuffer();
|
||||
for (int i = 0; i < abs(moveCount); ++i)
|
||||
{
|
||||
// check if we're at the edge of the screen info buffer
|
||||
if (currentScreenInfoRow == moveState.LimitingRow &&
|
||||
currentColumn == moveState.LastColumnInRow)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (currentColumn == moveState.LastColumnInRow)
|
||||
{
|
||||
// we're at the edge of a row and need to go to the
|
||||
// next one. move to the cell with the last non-whitespace charactor
|
||||
|
||||
currentScreenInfoRow += static_cast<int>(moveState.Increment);
|
||||
// get the right cell for the next row
|
||||
const ROW& row = buffer.GetRowByOffset(currentScreenInfoRow);
|
||||
const size_t right = row.GetCharRow().MeasureRight();
|
||||
currentColumn = gsl::narrow<Column>((right == 0) ? 0 : right - 1);
|
||||
|
||||
// get the right-most word for the previous row
|
||||
const auto point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
|
||||
target = buffer.GetWordStart(target, wordDelimiters, true);
|
||||
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start)
|
||||
{
|
||||
buffer.GetSize().IncrementInBounds(target);
|
||||
}
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
// moving somewhere away from the edges of a row
|
||||
const Endpoint point = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
auto target = _endpointToCoord(pData, point);
|
||||
buffer.GetSize().DecrementInBounds(target);
|
||||
|
||||
target = (moveState.Increment == MovementIncrement::Forward) ?
|
||||
buffer.GetWordEnd(target, wordDelimiters, true) :
|
||||
buffer.GetWordStart(target, wordDelimiters, true);
|
||||
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_End)
|
||||
{
|
||||
buffer.GetSize().DecrementInBounds(target);
|
||||
|
||||
if (static_cast<Column>(target.X) == moveState.FirstColumnInRow && currentScreenInfoRow != moveState.LimitingRow)
|
||||
{
|
||||
currentScreenInfoRow += static_cast<int>(moveState.Increment);
|
||||
}
|
||||
}
|
||||
|
||||
currentColumn = target.X;
|
||||
}
|
||||
*pAmountMoved += static_cast<int>(moveState.Increment);
|
||||
|
||||
FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex()));
|
||||
FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData)));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex()));
|
||||
FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData)));
|
||||
}
|
||||
|
||||
// translate the row back to an endpoint and handle any crossed endpoints
|
||||
const Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn;
|
||||
Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn;
|
||||
Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn;
|
||||
bool degenerate = false;
|
||||
if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start)
|
||||
{
|
||||
start = convertedEndpoint;
|
||||
if (_compareScreenCoords(pData,
|
||||
currentScreenInfoRow,
|
||||
currentColumn,
|
||||
moveState.EndScreenInfoRow,
|
||||
moveState.EndColumn) == 1)
|
||||
{
|
||||
end = start;
|
||||
degenerate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
end = convertedEndpoint;
|
||||
if (_compareScreenCoords(pData,
|
||||
currentScreenInfoRow,
|
||||
currentColumn,
|
||||
moveState.StartScreenInfoRow,
|
||||
moveState.StartColumn) == -1)
|
||||
{
|
||||
start = end;
|
||||
degenerate = true;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(start, end, degenerate);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - calculates new Endpoints/degenerate state if the indicated
|
||||
// endpoint was moved moveCount times by line.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - endpoint - the endpoint to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
|
@ -1892,6 +2434,7 @@ std::tuple<Endpoint, Endpoint, bool> UiaTextRangeBase::_moveEndpointByUnitLine(g
|
|||
// - calculates new Endpoints/degenerate state if the indicate
|
||||
// endpoint was moved moveCount times by document.
|
||||
// Arguments:
|
||||
// - pData - the UiaData for the terminal we are operating on
|
||||
// - moveCount - the number of times to move
|
||||
// - endpoint - the endpoint to move
|
||||
// - moveState - values indicating the state of the console for the
|
||||
|
|
|
@ -23,6 +23,7 @@ Author(s):
|
|||
#include "inc/viewport.hpp"
|
||||
#include "../buffer/out/textBuffer.hpp"
|
||||
#include "IUiaData.h"
|
||||
#include "unicode.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <tuple>
|
||||
|
@ -138,21 +139,27 @@ namespace Microsoft::Console::Types
|
|||
};
|
||||
|
||||
public:
|
||||
// The default word delimiter for UiaTextRanges
|
||||
static constexpr std::wstring_view DefaultWordDelimiter{ &UNICODE_SPACE, 1 };
|
||||
|
||||
// degenerate range
|
||||
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider) noexcept;
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
_In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept;
|
||||
|
||||
// degenerate range at cursor position
|
||||
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Cursor& cursor) noexcept;
|
||||
const Cursor& cursor,
|
||||
_In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept;
|
||||
|
||||
// specific endpoint range
|
||||
HRESULT RuntimeClassInitialize(_In_ IUiaData* pData,
|
||||
_In_ IRawElementProviderSimple* const pProvider,
|
||||
const Endpoint start,
|
||||
const Endpoint end,
|
||||
const bool degenerate) noexcept;
|
||||
const bool degenerate,
|
||||
_In_ std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept;
|
||||
|
||||
HRESULT RuntimeClassInitialize(const UiaTextRangeBase& a) noexcept;
|
||||
|
||||
|
@ -218,6 +225,8 @@ namespace Microsoft::Console::Types
|
|||
|
||||
IRawElementProviderSimple* _pProvider;
|
||||
|
||||
std::wstring _wordDelimiters;
|
||||
|
||||
virtual void _ChangeViewport(const SMALL_RECT NewWindow) = 0;
|
||||
virtual void _TranslatePointToScreen(LPPOINT clientPoint) const = 0;
|
||||
virtual void _TranslatePointFromScreen(LPPOINT screenPoint) const = 0;
|
||||
|
@ -276,6 +285,9 @@ namespace Microsoft::Console::Types
|
|||
const ScreenInfoRow row) noexcept;
|
||||
static const Endpoint _textBufferRowToEndpoint(gsl::not_null<IUiaData*> pData, const TextBufferRow row);
|
||||
|
||||
static const Endpoint _wordBeginEndpoint(gsl::not_null<IUiaData*> pData, Endpoint target, const std::wstring_view wordDelimiters);
|
||||
static const Endpoint _wordEndEndpoint(gsl::not_null<IUiaData*> pData, Endpoint target, const std::wstring_view wordDelimiters);
|
||||
|
||||
static const ScreenInfoRow _endpointToScreenInfoRow(gsl::not_null<IUiaData*> pData,
|
||||
const Endpoint endpoint);
|
||||
static const Endpoint _screenInfoRowToEndpoint(gsl::not_null<IUiaData*> pData,
|
||||
|
@ -339,6 +351,24 @@ namespace Microsoft::Console::Types
|
|||
const MoveState moveState,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::pair<Endpoint, Endpoint> _moveByWord(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::pair<Endpoint, Endpoint> _moveByWordForward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::pair<Endpoint, Endpoint> _moveByWordBackward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::pair<Endpoint, Endpoint> _moveByLine(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const MoveState moveState,
|
||||
|
@ -370,6 +400,30 @@ namespace Microsoft::Console::Types
|
|||
const MoveState moveState,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::tuple<Endpoint, Endpoint, bool>
|
||||
_moveEndpointByUnitWord(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const TextPatternRangeEndpoint endpoint,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::tuple<Endpoint, Endpoint, bool>
|
||||
_moveEndpointByUnitWordForward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const TextPatternRangeEndpoint endpoint,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::tuple<Endpoint, Endpoint, bool>
|
||||
_moveEndpointByUnitWordBackward(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
const TextPatternRangeEndpoint endpoint,
|
||||
const MoveState moveState,
|
||||
const std::wstring_view wordDelimiters,
|
||||
_Out_ gsl::not_null<int*> const pAmountMoved);
|
||||
|
||||
static std::tuple<Endpoint, Endpoint, bool>
|
||||
_moveEndpointByUnitLine(gsl::not_null<IUiaData*> pData,
|
||||
const int moveCount,
|
||||
|
|
Loading…
Reference in a new issue