2019-05-03 00:29:04 +02:00
|
|
|
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Licensed under the MIT license.
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
- textBuffer.hpp
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
- This module contains structures and functions for manipulating a text
|
|
|
|
based buffer within the console host window.
|
|
|
|
|
|
|
|
Author(s):
|
|
|
|
- Michael Niksa (miniksa) 10-Apr-2014
|
|
|
|
- Paul Campbell (paulcam) 10-Apr-2014
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
- From components of output.h/.c
|
|
|
|
by Therese Stowell (ThereseS) 1990-1991
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
ScreenBuffer data structure overview:
|
|
|
|
|
|
|
|
each screen buffer has an array of ROW structures. each ROW structure
|
|
|
|
contains the data for one row of text. the data stored for one row of
|
|
|
|
text is a character array and an attribute array. the character array
|
|
|
|
is allocated the full length of the row from the heap, regardless of the
|
|
|
|
non-space length. we also maintain the non-space length. the character
|
|
|
|
array is initialized to spaces. the attribute
|
|
|
|
array is run length encoded (i.e 5 BLUE, 3 RED). if there is only one
|
|
|
|
attribute for the whole row (the normal case), it is stored in the ATTR_ROW
|
|
|
|
structure. otherwise the attr string is allocated from the heap.
|
|
|
|
|
|
|
|
ROW - CHAR_ROW - CHAR string
|
|
|
|
\ \ length of char string
|
|
|
|
\
|
|
|
|
ATTR_ROW - ATTR_PAIR string
|
|
|
|
\ length of attr pair string
|
|
|
|
ROW
|
|
|
|
ROW
|
|
|
|
ROW
|
|
|
|
|
|
|
|
ScreenInfo->Rows points to the ROW array. ScreenInfo->Rows[0] is not
|
|
|
|
necessarily the top row. ScreenInfo->BufferInfo.TextInfo->FirstRow contains the index of
|
|
|
|
the top row. That means scrolling (if scrolling entire screen)
|
|
|
|
merely involves changing the FirstRow index,
|
|
|
|
filling in the last row, and updating the screen.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "cursor.h"
|
|
|
|
#include "Row.hpp"
|
|
|
|
#include "TextAttribute.hpp"
|
|
|
|
#include "UnicodeStorage.hpp"
|
|
|
|
#include "../types/inc/Viewport.hpp"
|
|
|
|
|
|
|
|
#include "../buffer/out/textBufferCellIterator.hpp"
|
|
|
|
#include "../buffer/out/textBufferTextIterator.hpp"
|
|
|
|
|
|
|
|
#include "../renderer/inc/IRenderTarget.hpp"
|
|
|
|
|
|
|
|
class TextBuffer final
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TextBuffer(const COORD screenBufferSize,
|
|
|
|
const TextAttribute defaultAttributes,
|
|
|
|
const UINT cursorSize,
|
|
|
|
Microsoft::Console::Render::IRenderTarget& renderTarget);
|
|
|
|
TextBuffer(const TextBuffer& a) = delete;
|
|
|
|
|
|
|
|
// Used for duplicating properties to another text buffer
|
2019-08-30 00:23:07 +02:00
|
|
|
void CopyProperties(const TextBuffer& OtherBuffer) noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
// row manipulation
|
|
|
|
const ROW& GetRowByOffset(const size_t index) const;
|
|
|
|
ROW& GetRowByOffset(const size_t index);
|
|
|
|
|
|
|
|
TextBufferCellIterator GetCellDataAt(const COORD at) const;
|
|
|
|
TextBufferCellIterator GetCellLineDataAt(const COORD at) const;
|
|
|
|
TextBufferCellIterator GetCellDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
|
|
|
|
TextBufferTextIterator GetTextDataAt(const COORD at) const;
|
|
|
|
TextBufferTextIterator GetTextLineDataAt(const COORD at) const;
|
|
|
|
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
|
|
|
|
|
|
|
|
// Text insertion functions
|
|
|
|
OutputCellIterator Write(const OutputCellIterator givenIt);
|
|
|
|
|
|
|
|
OutputCellIterator Write(const OutputCellIterator givenIt,
|
make filling chars (and, thus, erase line/char) unset wrap (#2831)
EraseInLine calls `FillConsoleOutputCharacterW()`. In filling the row with
chars, we were setting the wrap flag. We need to specifically not do this on
ANY _FILL_ operation. Now a fill operation UNSETS the wrap flag if we fill to
the end of the line.
Originally, we had a boolean `setWrap` that would mean...
- **true**: if writing to the end of the row, SET the wrap value to true
- **false**: if writing to the end of the row, DON'T CHANGE the wrap value
Now we're making this bool a std::optional to allow for a ternary state. This
allows for us to handle the following cases completely. Refer to the table
below:
,- current wrap value
| ,- are we filling the last cell in the row?
| | ,- new wrap value
| | | ,- comments
|-- |-- |-- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 1 | 1 | THIS CASE WAS HANDLED CORRECTLY
| 1 | 0 | 0 | THIS CASE WAS UNHANDLED
| 1 | 0 | 1 |
| 1 | 1 | 1 |
To handle that special case (1-0-0), we need to UNSET the wrap. So now, we have
~setWrap~ `wrap` mean the following:
- **true**: if writing to the end of the row, SET the wrap value to TRUE
- **false**: if writing to the end of the row, SET the wrap value to FALSE
- **nullopt**: leave the wrap value as it is
Closes #1126
2019-10-01 03:16:31 +02:00
|
|
|
const COORD target,
|
|
|
|
const std::optional<bool> wrap = true);
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
OutputCellIterator WriteLine(const OutputCellIterator givenIt,
|
|
|
|
const COORD target,
|
make filling chars (and, thus, erase line/char) unset wrap (#2831)
EraseInLine calls `FillConsoleOutputCharacterW()`. In filling the row with
chars, we were setting the wrap flag. We need to specifically not do this on
ANY _FILL_ operation. Now a fill operation UNSETS the wrap flag if we fill to
the end of the line.
Originally, we had a boolean `setWrap` that would mean...
- **true**: if writing to the end of the row, SET the wrap value to true
- **false**: if writing to the end of the row, DON'T CHANGE the wrap value
Now we're making this bool a std::optional to allow for a ternary state. This
allows for us to handle the following cases completely. Refer to the table
below:
,- current wrap value
| ,- are we filling the last cell in the row?
| | ,- new wrap value
| | | ,- comments
|-- |-- |-- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 1 | 1 | THIS CASE WAS HANDLED CORRECTLY
| 1 | 0 | 0 | THIS CASE WAS UNHANDLED
| 1 | 0 | 1 |
| 1 | 1 | 1 |
To handle that special case (1-0-0), we need to UNSET the wrap. So now, we have
~setWrap~ `wrap` mean the following:
- **true**: if writing to the end of the row, SET the wrap value to TRUE
- **false**: if writing to the end of the row, SET the wrap value to FALSE
- **nullopt**: leave the wrap value as it is
Closes #1126
2019-10-01 03:16:31 +02:00
|
|
|
const std::optional<bool> setWrap = std::nullopt,
|
2019-05-03 00:29:04 +02:00
|
|
|
const std::optional<size_t> limitRight = std::nullopt);
|
|
|
|
|
|
|
|
bool InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
|
|
|
bool InsertCharacter(const std::wstring_view chars, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
|
|
|
bool IncrementCursor();
|
|
|
|
bool NewlineCursor();
|
|
|
|
|
|
|
|
// Scroll needs access to this to quickly rotate around the buffer.
|
Correct fill attributes when scrolling and erasing (#3100)
## Summary of the Pull Request
Operations that erase areas of the screen are typically meant to do so using the current color attributes, but with the rendition attributes reset (what we refer to as meta attributes). This also includes scroll operations that have to clear the area of the screen that has scrolled into view. The only exception is the _Erase Scrollback_ operation, which needs to reset the buffer with the default attributes. This PR updates all of these cases to apply the correct attributes when scrolling and erasing.
## PR Checklist
* [x] Closes #2553
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
* [ ] Requires documentation to be updated
* [ ] I've not really discussed this with core contributors. I'm ready to accept this work might be rejected in favor of a different grand plan.
## Detailed Description of the Pull Request / Additional comments
My initial plan was to use a special case legacy attribute value to indicate the "standard erase attribute" which could safely be passed through the legacy APIs. But this wouldn't cover the cases that required default attributes to be used. And then with the changes in PR #2668 and #2987, it became clear that our requirements could be better achieved with a couple of new private APIs that wouldn't have to depend on legacy attribute hacks at all.
To that end, I've added the `PrivateFillRegion` and `PrivateScrollRegion` APIs to the `ConGetSet` interface. These are just thin wrappers around the existing `SCREEN_INFORMATION::Write` method and the `ScrollRegion` function respectively, but with a simple boolean parameter to choose between filling with default attributes or the standard erase attributes (i.e the current colors but with meta attributes reset).
With those new APIs in place, I could then update most scroll operations to use `PrivateScrollRegion`, and most erase operations to use `PrivateFillRegion`.
The functions affected by scrolling included:
* `DoSrvPrivateReverseLineFeed` (the RI command)
* `DoSrvPrivateModifyLinesImpl` (the IL and DL commands)
* `AdaptDispatch::_InsertDeleteHelper` (the ICH and DCH commands)
* `AdaptDispatch::_ScrollMovement` (the SU and SD commands)
The functions affected by erasing included:
* `AdaptDispatch::_EraseSingleLineHelper` (the EL command, and most ED variants)
* `AdaptDispatch::EraseCharacters` (the ECH command)
While updating these erase methods, I noticed that both of them also required boundary fixes similar to those in PR #2505 (i.e. the horizontal extent of the erase operation should apply to the full width of the buffer, and not just the current viewport width), so I've addressed that at the same time.
In addition to the changes above, there were also a few special cases, the first being the line feed handling, which required updating in a number of places to use the correct erase attributes:
* `SCREEN_INFORMATION::InitializeCursorRowAttributes` - this is used to initialise the rows that pan into view when the viewport is moved down the buffer.
* `TextBuffer::IncrementCircularBuffer` - this occurs when we scroll passed the very end of the buffer, and a recycled row now needs to be reinitialised.
* `AdjustCursorPosition` - when within margin boundaries, this relies on a couple of direct calls to `ScrollRegion` which needed to be passed the correct fill attributes.
The second special case was the full screen erase sequence (`ESC 2 J`), which is handled separately from the other ED sequences. This required updating the `SCREEN_INFORMATION::VtEraseAll` method to use the standard erase attributes, and also required changes to the horizontal extent of the filled area, since it should have been clearing the full buffer width (the same issue as the other erase operations mentioned above).
Finally, there was the `AdaptDispatch::_EraseScrollback` method, which uses both scroll and fill operations, which could now be handled by the new `PrivateScrollRegion` and `PrivateFillRegion` APIs. But in this case we needed to fill with the default attributes rather than the standard erase attributes. And again this implementation needed some changes to make sure the full width of the active area was retained after the erase, similar to the horizontal boundary issues with the other erase operations.
Once all these changes were made, there were a few areas of the code that could then be simplified quite a bit. The `FillConsoleOutputCharacterW`, `FillConsoleOutputAttribute`, and `ScrollConsoleScreenBufferW` were no longer needed in the `ConGetSet` interface, so all of that code could now be removed. The `_EraseSingleLineDistanceHelper` and `_EraseAreaHelper` methods in the `AdaptDispatch` class were also no longer required and could be removed.
Then there were the hacks to handle legacy default colors in the `FillConsoleOutputAttributeImpl` and `ScrollConsoleScreenBufferWImpl` implementations. Since those hacks were only needed for VT operations, and the VT code no longer calls those methods, there was no longer a need to retain that behaviour (in fact there are probably some edge cases where that behaviour might have been considered a bug when reached via the public console APIs).
## Validation Steps Performed
For most of the scrolling operations there were already existing tests in place, and those could easily be extended to check that the meta attributes were correctly reset when filling the revealed lines of the scrolling region.
In the screen buffer tests, I made updates of that sort to the `ScrollOperations` method (handling SU, SD, IL, DL, and RI), the `InsertChars` and `DeleteChars` methods (ICH and DCH), and the `VtNewlinePastViewport` method (LF). I also added a new `VtNewlinePastEndOfBuffer` test to check the case where the line feed causes the viewport to pan past the end of the buffer.
The erase operations, however, were being covered by adapter tests, and those aren't really suited for this kind of functionality (the same sort of issue came up in PR #2505). As a result I've had to reimplement those tests as screen buffer tests.
Most of the erase operations are covered by the `EraseTests` method, except the for the scrollback erase which has a dedicated `EraseScrollbackTests` method. I've also had to replace the `HardReset` adapter test, but that was already mostly covered by the `HardResetBuffer` screen buffer test, which I've now extended slightly (it could do with some more checks, but I think that can wait for a future PR when we're fixing other RIS issues).
2019-12-11 00:14:40 +01:00
|
|
|
bool IncrementCircularBuffer(const bool inVtMode = false);
|
2019-05-03 00:29:04 +02:00
|
|
|
|
Add support for "reflow"ing the Terminal buffer (#4741)
This PR adds support for "Resize with Reflow" to the Terminal. In
conhost, `ResizeWithReflow` is the function that's responsible for
reflowing wrapped lines of text as the buffer gets resized. Now that
#4415 has merged, we can also implement this in the Terminal. Now, when
the Terminal is resized, it will reflow the lines of it's buffer in the
same way that conhost does. This means, the terminal will no longer chop
off the ends of lines as the buffer is too small to represent them.
As a happy side effect of this PR, it also fixed #3490. This was a bug
that plagued me during the investigation into this functionality. The
original #3490 PR, #4354, tried to fix this bug with some heavy conpty
changes. Turns out, that only made things worse, and far more
complicated. When I really got to thinking about it, I realized "conhost
can handle this right, why can't the Terminal?". Turns out, by adding
resize with reflow, I was also able to fix this at the same time.
Conhost does a little bit of math after reflowing to attempt to keep the
viewport in the same relative place after a reflow. By re-using that
logic in the Terminal, I was able to fix #3490.
I also included that big ole test from #3490, because everyone likes
adding 60 test cases in a PR.
## References
* #4200 - this scenario
* #405/#4415 - conpty emits wrapped lines, which was needed for this PR
* #4403 - delayed EOL wrapping via conpty, which was also needed for
this
* #4354 - we don't speak of this PR anymore
## PR Checklist
* [x] Closes #1465
* [x] Closes #3490
* [x] Closes #4771
* [x] Tests added/passed
## EDIT: Changes to this PR on 5 March 2020
I learned more since my original version of this PR. I wrote that in
January, and despite my notes that say it was totally working, it
_really_ wasn't.
Part of the hard problem, as mentioned in #3490, is that the Terminal
might request a resize to (W, H-1), and while conpty is preparing that
frame, or before the terminal has received that frame, the Terminal
resizes to (W, H-2). Now, there aren't enough lines in the terminal
buffer to catch all the lines that conpty is about to emit. When that
happens, lines get duplicated in the buffer. From a UX perspective, this
certainly looks a lot worse than a couple lost lines. It looks like
utter chaos.
So I've introduced a new mode to conpty to try and counteract this
behavior. This behavior I'm calling "quirky resize". The **TL;DR** of
quirky resize mode is that conpty won't emit the entire buffer on a
resize, and will trust that the terminal is prepared to reflow it's
buffer on it's own.
This will enable the quirky resize behavior for applications that are
prepared for it. The "quirky resize" is "don't `InvalidateAll` when the
terminal resizes". This is added as a quirk as to not regress other
terminal applications that aren't prepared for this behavior
(gnome-terminal, conhost in particular). For those kinds of terminals,
when the buffer is resized, it's just going to lose lines. That's what
currently happens for them.
When the quirk is enabled, conpty won't repaint the entire buffer. This
gets around the "duplicated lines" issue that requesting multiple
resizes in a row can cause. However, for these terminals that are
unprepared, the conpty cursor might end up in the wrong position after a
quirky resize.
The case in point is maximizing the terminal. For maximizing
(height->50) from a buffer that's 30 lines tall, with the cursor on
y=30, this is what happens:
* With the quirk disabled, conpty reprints the entire buffer. This is
60 lines that get printed. This ends up blowing away about 20 lines
of scrollback history, as the terminal app would have tried to keep
the text pinned to the bottom of the window. The term. app moved the
viewport up 20 lines, and then the 50 lines of conpty output (30
lines of text, and 20 blank lines at the bottom) overwrote the lines
from the scrollback. This is bad, but not immediately obvious, and
is **what currently happens**.
* With the quirk enabled, conpty doesn't emit any lines, but the
actual content of the window is still only in the top 30 lines.
However, the terminal app has still moved 20 lines down from the
scrollback back into the viewport. So the terminal's cursor is at
y=50 now, but conpty's is at 30. This means that the terminal and
conpty are out of sync, and there's not a good way of re-syncing
these. It's very possible (trivial in `powershell`) that the new
output will jump up to y=30 override the existing output in the
terminal buffer.
The Windows Terminal is already prepared for this quirky behavior, so it
doesn't keep the output at the bottom of the window. It shifts it's
viewport down to match what conpty things the buffer looks like.
What happens when we have passthrough mode and WT is like "I would like
quirky resize"? I guess things will just work fine, cause there won't be
a buffer behind the passthrough app that the terminal cares about. Sure,
in the passthrough case the Terminal could _not_ quirky resize, but the
quirky resize won't be wrong.
2020-03-13 01:43:37 +01:00
|
|
|
COORD GetLastNonSpaceCharacter(std::optional<const Microsoft::Console::Types::Viewport> viewOptional = std::nullopt) const;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2019-08-30 00:23:07 +02:00
|
|
|
Cursor& GetCursor() noexcept;
|
|
|
|
const Cursor& GetCursor() const noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2019-08-30 00:23:07 +02:00
|
|
|
const SHORT GetFirstRowIndex() const noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2020-07-09 13:18:25 +02:00
|
|
|
const Microsoft::Console::Types::Viewport GetSize() const noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
void ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta);
|
|
|
|
|
2019-08-30 00:23:07 +02:00
|
|
|
UINT TotalRowCount() const noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
Tweaks: normalize TextAttribute method names (adjective form) (#6951)
## Summary of the Pull Request
Text can have various attributes, such as "bold", "italic", "underlined", etc. The TextAttribute class embodies this. It has methods to set/query these attributes.
This change tweaks a few of the method names to make them match. I.e. for an imaginary text property "Foo", we should have methods along the lines of:
```
IsFoo
SetFoo(bool isFoo)
```
And variations should match: we should have "Foo" and "OverFoo", not "Fooey" and "OverFoo".
I chose to standardize on the adjective form, since that's what we are closest to already. The attributes I attacked here are:
SetItalic**s** --> SetItalic
SetUnderline --> SetUnderline**d**
SetOverline --> SetOverline**d**
("italic" is an adjective; "italics" is a plural noun, representing letters or words in an italic typeface)
And I also added methods for "DoublyUnderlined" for good measure.
I stopped short of renaming the GraphicsOptions enum values to match, too; but I'd be willing to do that in a follow-up change if people wanted it.
## Validation Steps Performed
It builds, and tests still pass.
2020-07-17 17:50:23 +02:00
|
|
|
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
void Reset();
|
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2019-08-30 00:23:07 +02:00
|
|
|
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
|
|
|
UnicodeStorage& GetUnicodeStorage() noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2019-08-30 00:23:07 +02:00
|
|
|
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
Refactor UiaTextRange For Improved Navigation and Reliability (#4018)
## Summary of the Pull Request
This pull request is intended to achieve the following goals...
1) reduce duplicate code
2) remove static functions
3) improve readability
4) improve reliability
5) improve code-coverage for testing
6) establish functioning text buffer navigation in Narrator and NVDA
This also required a change to the wrapper class `XamlUiaTextRange` that has been causing issues with Narrator and NVDA.
See below for additional context.
## References
#3976 - I believe this might have been a result of improperly handling degenerate ranges. Fixed here.
#3895 - reduced the duplicate code. No need to separate into different files
#2160 - same as #3976 above
#1993 - I think just about everything is no longer static
## PR Checklist
* [x] Closes #3895, Closes #1993, Closes #3976, Closes #2160
* [x] CLA signed
* [x] Tests added/passed
## Detailed Description of the Pull Request / Additional comments
### UiaTextRange
- converted endpoints into the COORD system in the TextBuffer coordinate space
- `start` is inclusive, `end` is exclusive. A degenerate range is when start == end.
- all functions are no longer static
- `MoveByUnit()` functions now rely on `MoveEndpointByUnit()` functions
- removed unnecessary typedefs like `Endpoint`, `ScreenInfoRow`, etc..
- relied more heavily on existing functionality from `TextBuffer` and `Viewport`
### XamlUiaTextRange
- `GetAttributeValue()` must return a special HRESULT that signifies that the requested attribute is not supported. This was the cause of a number of inconsistencies between Narrator and NVDA.
- `FindText()` should return `nullptr` if nothing was found. #4373 properly fixes this functionality now that Search is a shared module
### TextBuffer
- Word navigation functionality is entirely in `TextBuffer` for proper abstraction
- a total of 6 functions are now dedicated to word navigation to get a good understanding of the differences between a "word" in Accessibility and a "word" in selection
As an example, consider a buffer with this text in it:
" word other "
In selection, a "word" is defined as the range between two delimiters, so the words in the example include [" ", "word", " ", "other", " "].
In accessibility , a "word" includes the delimiters after a range of readable characters, so the words in the example include ["word ", "other "].
Additionally, accessibility word navigation must be able to detect if it is on the first or last word. This resulted in a slight variant of word navigation functions that return a boolean instead of a COORD.
Ideally, these functions can be consolidated, but that is too risky for a PR of this size as it can have an effect on selection.
### Viewport
- the concept of `EndExclusive` is added. This is used by UiaTextRange's `end` anchor as it is exclusive. To signify that the last character in the buffer is included in this buffer, `end` must be one past the end of the buffer. This is `EndExclusive`
- Since many functions check if the given `COORD` is in bounds, a flag must be set to allow `EndExclusive` as a valid `COORD` that is in bounds.
### Testing
- word navigation testing relies more heavily on TextBuffer tests
- additional testing was created for non-movement focused functions of UiaTextRange
- The results have been compared to Microsoft Word and some have been verified by UiAutomation/Narrator contacts as expected results.
## Validation Steps Performed
Tests pass
Narrator works
NVDA works
2020-01-31 21:59:39 +01:00
|
|
|
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
|
|
|
|
const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
|
|
|
|
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const;
|
|
|
|
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
|
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.
2019-12-13 00:22:12 +01:00
|
|
|
|
2020-03-24 00:50:17 +01:00
|
|
|
const til::point GetGlyphStart(const til::point pos) const;
|
|
|
|
const til::point GetGlyphEnd(const til::point pos) const;
|
|
|
|
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
2020-10-06 00:11:47 +02:00
|
|
|
bool MoveToPreviousGlyph(til::point& pos) const;
|
2020-03-24 00:50:17 +01:00
|
|
|
|
2020-02-28 01:42:26 +01:00
|
|
|
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
|
|
|
|
|
2020-09-03 19:52:39 +02:00
|
|
|
void AddHyperlinkToMap(std::wstring_view uri, uint16_t id);
|
|
|
|
std::wstring GetHyperlinkUriFromId(uint16_t id) const;
|
2020-10-17 00:08:59 +02:00
|
|
|
uint16_t GetHyperlinkId(std::wstring_view uri, std::wstring_view id);
|
2020-09-03 19:52:39 +02:00
|
|
|
void RemoveHyperlinkFromMap(uint16_t id);
|
|
|
|
std::wstring GetCustomIdFromId(uint16_t id) const;
|
|
|
|
void CopyHyperlinkMaps(const TextBuffer& OtherBuffer);
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
class TextAndColor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
std::vector<std::wstring> text;
|
|
|
|
std::vector<std::vector<COLORREF>> FgAttr;
|
|
|
|
std::vector<std::vector<COLORREF>> BkAttr;
|
|
|
|
};
|
|
|
|
|
2020-03-09 16:17:34 +01:00
|
|
|
const TextAndColor GetText(const bool lineSelection,
|
|
|
|
const bool trimTrailingWhitespace,
|
|
|
|
const std::vector<SMALL_RECT>& textRects,
|
Refactor the renderer color calculations (#6853)
This is a refactoring of the renderer color calculations to simplify the
implementation, and to make it easier to support additional
color-altering rendition attributes in the future (e.g. _faint_ and
_conceal_).
## References
* This is a followup to PRs #3817 and #6809, which introduced additional
complexity in the color calculations, and which suggested the need for
refactoring.
## Detailed Description of the Pull Request / Additional comments
When we added support for `DECSCNM`, that required the foreground and
background color lookup methods to be able to return the opposite of
what was requested when the reversed mode was set. That made those
methods unnecessarily complicated, and I thought we could simplify them
considerably just by combining the calculations into a single method
that derived both colors at the same time.
And since both conhost and Windows Terminal needed to perform the same
calculations, it also made sense to move that functionality into the
`TextAttribute` class, where it could easily be shared.
In general this way of doing things is a bit more efficient. However, it
does result in some unnecessary work when only one of the colors is
required, as is the case for the gridline painter. So to make that less
of an issue, I've reordered the gridline code a bit so it at least
avoids looking up the colors when no gridlines are needed.
## Validation Steps Performed
Because of the API changes, quite a lot of the unit tests had to be
updated. For example instead of verifying colors with two separate calls
to `LookupForegroundColor` and `LookupBackgroundColor`, that's now
achieved with a single `LookupAttributeColors` call, comparing against a
pair of values. The specifics of the tests haven't changed though, and
they're all still working as expected.
I've also manually confirmed that the various color sequences and
rendition attributes are rendering correctly with the new refactoring.
2020-07-11 00:26:34 +02:00
|
|
|
std::function<std::pair<COLORREF, COLORREF>(const TextAttribute&)> GetAttributeColors = nullptr) const;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2019-08-20 00:59:01 +02:00
|
|
|
static std::string GenHTML(const TextAndColor& rows,
|
|
|
|
const int fontHeightPoints,
|
Allow FontInfo{,Base,Desired} to store a font name > 32 wch (#3107)
We now truncate the font name as it goes out to GDI APIs, in console API
servicing, and in the propsheet.
I attempted to defer truncating the font to as far up the stack as
possible, so as to make FontInfo usable for the broadest set of cases.
There were a couple questions that came up: I know that `Settings` gets
memset (memsat?) by the registry deserializer, and perhaps that's
another place for us to tackle. Right now, this pull request enables
fonts whose names are >= 32 characters _in Windows Terminal only_, but
the underpinnings are there for conhost as well. We'd need to explicitly
break at the API, or perhaps return a failure or log something to
telemetry.
* Should we log truncation at the API boundary to telemetry?
-> Later; followup filed (#3123)
* Should we fix Settings here, or later?
-> Later; followup filed (#3123)
* `TrueTypeFontList` is built out of things in winconp, the private
console header. Concern about interop structures.
-> Not used for interop, followup filed to clean it up (#3123)
* Is `unsigned int` right for codepage? For width?
-> Yes: codepage became UINT (from WORD) when we moved from Win16 to
Win32
This commit also includes a workaround for #3170. Growing
CONSOLE_INFORMATION made us lose the struct layout lottery during
release builds, and this was an expedient fix.
Closes #602.
Related to #3123.
2019-10-15 06:23:45 +02:00
|
|
|
const std::wstring_view fontFaceName,
|
2020-04-30 01:41:56 +02:00
|
|
|
const COLORREF backgroundColor);
|
2019-08-20 00:59:01 +02:00
|
|
|
|
2019-11-13 21:13:22 +01:00
|
|
|
static std::string GenRTF(const TextAndColor& rows,
|
|
|
|
const int fontHeightPoints,
|
|
|
|
const std::wstring_view fontFaceName,
|
|
|
|
const COLORREF backgroundColor);
|
|
|
|
|
2020-03-16 13:55:25 +01:00
|
|
|
struct PositionInformation
|
|
|
|
{
|
|
|
|
short mutableViewportTop{ 0 };
|
|
|
|
short visibleViewportTop{ 0 };
|
|
|
|
};
|
|
|
|
|
Add support for "reflow"ing the Terminal buffer (#4741)
This PR adds support for "Resize with Reflow" to the Terminal. In
conhost, `ResizeWithReflow` is the function that's responsible for
reflowing wrapped lines of text as the buffer gets resized. Now that
#4415 has merged, we can also implement this in the Terminal. Now, when
the Terminal is resized, it will reflow the lines of it's buffer in the
same way that conhost does. This means, the terminal will no longer chop
off the ends of lines as the buffer is too small to represent them.
As a happy side effect of this PR, it also fixed #3490. This was a bug
that plagued me during the investigation into this functionality. The
original #3490 PR, #4354, tried to fix this bug with some heavy conpty
changes. Turns out, that only made things worse, and far more
complicated. When I really got to thinking about it, I realized "conhost
can handle this right, why can't the Terminal?". Turns out, by adding
resize with reflow, I was also able to fix this at the same time.
Conhost does a little bit of math after reflowing to attempt to keep the
viewport in the same relative place after a reflow. By re-using that
logic in the Terminal, I was able to fix #3490.
I also included that big ole test from #3490, because everyone likes
adding 60 test cases in a PR.
## References
* #4200 - this scenario
* #405/#4415 - conpty emits wrapped lines, which was needed for this PR
* #4403 - delayed EOL wrapping via conpty, which was also needed for
this
* #4354 - we don't speak of this PR anymore
## PR Checklist
* [x] Closes #1465
* [x] Closes #3490
* [x] Closes #4771
* [x] Tests added/passed
## EDIT: Changes to this PR on 5 March 2020
I learned more since my original version of this PR. I wrote that in
January, and despite my notes that say it was totally working, it
_really_ wasn't.
Part of the hard problem, as mentioned in #3490, is that the Terminal
might request a resize to (W, H-1), and while conpty is preparing that
frame, or before the terminal has received that frame, the Terminal
resizes to (W, H-2). Now, there aren't enough lines in the terminal
buffer to catch all the lines that conpty is about to emit. When that
happens, lines get duplicated in the buffer. From a UX perspective, this
certainly looks a lot worse than a couple lost lines. It looks like
utter chaos.
So I've introduced a new mode to conpty to try and counteract this
behavior. This behavior I'm calling "quirky resize". The **TL;DR** of
quirky resize mode is that conpty won't emit the entire buffer on a
resize, and will trust that the terminal is prepared to reflow it's
buffer on it's own.
This will enable the quirky resize behavior for applications that are
prepared for it. The "quirky resize" is "don't `InvalidateAll` when the
terminal resizes". This is added as a quirk as to not regress other
terminal applications that aren't prepared for this behavior
(gnome-terminal, conhost in particular). For those kinds of terminals,
when the buffer is resized, it's just going to lose lines. That's what
currently happens for them.
When the quirk is enabled, conpty won't repaint the entire buffer. This
gets around the "duplicated lines" issue that requesting multiple
resizes in a row can cause. However, for these terminals that are
unprepared, the conpty cursor might end up in the wrong position after a
quirky resize.
The case in point is maximizing the terminal. For maximizing
(height->50) from a buffer that's 30 lines tall, with the cursor on
y=30, this is what happens:
* With the quirk disabled, conpty reprints the entire buffer. This is
60 lines that get printed. This ends up blowing away about 20 lines
of scrollback history, as the terminal app would have tried to keep
the text pinned to the bottom of the window. The term. app moved the
viewport up 20 lines, and then the 50 lines of conpty output (30
lines of text, and 20 blank lines at the bottom) overwrote the lines
from the scrollback. This is bad, but not immediately obvious, and
is **what currently happens**.
* With the quirk enabled, conpty doesn't emit any lines, but the
actual content of the window is still only in the top 30 lines.
However, the terminal app has still moved 20 lines down from the
scrollback back into the viewport. So the terminal's cursor is at
y=50 now, but conpty's is at 30. This means that the terminal and
conpty are out of sync, and there's not a good way of re-syncing
these. It's very possible (trivial in `powershell`) that the new
output will jump up to y=30 override the existing output in the
terminal buffer.
The Windows Terminal is already prepared for this quirky behavior, so it
doesn't keep the output at the bottom of the window. It shifts it's
viewport down to match what conpty things the buffer looks like.
What happens when we have passthrough mode and WT is like "I would like
quirky resize"? I guess things will just work fine, cause there won't be
a buffer behind the passthrough app that the terminal cares about. Sure,
in the passthrough case the Terminal could _not_ quirky resize, but the
quirky resize won't be wrong.
2020-03-13 01:43:37 +01:00
|
|
|
static HRESULT Reflow(TextBuffer& oldBuffer,
|
|
|
|
TextBuffer& newBuffer,
|
|
|
|
const std::optional<Microsoft::Console::Types::Viewport> lastCharacterViewport,
|
2020-03-16 13:55:25 +01:00
|
|
|
std::optional<std::reference_wrapper<PositionInformation>> positionInfo);
|
2020-01-14 22:34:43 +01:00
|
|
|
|
2020-10-28 21:24:43 +01:00
|
|
|
const size_t AddPatternRecognizer(const std::wstring_view regexString);
|
|
|
|
void CopyPatterns(const TextBuffer& OtherBuffer);
|
|
|
|
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const size_t firstRow, const size_t lastRow) const;
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
private:
|
2020-07-09 13:18:25 +02:00
|
|
|
void _UpdateSize();
|
|
|
|
Microsoft::Console::Types::Viewport _size;
|
2019-05-03 00:29:04 +02:00
|
|
|
std::deque<ROW> _storage;
|
|
|
|
Cursor _cursor;
|
|
|
|
|
|
|
|
SHORT _firstRow; // indexes top row (not necessarily 0)
|
|
|
|
|
|
|
|
TextAttribute _currentAttributes;
|
|
|
|
|
|
|
|
// storage location for glyphs that can't fit into the buffer normally
|
|
|
|
UnicodeStorage _unicodeStorage;
|
|
|
|
|
2020-09-03 19:52:39 +02:00
|
|
|
std::unordered_map<uint16_t, std::wstring> _hyperlinkMap;
|
|
|
|
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
|
|
|
|
uint16_t _currentHyperlinkId;
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
void _RefreshRowIDs(std::optional<SHORT> newRowWidth);
|
|
|
|
|
|
|
|
Microsoft::Console::Render::IRenderTarget& _renderTarget;
|
|
|
|
|
2019-08-30 00:23:07 +02:00
|
|
|
void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2020-07-09 13:18:25 +02:00
|
|
|
COORD _GetPreviousFromCursor() const noexcept;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
void _SetWrapOnCurrentRow();
|
|
|
|
void _AdjustWrapOnCurrentRow(const bool fSet);
|
|
|
|
|
|
|
|
void _NotifyPaint(const Microsoft::Console::Types::Viewport& viewport) const;
|
|
|
|
|
|
|
|
// Assist with maintaining proper buffer state for Double Byte character sequences
|
|
|
|
bool _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute);
|
|
|
|
bool _AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute);
|
|
|
|
|
|
|
|
ROW& _GetFirstRow();
|
|
|
|
ROW& _GetPrevRowNoWrap(const ROW& row);
|
|
|
|
|
2020-02-28 01:42:26 +01:00
|
|
|
void _ExpandTextRow(SMALL_RECT& selectionRow) const;
|
|
|
|
|
2020-03-05 00:10:10 +01:00
|
|
|
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
|
Refactor UiaTextRange For Improved Navigation and Reliability (#4018)
## Summary of the Pull Request
This pull request is intended to achieve the following goals...
1) reduce duplicate code
2) remove static functions
3) improve readability
4) improve reliability
5) improve code-coverage for testing
6) establish functioning text buffer navigation in Narrator and NVDA
This also required a change to the wrapper class `XamlUiaTextRange` that has been causing issues with Narrator and NVDA.
See below for additional context.
## References
#3976 - I believe this might have been a result of improperly handling degenerate ranges. Fixed here.
#3895 - reduced the duplicate code. No need to separate into different files
#2160 - same as #3976 above
#1993 - I think just about everything is no longer static
## PR Checklist
* [x] Closes #3895, Closes #1993, Closes #3976, Closes #2160
* [x] CLA signed
* [x] Tests added/passed
## Detailed Description of the Pull Request / Additional comments
### UiaTextRange
- converted endpoints into the COORD system in the TextBuffer coordinate space
- `start` is inclusive, `end` is exclusive. A degenerate range is when start == end.
- all functions are no longer static
- `MoveByUnit()` functions now rely on `MoveEndpointByUnit()` functions
- removed unnecessary typedefs like `Endpoint`, `ScreenInfoRow`, etc..
- relied more heavily on existing functionality from `TextBuffer` and `Viewport`
### XamlUiaTextRange
- `GetAttributeValue()` must return a special HRESULT that signifies that the requested attribute is not supported. This was the cause of a number of inconsistencies between Narrator and NVDA.
- `FindText()` should return `nullptr` if nothing was found. #4373 properly fixes this functionality now that Search is a shared module
### TextBuffer
- Word navigation functionality is entirely in `TextBuffer` for proper abstraction
- a total of 6 functions are now dedicated to word navigation to get a good understanding of the differences between a "word" in Accessibility and a "word" in selection
As an example, consider a buffer with this text in it:
" word other "
In selection, a "word" is defined as the range between two delimiters, so the words in the example include [" ", "word", " ", "other", " "].
In accessibility , a "word" includes the delimiters after a range of readable characters, so the words in the example include ["word ", "other "].
Additionally, accessibility word navigation must be able to detect if it is on the first or last word. This resulted in a slight variant of word navigation functions that return a boolean instead of a COORD.
Ideally, these functions can be consolidated, but that is too risky for a PR of this size as it can have an effect on selection.
### Viewport
- the concept of `EndExclusive` is added. This is used by UiaTextRange's `end` anchor as it is exclusive. To signify that the last character in the buffer is included in this buffer, `end` must be one past the end of the buffer. This is `EndExclusive`
- Since many functions check if the given `COORD` is in bounds, a flag must be set to allow `EndExclusive` as a valid `COORD` that is in bounds.
### Testing
- word navigation testing relies more heavily on TextBuffer tests
- additional testing was created for non-movement focused functions of UiaTextRange
- The results have been compared to Microsoft Word and some have been verified by UiAutomation/Narrator contacts as expected results.
## Validation Steps Performed
Tests pass
Narrator works
NVDA works
2020-01-31 21:59:39 +01:00
|
|
|
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
|
|
|
|
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
2020-09-30 23:11:46 +02:00
|
|
|
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const;
|
Refactor UiaTextRange For Improved Navigation and Reliability (#4018)
## Summary of the Pull Request
This pull request is intended to achieve the following goals...
1) reduce duplicate code
2) remove static functions
3) improve readability
4) improve reliability
5) improve code-coverage for testing
6) establish functioning text buffer navigation in Narrator and NVDA
This also required a change to the wrapper class `XamlUiaTextRange` that has been causing issues with Narrator and NVDA.
See below for additional context.
## References
#3976 - I believe this might have been a result of improperly handling degenerate ranges. Fixed here.
#3895 - reduced the duplicate code. No need to separate into different files
#2160 - same as #3976 above
#1993 - I think just about everything is no longer static
## PR Checklist
* [x] Closes #3895, Closes #1993, Closes #3976, Closes #2160
* [x] CLA signed
* [x] Tests added/passed
## Detailed Description of the Pull Request / Additional comments
### UiaTextRange
- converted endpoints into the COORD system in the TextBuffer coordinate space
- `start` is inclusive, `end` is exclusive. A degenerate range is when start == end.
- all functions are no longer static
- `MoveByUnit()` functions now rely on `MoveEndpointByUnit()` functions
- removed unnecessary typedefs like `Endpoint`, `ScreenInfoRow`, etc..
- relied more heavily on existing functionality from `TextBuffer` and `Viewport`
### XamlUiaTextRange
- `GetAttributeValue()` must return a special HRESULT that signifies that the requested attribute is not supported. This was the cause of a number of inconsistencies between Narrator and NVDA.
- `FindText()` should return `nullptr` if nothing was found. #4373 properly fixes this functionality now that Search is a shared module
### TextBuffer
- Word navigation functionality is entirely in `TextBuffer` for proper abstraction
- a total of 6 functions are now dedicated to word navigation to get a good understanding of the differences between a "word" in Accessibility and a "word" in selection
As an example, consider a buffer with this text in it:
" word other "
In selection, a "word" is defined as the range between two delimiters, so the words in the example include [" ", "word", " ", "other", " "].
In accessibility , a "word" includes the delimiters after a range of readable characters, so the words in the example include ["word ", "other "].
Additionally, accessibility word navigation must be able to detect if it is on the first or last word. This resulted in a slight variant of word navigation functions that return a boolean instead of a COORD.
Ideally, these functions can be consolidated, but that is too risky for a PR of this size as it can have an effect on selection.
### Viewport
- the concept of `EndExclusive` is added. This is used by UiaTextRange's `end` anchor as it is exclusive. To signify that the last character in the buffer is included in this buffer, `end` must be one past the end of the buffer. This is `EndExclusive`
- Since many functions check if the given `COORD` is in bounds, a flag must be set to allow `EndExclusive` as a valid `COORD` that is in bounds.
### Testing
- word navigation testing relies more heavily on TextBuffer tests
- additional testing was created for non-movement focused functions of UiaTextRange
- The results have been compared to Microsoft Word and some have been verified by UiAutomation/Narrator contacts as expected results.
## Validation Steps Performed
Tests pass
Narrator works
NVDA works
2020-01-31 21:59:39 +01:00
|
|
|
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
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.
2019-12-13 00:22:12 +01:00
|
|
|
|
2020-09-03 19:52:39 +02:00
|
|
|
void _PruneHyperlinks();
|
|
|
|
|
2020-10-28 21:24:43 +01:00
|
|
|
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
|
|
|
|
size_t _currentPatternId;
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
#ifdef UNIT_TESTING
|
|
|
|
friend class TextBufferTests;
|
|
|
|
friend class UiaTextRangeTests;
|
|
|
|
#endif
|
|
|
|
};
|