Merge branch 'dev/migrie/f/just-elevated-state-2' into dev/migrie/f/non-terminal-content-elevation-warning

This commit is contained in:
Mike Griese 2021-09-20 11:38:19 -05:00
commit d0f05f60e9
91 changed files with 3511 additions and 1278 deletions

View file

@ -36,6 +36,9 @@ liga
lje
locl
lorem
Llast
Lmid
Lorigin
maxed
mkmk
mru

View file

@ -69,6 +69,7 @@ iosfwd
IPackage
IPeasant
isspace
ISetup
IStorage
istream
IStringable

View file

@ -2578,6 +2578,7 @@ VREDRAW
vsc
vscprintf
VSCROLL
vsdevshell
vsinfo
vsnprintf
vso

View file

@ -346,11 +346,13 @@
],
"type": "string"
},
"SplitState": {
"SplitDirection": {
"enum": [
"vertical",
"horizontal",
"auto"
"auto",
"up",
"right",
"down",
"left"
],
"type": "string"
},
@ -629,9 +631,9 @@
"properties": {
"action": { "type": "string", "pattern": "splitPane" },
"split": {
"$ref": "#/definitions/SplitState",
"$ref": "#/definitions/SplitDirection",
"default": "auto",
"description": "The orientation to split the pane in. Possible values:\n -\"auto\" (splits pane based on remaining space)\n -\"horizontal\" (think [-])\n -\"vertical\" (think [|])"
"description": "The orientation to split the pane in. Possible values:\n -\"auto\" (splits pane based on remaining space)\n -\"up\" (think [-] and above)\n -\"down\" (think [-] and below)\n -\"left\" (think [|] and to the left)\n -\"right\"(think [|] and to the right)"
},
"splitMode": {
"default": "duplicate",
@ -1262,14 +1264,14 @@
"type": [ "integer", "string" ],
"deprecated": true
},
"minimizeToTray": {
"minimizeToNotificationArea": {
"default": "false",
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the system tray through which the user can access their windows.",
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the notification area through which the user can access their windows.",
"type": "boolean"
},
"alwaysShowTrayIcon": {
"alwaysShowNotificationIcon": {
"default": "false",
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
"description": "When set to true, the Terminal's notification icon will always be shown in the notification area.",
"type": "boolean"
},
"useAcrylicInTabRow": {

View file

@ -1057,9 +1057,10 @@ const DelimiterClass TextBuffer::_GetDelimiterClassAt(const COORD pos, const std
// - accessibilityMode - when enabled, we continue expanding left until we are at the beginning of a readable word.
// Otherwise, expand left until a character of a new delimiter class is found
// (or a row boundary is encountered)
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// 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 accessibilityMode) const
const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
{
// Consider a buffer with this text in it:
// " word other "
@ -1072,10 +1073,9 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
// NOTE: the start anchor (this one) is inclusive, whereas the end anchor (GetWordEnd) is exclusive
#pragma warning(suppress : 26496)
// GH#7664: Treat EndExclusive as EndInclusive so
// that it actually points to a space in the buffer
auto copy{ target };
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (target == bufferSize.Origin())
{
// can't expand left
@ -1083,9 +1083,15 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
}
else if (target == bufferSize.EndExclusive())
{
// treat EndExclusive as EndInclusive
// GH#7664: Treat EndExclusive as EndInclusive so
// that it actually points to a space in the buffer
copy = { bufferSize.RightInclusive(), bufferSize.BottomInclusive() };
}
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
// if at/past the limit --> clamp to limit
copy = *limitOptional;
}
if (accessibilityMode)
{
@ -1179,9 +1185,10 @@ const COORD TextBuffer::_GetWordStartForSelection(const COORD target, const std:
// - accessibilityMode - when enabled, we continue expanding right until we are at the beginning of the next READABLE word
// Otherwise, expand right until a character of a new delimiter class is found
// (or a row boundary is encountered)
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// 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 accessibilityMode) const
const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
{
// Consider a buffer with this text in it:
// " word other "
@ -1193,16 +1200,17 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
// so the words in the example include ["word ", "other "]
// NOTE: the end anchor (this one) is exclusive, whereas the start anchor (GetWordStart) is inclusive
// Already at the end. Can't move forward.
if (target == GetSize().EndExclusive())
// Already at/past the limit. Can't move forward.
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
return target;
}
if (accessibilityMode)
{
const auto lastCharPos{ GetLastNonSpaceCharacter() };
return _GetWordEndForAccessibility(target, wordDelimiters, lastCharPos);
return _GetWordEndForAccessibility(target, wordDelimiters, limit);
}
else
{
@ -1215,44 +1223,46 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
// Arguments:
// - target - a COORD on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// - lastCharPos - the position of the last nonspace character in the text buffer (to improve performance)
// - limit - the last "valid" position in the text buffer (to improve performance)
// Return Value:
// - The COORD for the first character of the next readable "word". If no next word, return one past the end of the buffer
const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const
const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD limit) const
{
const auto bufferSize = GetSize();
COORD result = target;
const auto bufferSize{ GetSize() };
COORD result{ target };
// Check if we're already on/past the last RegularChar
if (bufferSize.CompareInBounds(result, lastCharPos, true) >= 0)
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
return bufferSize.EndExclusive();
// if we're already on/past the last RegularChar,
// clamp result to that position
result = limit;
// make the result exclusive
bufferSize.IncrementInBounds(result, true);
}
// ignore right boundary. Continue through readable text found
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
else
{
if (!bufferSize.IncrementInBounds(result, true))
auto iter{ GetCellDataAt(result, bufferSize) };
while (iter && iter.Pos() != limit && _GetDelimiterClassAt(iter.Pos(), wordDelimiters) == DelimiterClass::RegularChar)
{
break;
// Iterate through readable text
++iter;
}
}
// we are already on/past the last RegularChar
if (bufferSize.CompareInBounds(result, lastCharPos, true) >= 0)
{
return bufferSize.EndExclusive();
}
// make sure we expand to the beginning of the NEXT word
while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
{
if (!bufferSize.IncrementInBounds(result, true))
while (iter && iter.Pos() != limit && _GetDelimiterClassAt(iter.Pos(), wordDelimiters) != DelimiterClass::RegularChar)
{
// we are at the EndInclusive COORD
// this signifies that we must include the last char in the buffer
// but the position of the COORD points to nothing
break;
// expand to the beginning of the NEXT word
++iter;
}
result = iter.Pos();
// Special case: we tried to move one past the end of the buffer,
// but iter prevented that (because that pos doesn't exist).
// Manually increment onto the EndExclusive point.
if (!iter)
{
bufferSize.IncrementInBounds(result, true);
}
}
@ -1345,18 +1355,20 @@ void TextBuffer::_PruneHyperlinks()
// Arguments:
// - pos - a COORD on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// - lastCharPos - the position of the last nonspace character in the text buffer (to improve performance)
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// Return Value:
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
// - pos - The COORD for the first character on the "word" (inclusive)
bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const
bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional) const
{
// move to the beginning of the next word
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the word"
// This is also the inclusive start of the next word.
auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, lastCharPos) };
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
if (copy == GetSize().EndExclusive())
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
{
return false;
}
@ -1393,19 +1405,23 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
// - Update pos to be the beginning of the current glyph/character. This is used for accessibility
// Arguments:
// - pos - a COORD on the word you are currently on
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// Return Value:
// - pos - The COORD for the first cell of the current glyph (inclusive)
const til::point TextBuffer::GetGlyphStart(const til::point pos) const
const til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional) const
{
COORD resultPos = pos;
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (resultPos == bufferSize.EndExclusive())
// Clamp pos to limit
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
{
bufferSize.DecrementInBounds(resultPos, true);
resultPos = limit;
}
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
// limit is exclusive, so we need to move back to be within valid bounds
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
{
bufferSize.DecrementInBounds(resultPos, true);
}
@ -1419,12 +1435,19 @@ const til::point TextBuffer::GetGlyphStart(const til::point pos) const
// - pos - a COORD on the word you are currently on
// Return Value:
// - pos - The COORD for the last cell of the current glyph (exclusive)
const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
const til::point TextBuffer::GetGlyphEnd(const til::point pos, std::optional<til::point> limitOptional) const
{
COORD resultPos = pos;
const auto bufferSize = GetSize();
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
// Clamp pos to limit
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
{
resultPos = limit;
}
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
{
bufferSize.IncrementInBounds(resultPos, true);
}
@ -1438,29 +1461,43 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
// - Update pos to be the beginning of the next glyph/character. This is used for accessibility
// Arguments:
// - pos - a COORD on the word you are currently on
// - allowBottomExclusive - allow the nonexistent end-of-buffer cell to be encountered
// - allowExclusiveEnd - allow result to be the exclusive limit (one past limit)
// - limit - boundaries for the iterator to operate within
// Return Value:
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
// - pos - The COORD for the first cell of the current glyph (inclusive)
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) const
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::optional<til::point> limitOptional) const
{
COORD resultPos = pos;
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (resultPos == GetSize().EndExclusive())
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit, true) };
if (distanceToLimit >= 0)
{
// we're already at the end
// Corner Case: we're on/past the limit
// Clamp us to the limit
pos = limit;
return false;
}
else if (!allowExclusiveEnd && distanceToLimit == -1)
{
// Corner Case: we're just before the limit
// and we are not allowed onto the exclusive end.
// Fail to move.
return false;
}
// try to move. If we can't, we're done.
const bool success = bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
// Try to move forward, but if we hit the buffer boundary, we fail to move.
auto iter{ GetCellDataAt(pos, bufferSize) };
const bool success{ ++iter };
// Move again if we're on a wide glyph
if (success && iter->DbcsAttr().IsTrailing())
{
bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
++iter;
}
pos = resultPos;
pos = iter.Pos();
return success;
}
@ -1471,12 +1508,21 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) con
// Return Value:
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
// - pos - The COORD for the first cell of the previous glyph (inclusive)
bool TextBuffer::MoveToPreviousGlyph(til::point& pos) const
bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional) const
{
COORD resultPos = pos;
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (bufferSize.CompareInBounds(pos, limit, true) > 0)
{
// we're past the end
// clamp us to the limit
pos = limit;
return true;
}
// try to move. If we can't, we're done.
const auto bufferSize = GetSize();
const bool success = bufferSize.DecrementInBounds(resultPos, true);
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
{

View file

@ -141,15 +141,15 @@ public:
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
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;
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
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;
bool MoveToPreviousGlyph(til::point& pos) const;
const til::point GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
const til::point GetGlyphEnd(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional = std::nullopt) const;
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const;
@ -242,7 +242,7 @@ private:
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const;
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD limit) const;
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
void _PruneHyperlinks();

View file

@ -152,6 +152,10 @@ namespace SettingsModelLocalTests
{ "name": "command4", "command": { "action": "splitPane" } },
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } },
{ "name": "command6", "command": { "action": "splitPane", "size": 0.25 } },
{ "name": "command7", "command": { "action": "splitPane", "split": "right" } },
{ "name": "command8", "command": { "action": "splitPane", "split": "left" } },
{ "name": "command9", "command": { "action": "splitPane", "split": "up" } },
{ "name": "command10", "command": { "action": "splitPane", "split": "down" } },
])" };
const auto commands0Json = VerifyParseSucceeded(commands0String);
@ -160,7 +164,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, commands.Size());
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
VERIFY_ARE_EQUAL(0u, warnings.size());
VERIFY_ARE_EQUAL(5u, commands.Size());
VERIFY_ARE_EQUAL(9u, commands.Size());
{
auto command = commands.Lookup(L"command1");
@ -170,7 +174,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -181,7 +185,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -192,7 +196,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -203,7 +207,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -214,9 +218,53 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command7");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command8");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Left, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command9");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Up, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command10");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
}
void CommandTests::TestSplitPaneBadSize()
@ -244,7 +292,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
}
}
@ -286,8 +334,10 @@ namespace SettingsModelLocalTests
const std::string commands0String{ R"([
{ "command": { "action": "splitPane", "split": null } },
{ "command": { "action": "splitPane", "split": "vertical" } },
{ "command": { "action": "splitPane", "split": "horizontal" } },
{ "command": { "action": "splitPane", "split": "left" } },
{ "command": { "action": "splitPane", "split": "right" } },
{ "command": { "action": "splitPane", "split": "up" } },
{ "command": { "action": "splitPane", "split": "down" } },
{ "command": { "action": "splitPane", "split": "none" } },
{ "command": { "action": "splitPane" } },
{ "command": { "action": "splitPane", "split": "auto" } },
@ -301,10 +351,10 @@ namespace SettingsModelLocalTests
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
VERIFY_ARE_EQUAL(0u, warnings.size());
// There are only 3 commands here: all of the `"none"`, `"auto"`,
// There are only 5 commands here: all of the `"none"`, `"auto"`,
// `"foo"`, `null`, and <no args> bindings all generate the same action,
// which will generate just a single name for all of them.
VERIFY_ARE_EQUAL(3u, commands.Size());
VERIFY_ARE_EQUAL(5u, commands.Size());
{
auto command = commands.Lookup(L"Split pane");
@ -314,27 +364,47 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: vertical");
auto command = commands.Lookup(L"Split pane, split: left");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Left, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: horizontal");
auto command = commands.Lookup(L"Split pane, split: right");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: up");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Up, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: down");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
}
}
void CommandTests::TestLayerOnAutogeneratedName()
@ -360,7 +430,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
}
}

View file

@ -2130,7 +2130,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2147,7 +2147,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2161,7 +2161,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2175,7 +2175,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2189,7 +2189,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2218,7 +2218,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());

View file

@ -432,7 +432,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
}
{
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
@ -440,7 +440,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
}
{
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
@ -448,7 +448,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
}
{
KeyChord kc{ true, false, false, false, static_cast<int32_t>('H'), 0 };
@ -456,7 +456,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
}
}

View file

@ -256,10 +256,14 @@ namespace SettingsModelLocalTests
])" };
// complex command with key chords
const std::string actionsString4{ R"([
const std::string actionsString4A{ R"([
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" },
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" }
])" };
const std::string actionsString4B{ R"([
{ "command": { "action": "findMatch", "direction": "next" }, "keys": "ctrl+shift+s" },
{ "command": { "action": "findMatch", "direction": "prev" }, "keys": "ctrl+shift+r" }
])" };
// command with name and icon and multiple key chords
const std::string actionsString5{ R"([
@ -372,7 +376,8 @@ namespace SettingsModelLocalTests
RoundtripTest<implementation::ActionMap>(actionsString3);
Log::Comment(L"complex commands with key chords");
RoundtripTest<implementation::ActionMap>(actionsString4);
RoundtripTest<implementation::ActionMap>(actionsString4A);
RoundtripTest<implementation::ActionMap>(actionsString4B);
Log::Comment(L"command with name and icon and multiple key chords");
RoundtripTest<implementation::ActionMap>(actionsString5);

View file

@ -124,7 +124,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -145,7 +145,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -167,7 +167,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -189,7 +189,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -211,7 +211,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -242,7 +242,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());

View file

@ -715,7 +715,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -735,7 +735,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Horizontal, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -757,7 +757,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Vertical, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -799,7 +799,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
@ -828,7 +828,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Horizontal, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, myArgs.SplitDirection());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
@ -857,7 +857,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
@ -1779,7 +1779,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1799,7 +1799,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1820,7 +1820,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1830,7 +1830,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1852,7 +1852,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1862,7 +1862,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.7f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}

View file

@ -132,7 +132,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -156,7 +156,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -174,7 +174,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -192,7 +192,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -259,7 +259,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -283,7 +283,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -301,7 +301,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -319,7 +319,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -388,7 +388,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -412,7 +412,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -430,7 +430,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -448,7 +448,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -680,16 +680,16 @@ namespace TerminalAppLocalTests
// <Command Palette>
// ├─ profile0...
// | ├─ Split pane, profile: profile0
// | ├─ Split pane, direction: vertical, profile: profile0
// | └─ Split pane, direction: horizontal, profile: profile0
// | ├─ Split pane, direction: right, profile: profile0
// | └─ Split pane, direction: down, profile: profile0
// ├─ profile1...
// | ├─Split pane, profile: profile1
// | ├─Split pane, direction: vertical, profile: profile1
// | └─Split pane, direction: horizontal, profile: profile1
// | ├─Split pane, direction: right, profile: profile1
// | └─Split pane, direction: down, profile: profile1
// └─ profile2...
// ├─ Split pane, profile: profile2
// ├─ Split pane, direction: vertical, profile: profile2
// └─ Split pane, direction: horizontal, profile: profile2
// ├─ Split pane, direction: right, profile: profile2
// └─ Split pane, direction: down, profile: profile2
const std::string settingsJson{ R"(
{
@ -719,8 +719,8 @@ namespace TerminalAppLocalTests
"name": "${profile.name}...",
"commands": [
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "auto" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" } }
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "right" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } }
]
}
],
@ -762,7 +762,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -773,7 +773,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: down, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -783,7 +783,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -794,7 +794,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: right, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -804,7 +804,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -915,16 +915,16 @@ namespace TerminalAppLocalTests
// └─ New Pane...
// ├─ profile0...
// | ├─ Split automatically
// | ├─ Split vertically
// | └─ Split horizontally
// | ├─ Split right
// | └─ Split down
// ├─ profile1...
// | ├─ Split automatically
// | ├─ Split vertically
// | └─ Split horizontally
// | ├─ Split right
// | └─ Split down
// └─ profile2...
// ├─ Split automatically
// ├─ Split vertically
// └─ Split horizontally
// ├─ Split right
// └─ Split down
const std::string settingsJson{ R"(
{
@ -957,8 +957,8 @@ namespace TerminalAppLocalTests
"name": "${profile.name}...",
"commands": [
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "auto" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" } }
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "right" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } }
]
}
]
@ -1010,7 +1010,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1021,7 +1021,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: down, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -1031,7 +1031,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1042,7 +1042,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: right, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -1052,7 +1052,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1127,7 +1127,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1156,7 +1156,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1174,7 +1174,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1192,7 +1192,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());

View file

@ -508,7 +508,7 @@ namespace TerminalAppLocalTests
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
@ -526,7 +526,7 @@ namespace TerminalAppLocalTests
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
@ -844,7 +844,7 @@ namespace TerminalAppLocalTests
// | 1 | 2 |
// | | |
// -------------------
page->_SplitPane(SplitState::Vertical, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Right, SplitType::Duplicate, 0.5f, nullptr);
secondId = tab->_activePane->Id().value();
});
Sleep(250);
@ -862,7 +862,7 @@ namespace TerminalAppLocalTests
// | 3 | |
// | | |
// -------------------
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr);
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
// Split again to make the 3rd tab
thirdId = tab->_activePane->Id().value();
@ -882,7 +882,7 @@ namespace TerminalAppLocalTests
// | 3 | 4 |
// | | |
// -------------------
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr);
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
fourthId = tab->_activePane->Id().value();
});

View file

@ -80,8 +80,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
peasant.RenameRequested({ this, &Monarch::_renameRequested });
peasant.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
peasant.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
peasant.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); });
peasant.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll });
_peasants[newPeasantsId] = peasant;
@ -147,6 +147,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - <none>
void Monarch::SignalClose(const uint64_t peasantId)
{
_clearOldMruEntries(peasantId);
_peasants.erase(peasantId);
_WindowClosedHandlers(nullptr, nullptr);
}

View file

@ -60,8 +60,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);

View file

@ -54,8 +54,8 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;

View file

@ -226,34 +226,34 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestShowTrayIcon()
void Peasant::RequestShowNotificationIcon()
{
try
{
_ShowTrayIconRequestedHandlers(*this, nullptr);
_ShowNotificationIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestShowTrayIcon",
"Peasant_RequestShowNotificationIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestHideTrayIcon()
void Peasant::RequestHideNotificationIcon()
{
try
{
_HideTrayIconRequestedHandlers(*this, nullptr);
_HideNotificationIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestHideTrayIcon",
"Peasant_RequestHideNotificationIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}

View file

@ -28,8 +28,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestIdentifyWindows();
void DisplayWindowId();
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
void RequestShowTrayIcon();
void RequestHideTrayIcon();
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
void RequestQuitAll();
void Quit();
@ -45,8 +45,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);

View file

@ -65,8 +65,8 @@ namespace Microsoft.Terminal.Remoting
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
void Summon(SummonWindowBehavior behavior);
void RequestShowTrayIcon();
void RequestHideTrayIcon();
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
void RequestQuitAll();
void Quit();
@ -76,8 +76,8 @@ namespace Microsoft.Terminal.Remoting
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
};

View file

@ -269,8 +269,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
_monarch.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
_monarch.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
_monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.QuitAllRequested([this](auto&&, auto&&) { _QuitAllRequestedHandlers(*this, nullptr); });
_BecameMonarchHandlers(*this, nullptr);
@ -529,7 +529,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void WindowManager::SummonAllWindows()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
_monarch.SummonAllWindows();
}
@ -556,28 +556,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
// Method Description:
// - Ask the monarch to show a tray icon.
// - Ask the monarch to show a notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestShowTrayIcon()
winrt::fire_and_forget WindowManager::RequestShowNotificationIcon()
{
co_await winrt::resume_background();
_peasant.RequestShowTrayIcon();
_peasant.RequestShowNotificationIcon();
}
// Method Description:
// - Ask the monarch to hide its tray icon.
// - Ask the monarch to hide its notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestHideTrayIcon()
winrt::fire_and_forget WindowManager::RequestHideNotificationIcon()
{
auto strongThis{ get_strong() };
co_await winrt::resume_background();
_peasant.RequestHideTrayIcon();
_peasant.RequestHideNotificationIcon();
}
// Method Description:

View file

@ -45,8 +45,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
uint64_t GetNumberOfPeasants();
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
winrt::fire_and_forget RequestShowTrayIcon();
winrt::fire_and_forget RequestHideTrayIcon();
winrt::fire_and_forget RequestShowNotificationIcon();
winrt::fire_and_forget RequestHideNotificationIcon();
winrt::fire_and_forget RequestQuitAll();
bool DoesQuakeWindowExist();
void UpdateActiveTabTitle(winrt::hstring title);
@ -55,8 +55,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:

View file

@ -14,8 +14,8 @@ namespace Microsoft.Terminal.Remoting
Boolean IsMonarch { get; };
void SummonWindow(SummonWindowSelectionArgs args);
void SummonAllWindows();
void RequestShowTrayIcon();
void RequestHideTrayIcon();
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
UInt64 GetNumberOfPeasants();
void RequestQuitAll();
void UpdateActiveTabTitle(String title);
@ -25,8 +25,8 @@ namespace Microsoft.Terminal.Remoting
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
};
}

View file

@ -173,7 +173,7 @@ namespace winrt::TerminalApp::implementation
}
else if (const auto& realArgs = args.ActionArgs().try_as<SplitPaneArgs>())
{
_SplitPane(realArgs.SplitStyle(),
_SplitPane(realArgs.SplitDirection(),
realArgs.SplitMode(),
// This is safe, we're already filtering so the value is (0, 1)
::base::saturated_cast<float>(realArgs.SplitSize()),

View file

@ -274,18 +274,18 @@ void AppCommandlineArgs::_buildSplitPaneParser()
// _getNewTerminalArgs MUST be called before parsing any other options,
// as it might clear those options while finding the commandline
auto terminalArgs{ _getNewTerminalArgs(subcommand) };
auto style{ SplitState::Automatic };
auto style{ SplitDirection::Automatic };
// Make sure to use the `Option`s here to check if they were set -
// _getNewTerminalArgs might reset them while parsing a commandline
if ((*subcommand._horizontalOption || *subcommand._verticalOption))
{
if (_splitHorizontal)
{
style = SplitState::Horizontal;
style = SplitDirection::Down;
}
else if (_splitVertical)
{
style = SplitState::Vertical;
style = SplitDirection::Right;
}
}
const auto splitMode{ subcommand._duplicateOption && _splitDuplicate ? SplitType::Duplicate : SplitType::Manual };

View file

@ -10,6 +10,9 @@
#include <LibraryResources.h>
#include <WtExeUtils.h>
#include <wil/token_helpers.h >
#include "../../types/inc/utils.hpp"
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
@ -136,38 +139,8 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD
// Return Value:
// - true if the user is an administrator
static bool _isUserAdmin() noexcept
try
{
DWORD dwSize;
wil::unique_handle hToken;
TOKEN_ELEVATION_TYPE elevationType;
TOKEN_ELEVATION elevationState{ 0 };
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
GetTokenInformation(hToken.get(), TokenElevationType, &elevationType, sizeof(elevationType), &dwSize);
GetTokenInformation(hToken.get(), TokenElevation, &elevationState, sizeof(elevationState), &dwSize);
if (elevationType == TokenElevationTypeDefault && elevationState.TokenIsElevated)
{
// In this case, the user has UAC entirely disabled. This is sorta
// weird, we treat this like the user isn't an admin at all. There's no
// separation of powers, so the things we normally want to gate on
// "having special powers" doesn't apply.
//
// See GH#7754, GH#11096
return false;
}
SID_IDENTIFIER_AUTHORITY ntAuthority{ SECURITY_NT_AUTHORITY };
wil::unique_sid adminGroupSid{};
THROW_IF_WIN32_BOOL_FALSE(AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroupSid));
BOOL b;
THROW_IF_WIN32_BOOL_FALSE(CheckTokenMembership(NULL, adminGroupSid.get(), &b));
return !!b;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
return Microsoft::Console::Utils::IsElevated();
}
namespace winrt::TerminalApp::implementation
@ -210,7 +183,7 @@ namespace winrt::TerminalApp::implementation
}
AppLogic::AppLogic() :
_reloadState{ std::chrono::milliseconds(100), []() { ApplicationState::SharedInstance().Reload(); ElevatedState::SharedInstance().Reload(); } }
_reloadState{ std::chrono::milliseconds(100), []() { ApplicationState::SharedInstance().Reload(); } }
{
// For your own sanity, it's better to do setup outside the ctor.
// If you do any setup in the ctor that ends up throwing an exception,
@ -937,7 +910,6 @@ namespace winrt::TerminalApp::implementation
wil::FolderChangeEvents::FileName | wil::FolderChangeEvents::LastWriteTime,
[this, settingsPath](wil::FolderChangeEvent, PCWSTR fileModified) {
static const std::filesystem::path statePath{ std::wstring_view{ ApplicationState::SharedInstance().FilePath() } };
static const std::filesystem::path elevatedStatePath{ std::wstring_view{ ElevatedState::SharedInstance().FilePath() } };
const auto modifiedBasename = std::filesystem::path{ fileModified }.filename();
@ -945,7 +917,7 @@ namespace winrt::TerminalApp::implementation
{
_reloadSettings->Run();
}
else if (modifiedBasename == statePath.filename() || modifiedBasename == elevatedStatePath.filename())
else if (modifiedBasename == statePath.filename())
{
_reloadState();
}
@ -1509,9 +1481,9 @@ namespace winrt::TerminalApp::implementation
return _root->IsQuakeWindow();
}
bool AppLogic::GetMinimizeToTray()
bool AppLogic::GetMinimizeToNotificationArea()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (!_loadedInitialSettings)
{
@ -1519,7 +1491,7 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
return _settings.GlobalSettings().MinimizeToTray();
return _settings.GlobalSettings().MinimizeToNotificationArea();
}
else
{
@ -1527,9 +1499,9 @@ namespace winrt::TerminalApp::implementation
}
}
bool AppLogic::GetAlwaysShowTrayIcon()
bool AppLogic::GetAlwaysShowNotificationIcon()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (!_loadedInitialSettings)
{
@ -1537,7 +1509,7 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
return _settings.GlobalSettings().AlwaysShowTrayIcon();
return _settings.GlobalSettings().AlwaysShowNotificationIcon();
}
else
{

View file

@ -95,8 +95,8 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::TaskbarState TaskbarState();
bool GetMinimizeToTray();
bool GetAlwaysShowTrayIcon();
bool GetMinimizeToNotificationArea();
bool GetAlwaysShowNotificationIcon();
bool GetShowTitleInTitlebar();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);

View file

@ -73,8 +73,8 @@ namespace TerminalApp
TaskbarState TaskbarState{ get; };
Boolean GetMinimizeToTray();
Boolean GetAlwaysShowTrayIcon();
Boolean GetMinimizeToNotificationArea();
Boolean GetAlwaysShowNotificationIcon();
Boolean GetShowTitleInTitlebar();
FindTargetWindowResult FindTargetWindow(String[] args);

View file

@ -163,7 +163,8 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n
const auto terminalArgs{ newPane->GetTerminalArgsForPane() };
// When creating a pane the split size is the size of the new pane
// and not position.
SplitPaneArgs args{ SplitType::Manual, _splitState, 1. - _desiredSplitPosition, terminalArgs };
const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right;
SplitPaneArgs args{ SplitType::Manual, splitDirection, 1. - _desiredSplitPosition, terminalArgs };
actionAndArgs.Args(args);
return actionAndArgs;
@ -1355,7 +1356,7 @@ void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Pr
// - splitType: How the pane should be attached
// Return Value:
// - the new reference to the child created from the current pane.
std::shared_ptr<Pane> Pane::AttachPane(std::shared_ptr<Pane> pane, SplitState splitType)
std::shared_ptr<Pane> Pane::AttachPane(std::shared_ptr<Pane> pane, SplitDirection splitType)
{
// Splice the new pane into the tree
const auto [first, _] = _Split(splitType, .5, pane);
@ -2083,7 +2084,7 @@ void Pane::_SetupEntranceAnimation()
// Note:
// - This method is highly similar to Pane::PreCalculateAutoSplit
std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> target,
SplitState splitType,
SplitDirection splitType,
const float splitSize,
const winrt::Windows::Foundation::Size availableSpace) const
{
@ -2097,12 +2098,7 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
// the available space to calculate which direction to split in.
const Size minSize = _GetMinSize();
if (splitType == SplitState::None)
{
return { false };
}
else if (splitType == SplitState::Vertical)
if (splitType == SplitDirection::Left || splitType == SplitDirection::Right)
{
const auto widthMinusSeparator = availableSpace.Width - CombinedPaneBorderSize;
const auto newFirstWidth = widthMinusSeparator * firstPrecent;
@ -2111,7 +2107,7 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
return { newFirstWidth > minSize.Width && newSecondWidth > minSize.Width };
}
else if (splitType == SplitState::Horizontal)
else if (splitType == SplitDirection::Up || splitType == SplitDirection::Down)
{
const auto heightMinusSeparator = availableSpace.Height - CombinedPaneBorderSize;
const auto newFirstHeight = heightMinusSeparator * firstPrecent;
@ -2164,8 +2160,8 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
// - profile: The profile to associate with the newly created pane.
// - control: A UserControl to use in the new pane.
// Return Value:
// - The two newly created Panes
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState splitType,
// - The two newly created Panes, with the original pane first
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitDirection splitType,
const float splitSize,
const Profile& profile,
const Controls::UserControl& control)
@ -2228,18 +2224,18 @@ bool Pane::ToggleSplitOrientation()
// Method Description:
// - Converts an "automatic" split type into either Vertical or Horizontal,
// based upon the current dimensions of the Pane.
// - If any of the other SplitState values are passed in, they're returned
// unmodified.
// - Similarly, if Up/Down or Left/Right are provided a Horizontal or Vertical
// split type will be returned.
// Arguments:
// - splitType: The SplitState to attempt to convert
// - splitType: The SplitDirection to attempt to convert
// Return Value:
// - None if splitType was None, otherwise one of Horizontal or Vertical
SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
// - One of Horizontal or Vertical
SplitState Pane::_convertAutomaticOrDirectionalSplitState(const SplitDirection& splitType) const
{
// Careful here! If the pane doesn't yet have a size, these dimensions will
// be 0, and we'll always return Vertical.
if (splitType == SplitState::Automatic)
if (splitType == SplitDirection::Automatic)
{
// If the requested split type was "auto", determine which direction to
// split based on our current dimensions
@ -2247,7 +2243,12 @@ SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
gsl::narrow_cast<float>(_root.ActualHeight()) };
return actualSize.Width >= actualSize.Height ? SplitState::Vertical : SplitState::Horizontal;
}
return splitType;
if (splitType == SplitDirection::Up || splitType == SplitDirection::Down)
{
return SplitState::Horizontal;
}
// All that is left is Left / Right which are vertical splits
return SplitState::Vertical;
}
// Method Description:
@ -2258,17 +2259,12 @@ SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
// - splitSize: what fraction of the pane the new pane should get
// - newPane: the pane to add as a child
// Return Value:
// - The two newly created Panes
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState splitType,
// - The two newly created Panes, with the original pane as the first pane.
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane)
{
if (splitType == SplitState::None)
{
return { nullptr, nullptr };
}
auto actualSplitType = _convertAutomaticSplitState(splitType);
auto actualSplitType = _convertAutomaticOrDirectionalSplitState(splitType);
// Lock the create/close lock so that another operation won't concurrently
// modify our tree
@ -2303,9 +2299,16 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
// Move the new guid, control into the second.
_firstChild = std::make_shared<Pane>(_profile, _control);
_firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected);
_secondChild = newPane;
// If we want the new pane to be the first child, swap the children
if (splitType == SplitDirection::Up || splitType == SplitDirection::Left)
{
std::swap(_firstChild, _secondChild);
}
_profile = nullptr;
_control = { nullptr };
_secondChild = newPane;
_CreateRowColDefinitions();
@ -2324,6 +2327,12 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
// Clear out our ID, only leaves should have IDs
_id = {};
// Regardless of which child the new child is, we want to return the
// original one first.
if (splitType == SplitDirection::Up || splitType == SplitDirection::Left)
{
return { _secondChild, _firstChild };
}
return { _firstChild, _secondChild };
}
@ -2975,10 +2984,10 @@ int Pane::GetLeafPaneCount() const noexcept
// - availableSpace: The theoretical space that's available for this pane to be able to split.
// Return Value:
// - nullopt if `target` is not this pane or a child of this pane, otherwise the
// SplitState that `target` would use for an `Automatic` split given
// SplitDirection that `target` would use for an `Automatic` split given
// `availableSpace`
std::optional<SplitState> Pane::PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size availableSpace) const
std::optional<SplitDirection> Pane::PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size availableSpace) const
{
if (_IsLeaf())
{
@ -2986,7 +2995,7 @@ std::optional<SplitState> Pane::PreCalculateAutoSplit(const std::shared_ptr<Pane
{
//If this pane is a leaf, and it's the pane we're looking for, use
//the available space to calculate which direction to split in.
return availableSpace.Width > availableSpace.Height ? SplitState::Vertical : SplitState::Horizontal;
return availableSpace.Width > availableSpace.Height ? SplitDirection::Right : SplitDirection::Down;
}
else
{

View file

@ -44,6 +44,13 @@ enum class Borders : int
};
DEFINE_ENUM_FLAG_OPERATORS(Borders);
enum class SplitState : int
{
None = 0,
Horizontal = 1,
Vertical = 2
};
class Pane : public std::enable_shared_from_this<Pane>
{
public:
@ -94,23 +101,23 @@ public:
std::shared_ptr<Pane> NextPane(const std::shared_ptr<Pane> pane);
std::shared_ptr<Pane> PreviousPane(const std::shared_ptr<Pane> pane);
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Windows::UI::Xaml::Controls::UserControl& control);
bool ToggleSplitOrientation();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size parentSize) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size parentSize) const;
std::optional<bool> PreCalculateCanSplit(const std::shared_ptr<Pane> target,
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
const winrt::Windows::Foundation::Size availableSpace) const;
void Shutdown();
void Close();
std::shared_ptr<Pane> AttachPane(std::shared_ptr<Pane> pane,
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType);
std::shared_ptr<Pane> DetachPane(std::shared_ptr<Pane> pane);
winrt::Windows::UI::Xaml::Controls::UserControl ReplaceControl(const winrt::Windows::UI::Xaml::Controls::UserControl& control);
@ -177,7 +184,7 @@ private:
std::shared_ptr<Pane> _firstChild{ nullptr };
std::shared_ptr<Pane> _secondChild{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::SplitState _splitState{ winrt::Microsoft::Terminal::Settings::Model::SplitState::None };
SplitState _splitState{ SplitState::None };
float _desiredSplitPosition;
std::optional<uint32_t> _id;
@ -202,7 +209,7 @@ private:
bool _HasFocusedChild() const noexcept;
void _SetupChildCloseHandlers();
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane);
@ -245,9 +252,9 @@ private:
LayoutSizeNode _CreateMinSizeTree(const bool widthOrHeight) const;
float _ClampSplitPosition(const bool widthOrHeight, const float requestedValue, const float totalSize) const;
winrt::Microsoft::Terminal::Settings::Model::SplitState _convertAutomaticSplitState(const winrt::Microsoft::Terminal::Settings::Model::SplitState& splitType) const;
SplitState _convertAutomaticOrDirectionalSplitState(const winrt::Microsoft::Terminal::Settings::Model::SplitDirection& splitType) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> _preCalculateAutoSplit(const std::shared_ptr<Pane> target, const winrt::Windows::Foundation::Size parentSize) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> _preCalculateAutoSplit(const std::shared_ptr<Pane> target, const winrt::Windows::Foundation::Size parentSize) const;
// Function Description:
// - Returns true if the given direction can be used with the given split
@ -258,24 +265,24 @@ private:
// - Also used for moving focus between panes, which again happens _across_ a separator.
// Arguments:
// - direction: The Direction to compare
// - splitType: The winrt::TerminalApp::SplitState to compare
// - splitType: The SplitState to compare
// Return Value:
// - true iff the direction is perpendicular to the splitType. False for
// winrt::TerminalApp::SplitState::None.
// SplitState::None.
template<typename T>
static constexpr bool DirectionMatchesSplit(const T& direction,
const winrt::Microsoft::Terminal::Settings::Model::SplitState& splitType)
const SplitState& splitType)
{
if (splitType == winrt::Microsoft::Terminal::Settings::Model::SplitState::None)
if (splitType == SplitState::None)
{
return false;
}
else if (splitType == winrt::Microsoft::Terminal::Settings::Model::SplitState::Horizontal)
else if (splitType == SplitState::Horizontal)
{
return direction == T::Up ||
direction == T::Down;
}
else if (splitType == winrt::Microsoft::Terminal::Settings::Model::SplitState::Vertical)
else if (splitType == SplitState::Vertical)
{
return direction == T::Left ||
direction == T::Right;

View file

@ -680,11 +680,11 @@
<data name="CommandPaletteMenuItem" xml:space="preserve">
<value>Command Palette</value>
</data>
<data name="TrayIconFocusTerminal" xml:space="preserve">
<data name="NotificationIconFocusTerminal" xml:space="preserve">
<value>Focus Terminal</value>
<comment>This is displayed as a label for the context menu item that focuses the terminal.</comment>
</data>
<data name="TrayIconWindowSubmenu" xml:space="preserve">
<data name="NotificationIconWindowSubmenu" xml:space="preserve">
<value>Windows</value>
<comment>This is displayed as a label for the context menu item that holds the submenu of available windows.</comment>
</data>

View file

@ -301,7 +301,7 @@ namespace winrt::TerminalApp::implementation
auto newControl = _InitControl(settings, debugConnection);
_RegisterTerminalEvents(newControl);
// Split (auto) with the debug tap.
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profile, newControl);
newTabImpl->SplitPane(SplitDirection::Automatic, 0.5f, profile, newControl);
}
}
@ -411,7 +411,7 @@ namespace winrt::TerminalApp::implementation
try
{
_SetFocusedTab(tab);
_SplitPane(tab, SplitState::Automatic, SplitType::Duplicate);
_SplitPane(tab, SplitDirection::Automatic, SplitType::Duplicate);
}
CATCH_LOG();
}

View file

@ -21,14 +21,12 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
<ItemDefinitionGroup>
<ClCompile>
<!-- For CLI11: It uses dynamic_cast to cast types around, which depends
on being compiled with RTTI (/GR). -->
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
</ItemDefinitionGroup>
<!-- ========================= XAML files ======================== -->
<ItemGroup>
<!-- HERE BE DRAGONS:
@ -247,7 +245,6 @@
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="Toast.cpp" />
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
@ -392,7 +389,6 @@
</ItemDefinitionGroup>
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
@ -401,8 +397,8 @@
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets'))" />
</Target>
<!--
By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
@ -422,6 +418,5 @@
</PackagingOutputs>
</ItemGroup>
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>

View file

@ -842,7 +842,7 @@ namespace winrt::TerminalApp::implementation
if (altPressed && !debugTap)
{
this->_SplitPane(SplitState::Automatic,
this->_SplitPane(SplitDirection::Automatic,
SplitType::Manual,
0.5f,
newTerminalArgs);
@ -1720,25 +1720,18 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Split the focused pane either horizontally or vertically, and place the
// given TermControl into the newly created pane.
// - If splitType == SplitState::None, this method does nothing.
// Arguments:
// - splitType: one value from the TerminalApp::SplitState enum, indicating how the
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
// new pane should be split from its parent.
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
// - newTerminalArgs: An object that may contain a blob of parameters to
// control which profile is created and with possible other
// configurations. See CascadiaSettings::BuildSettings for more details.
void TerminalPage::_SplitPane(const SplitState splitType,
void TerminalPage::_SplitPane(const SplitDirection splitDirection,
const SplitType splitMode,
const float splitSize,
const NewTerminalArgs& newTerminalArgs)
{
// Do nothing if we're requesting no split.
if (splitType == SplitState::None)
{
return;
}
const auto focusedTab{ _GetFocusedTabImpl() };
// Do nothing if no TerminalTab is focused
@ -1747,33 +1740,26 @@ namespace winrt::TerminalApp::implementation
return;
}
_SplitPane(*focusedTab, splitType, splitMode, splitSize, newTerminalArgs);
_SplitPane(*focusedTab, splitDirection, splitMode, splitSize, newTerminalArgs);
}
// Method Description:
// - Split the focused pane of the given tab, either horizontally or vertically, and place the
// given TermControl into the newly created pane.
// - If splitType == SplitState::None, this method does nothing.
// Arguments:
// - tab: The tab that is going to be split.
// - splitType: one value from the TerminalApp::SplitState enum, indicating how the
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
// new pane should be split from its parent.
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
// - newTerminalArgs: An object that may contain a blob of parameters to
// control which profile is created and with possible other
// configurations. See CascadiaSettings::BuildSettings for more details.
void TerminalPage::_SplitPane(TerminalTab& tab,
const SplitState splitType,
const SplitDirection splitDirection,
const SplitType splitMode,
const float splitSize,
const NewTerminalArgs& newTerminalArgs)
{
// Do nothing if we're requesting no split.
if (splitType == SplitState::None)
{
return;
}
try
{
TerminalSettingsCreateResult controlSettings{ nullptr };
@ -1819,8 +1805,8 @@ namespace winrt::TerminalApp::implementation
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
auto realSplitType = splitType;
if (realSplitType == SplitState::Automatic)
auto realSplitType = splitDirection;
if (realSplitType == SplitDirection::Automatic)
{
realSplitType = tab.PreCalculateAutoSplit(availableSpace);
}

View file

@ -271,12 +271,12 @@ namespace winrt::TerminalApp::implementation
void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference<uint32_t>& rowsToScroll);
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitState splitType,
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
const float splitSize = 0.5f,
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
void _SplitPane(TerminalTab& tab,
const Microsoft::Terminal::Settings::Model::SplitState splitType,
const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
const float splitSize = 0.5f,
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);

View file

@ -493,41 +493,44 @@ namespace winrt::TerminalApp::implementation
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void TerminalTab::SplitPane(SplitState splitType,
void TerminalTab::SplitPane(SplitDirection splitType,
const float splitSize,
const Profile& profile,
const WUX::Controls::UserControl& control)
{
// Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID
const auto activePaneId = _activePane->Id();
auto [first, second] = _activePane->Split(splitType, splitSize, profile, control);
// Depending on which direction will be split, the new pane can be
// either the first or second child, but this will always return the
// original pane first.
auto [original, newPane] = _activePane->Split(splitType, splitSize, profile, control);
if (activePaneId)
{
first->Id(activePaneId.value());
second->Id(_nextPaneId);
original->Id(activePaneId.value());
newPane->Id(_nextPaneId);
++_nextPaneId;
}
else
{
first->Id(_nextPaneId);
original->Id(_nextPaneId);
++_nextPaneId;
second->Id(_nextPaneId);
newPane->Id(_nextPaneId);
++_nextPaneId;
}
_activePane = first;
_activePane = original;
// Add a event handlers to the new panes' GotFocus event. When the pane
// gains focus, we'll mark it as the new active pane.
const auto& termControl{ control.try_as<TermControl>() };
_AttachEventHandlersToControl(second->Id().value(), termControl);
_AttachEventHandlersToPane(first);
_AttachEventHandlersToPane(second);
_AttachEventHandlersToControl(newPane->Id().value(), termControl);
_AttachEventHandlersToPane(original);
_AttachEventHandlersToPane(newPane);
// Immediately update our tracker of the focused pane now. If we're
// splitting panes during startup (from a commandline), then it's
// possible that the focus events won't propagate immediately. Updating
// the focus here will give the same effect though.
_UpdateActivePane(second);
_UpdateActivePane(newPane);
}
// Method Description:
@ -613,7 +616,7 @@ namespace winrt::TerminalApp::implementation
const auto previousId = _activePane->Id();
// Add the new pane as an automatic split on the active pane.
auto first = _activePane->AttachPane(pane, SplitState::Automatic);
auto first = _activePane->AttachPane(pane, SplitDirection::Automatic);
// under current assumptions this condition should always be true.
if (previousId)
@ -1528,14 +1531,14 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - availableSpace: The theoretical space that's available for this Tab's content
// Return Value:
// - The SplitState that we should use for an `Automatic` split given
// - The SplitDirection that we should use for an `Automatic` split given
// `availableSpace`
SplitState TerminalTab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
SplitDirection TerminalTab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
{
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical);
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitDirection::Right);
}
bool TerminalTab::PreCalculateCanSplit(SplitState splitType,
bool TerminalTab::PreCalculateCanSplit(SplitDirection splitType,
const float splitSize,
winrt::Windows::Foundation::Size availableSpace) const
{

View file

@ -39,7 +39,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> DetachPane();
void AttachPane(std::shared_ptr<Pane> pane);
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Windows::UI::Xaml::Controls::UserControl& control);
@ -52,8 +52,8 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget ActivateBellIndicatorTimer();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
winrt::Microsoft::Terminal::Settings::Model::SplitState PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const;
bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
winrt::Microsoft::Terminal::Settings::Model::SplitDirection PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const;
bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
winrt::Windows::Foundation::Size availableSpace) const;

View file

@ -199,8 +199,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
bool GlobalAppearance::FeatureTrayIconEnabled() const noexcept
bool GlobalAppearance::FeatureNotificationIconEnabled() const noexcept
{
return Feature_TrayIcon::IsEnabled();
return Feature_NotificationIcon::IsEnabled();
}
}

View file

@ -25,7 +25,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
bool FeatureTrayIconEnabled() const noexcept;
bool FeatureNotificationIconEnabled() const noexcept;
WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr);
GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals, Theme);

View file

@ -26,6 +26,6 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentTabWidthMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };
Boolean FeatureTrayIconEnabled { get; };
Boolean FeatureNotificationIconEnabled { get; };
}
}

View file

@ -85,16 +85,16 @@
<ToggleSwitch IsOn="{x:Bind local:Converters.InvertBoolean(State.Globals.DisableAnimations), BindBack=State.Globals.SetInvertedDisableAnimationsValue, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Always Show Tray Icon -->
<local:SettingContainer x:Uid="Globals_AlwaysShowTrayIcon"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowTrayIcon, Mode=TwoWay}" />
<!-- Always Show Notification Icon -->
<local:SettingContainer x:Uid="Globals_AlwaysShowNotificationIcon"
Visibility="{x:Bind FeatureNotificationIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowNotificationIcon, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Minimize To Tray -->
<local:SettingContainer x:Uid="Globals_MinimizeToTray"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.MinimizeToTray, Mode=TwoWay}" />
<!-- Minimize To Notification Area -->
<local:SettingContainer x:Uid="Globals_MinimizeToNotificationArea"
Visibility="{x:Bind FeatureNotificationIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.MinimizeToNotificationArea, Mode=TwoWay}" />
</local:SettingContainer>
</StackPanel>
</ScrollViewer>

View file

@ -1106,13 +1106,13 @@
<value>Pane animations</value>
<comment>Header for a control to toggle animations on panes. "Enabled" value enables the animations.</comment>
</data>
<data name="Globals_AlwaysShowTrayIcon.Header" xml:space="preserve">
<data name="Globals_AlwaysShowNotificationIcon.Header" xml:space="preserve">
<value>Always display an icon in the notification area</value>
<comment>Header for a control to toggle whether the tray icon should always be shown.</comment>
<comment>Header for a control to toggle whether the notification icon should always be shown.</comment>
</data>
<data name="Globals_MinimizeToTray.Header" xml:space="preserve">
<data name="Globals_MinimizeToNotificationArea.Header" xml:space="preserve">
<value>Hide Terminal in the notification area when it is minimized</value>
<comment>Header for a control to toggle whether the terminal should hide itself in the tray instead of the taskbar when minimized.</comment>
<comment>Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized.</comment>
</data>
<data name="SettingContainer_OverrideMessageBaseLayer" xml:space="preserve">
<value>Reset to inherited value.</value>
@ -1234,4 +1234,4 @@
<value>Bold font with bright colors</value>
<comment>An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color</comment>
</data>
</root>
</root>

View file

@ -399,13 +399,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// This text is intentionally _not_ localized, to attempt to mirror the
// exact syntax that the property would have in JSON.
switch (SplitStyle())
switch (SplitDirection())
{
case SplitState::Vertical:
ss << L"split: vertical, ";
case SplitDirection::Up:
ss << L"split: up, ";
break;
case SplitState::Horizontal:
ss << L"split: horizontal, ";
case SplitDirection::Right:
ss << L"split: right, ";
break;
case SplitDirection::Down:
ss << L"split: down, ";
break;
case SplitDirection::Left:
ss << L"split: left, ";
break;
}

View file

@ -674,21 +674,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
{
SplitPaneArgs() = default;
SplitPaneArgs(SplitType splitMode, SplitState style, double size, const Model::NewTerminalArgs& terminalArgs) :
SplitPaneArgs(SplitType splitMode, SplitDirection direction, double size, const Model::NewTerminalArgs& terminalArgs) :
_SplitMode{ splitMode },
_SplitStyle{ style },
_SplitDirection{ direction },
_SplitSize{ size },
_TerminalArgs{ terminalArgs } {};
SplitPaneArgs(SplitState style, double size, const Model::NewTerminalArgs& terminalArgs) :
_SplitStyle{ style },
SplitPaneArgs(SplitDirection direction, double size, const Model::NewTerminalArgs& terminalArgs) :
_SplitDirection{ direction },
_SplitSize{ size },
_TerminalArgs{ terminalArgs } {};
SplitPaneArgs(SplitState style, const Model::NewTerminalArgs& terminalArgs) :
_SplitStyle{ style },
SplitPaneArgs(SplitDirection direction, const Model::NewTerminalArgs& terminalArgs) :
_SplitDirection{ direction },
_TerminalArgs{ terminalArgs } {};
SplitPaneArgs(SplitType splitMode) :
_SplitMode{ splitMode } {};
ACTION_ARG(SplitState, SplitStyle, SplitState::Automatic);
ACTION_ARG(Model::SplitDirection, SplitDirection, SplitDirection::Automatic);
WINRT_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr);
ACTION_ARG(SplitType, SplitMode, SplitType::Manual);
ACTION_ARG(double, SplitSize, .5);
@ -705,7 +705,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto otherAsUs = other.try_as<SplitPaneArgs>();
if (otherAsUs)
{
return otherAsUs->_SplitStyle == _SplitStyle &&
return otherAsUs->_SplitDirection == _SplitDirection &&
(otherAsUs->_TerminalArgs ? otherAsUs->_TerminalArgs.Equals(_TerminalArgs) :
otherAsUs->_TerminalArgs == _TerminalArgs) &&
otherAsUs->_SplitSize == _SplitSize &&
@ -718,7 +718,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<SplitPaneArgs>();
args->_TerminalArgs = NewTerminalArgs::FromJson(json);
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitStyle);
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitDirection);
JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode);
JsonUtils::GetValueForKey(json, SplitSizeKey, args->_SplitSize);
if (args->SplitSize() >= 1 || args->SplitSize() <= 0)
@ -735,7 +735,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
const auto args{ get_self<SplitPaneArgs>(val) };
auto json{ NewTerminalArgs::ToJson(args->_TerminalArgs) };
JsonUtils::SetValueForKey(json, SplitKey, args->_SplitStyle);
JsonUtils::SetValueForKey(json, SplitKey, args->_SplitDirection);
JsonUtils::SetValueForKey(json, SplitModeKey, args->_SplitMode);
JsonUtils::SetValueForKey(json, SplitSizeKey, args->_SplitSize);
return json;
@ -743,7 +743,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
IActionArgs Copy() const
{
auto copy{ winrt::make_self<SplitPaneArgs>() };
copy->_SplitStyle = _SplitStyle;
copy->_SplitDirection = _SplitDirection;
copy->_TerminalArgs = _TerminalArgs.Copy();
copy->_SplitMode = _SplitMode;
copy->_SplitSize = _SplitSize;
@ -751,7 +751,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SplitStyle(), TerminalArgs(), SplitMode(), SplitSize());
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SplitDirection(), TerminalArgs(), SplitMode(), SplitSize());
}
};
@ -1417,7 +1417,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
Json::Value json{ Json::ValueType::objectValue };
const auto args{ get_self<FindMatchArgs>(val) };
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
JsonUtils::SetValueForKey(json, DirectionKey, args->_Direction);
return json;
}
IActionArgs Copy() const

View file

@ -41,12 +41,13 @@ namespace Microsoft.Terminal.Settings.Model
First
};
enum SplitState
enum SplitDirection
{
Automatic = -1,
None = 0,
Vertical = 1,
Horizontal = 2
Automatic = 0,
Up,
Right,
Down,
Left
};
enum SplitType
@ -187,12 +188,12 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass SplitPaneArgs : IActionArgs
{
SplitPaneArgs(SplitType splitMode, SplitState split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitState split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitState split, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitType splitMode, SplitDirection split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitDirection split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitDirection split, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitType splitMode);
SplitState SplitStyle { get; };
SplitDirection SplitDirection { get; };
NewTerminalArgs TerminalArgs { get; };
SplitType SplitMode { get; };
Double SplitSize { get; };

View file

@ -9,8 +9,11 @@
#include "ActionAndArgs.h"
#include "JsonUtils.h"
#include "FileUtils.h"
#include "../../types/inc/utils.hpp"
static constexpr std::wstring_view stateFileName{ L"state.json" };
static constexpr std::wstring_view elevatedStateFileName{ L"elevated-state.json" };
static constexpr std::string_view TabLayoutKey{ "tabLayout" };
static constexpr std::string_view InitialPositionKey{ "initialPosition" };
static constexpr std::string_view InitialSizeKey{ "initialSize" };
@ -66,7 +69,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// Returns the application-global ApplicationState object.
Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance()
{
static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath() / stateFileName);
static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath() / (::Microsoft::Console::Utils::IsElevated() ? elevatedStateFileName : stateFileName));
state->Reload();
return *state;
}
@ -117,4 +120,29 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
void ApplicationState::_writeFileContents(const std::string_view content) const
{
if (::Microsoft::Console::Utils::IsElevated())
{
// DON'T use WriteUTF8FileAtomic, which will write to a temporary file
// then rename that file to the final filename. That actually lets us
// overwrite the elevate file's contents even when unelevated, because
// we're effectively deleting the original file, then renaming a
// different file in it's place.
//
// We're not worried about someone else doing that though, if they do
// that with the wrong permissions, then we'll just ignore the file and
// start over.
WriteUTF8File(_path, content, true);
}
else
{
WriteUTF8FileAtomic(_path, content);
}
}
std::optional<std::string> ApplicationState::_readFileContents() const
{
return ReadUTF8FileIfExists(_path, ::Microsoft::Console::Utils::IsElevated());
}
}

View file

@ -24,11 +24,12 @@ Abstract:
// (type, function name, JSON key, ...variadic construction arguments)
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
X(Windows::Foundation::Collections::IVector<Model::WindowLayout>, PersistedWindowLayouts, "persistedWindowLayouts") \
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands") \
X(Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage>, DismissedMessages, "dismissedMessages")
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
X(Windows::Foundation::Collections::IVector<Model::WindowLayout>, PersistedWindowLayouts, "persistedWindowLayouts") \
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands") \
X(Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage>, DismissedMessages, "dismissedMessages") \
X(Windows::Foundation::Collections::IVector<hstring>, AllowedCommandlines, "allowedCommandlines")
struct WindowLayout : WindowLayoutT<WindowLayout>
{
@ -63,6 +64,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
#undef MTSM_APPLICATION_STATE_GEN
};
til::shared_mutex<state_t> _state;
virtual std::optional<std::string> _readFileContents() const override;
virtual void _writeFileContents(const std::string_view content) const override;
};
}

View file

@ -32,5 +32,8 @@ namespace Microsoft.Terminal.Settings.Model
Windows.Foundation.Collections.IVector<String> RecentCommands { get; set; };
Windows.Foundation.Collections.IVector<InfoBarMessage> DismissedMessages { get; set; };
Windows.Foundation.Collections.IVector<String> AllowedCommandlines { get; set; };
}
}

View file

@ -8,7 +8,6 @@
#include "JsonUtils.h"
#include "FileUtils.h"
constexpr std::wstring_view stateFileName{ L"state.json" };
using namespace ::Microsoft::Terminal::Settings::Model;
BaseApplicationState::BaseApplicationState(std::filesystem::path path) noexcept :
@ -85,13 +84,3 @@ try
_writeFileContents(content);
}
CATCH_LOG()
std::optional<std::string> BaseApplicationState::_readFileContents() const
{
return ReadUTF8FileIfExists(_path);
}
void BaseApplicationState::_writeFileContents(const std::string_view content) const
{
WriteUTF8FileAtomic(_path, content);
}

View file

@ -27,8 +27,8 @@ struct BaseApplicationState
virtual Json::Value ToJson() const noexcept = 0;
protected:
virtual std::optional<std::string> _readFileContents() const;
virtual void _writeFileContents(const std::string_view content) const;
virtual std::optional<std::string> _readFileContents() const = 0;
virtual void _writeFileContents(const std::string_view content) const = 0;
void _write() const noexcept;
void _read() const noexcept;

View file

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "BaseVisualStudioGenerator.h"
#include "DefaultProfileUtils.h"
using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model;
std::vector<Profile> BaseVisualStudioGenerator::GenerateProfiles()
{
std::vector<Profile> profiles;
// There's no point in enumerating valid Visual Studio instances more than once,
// so cache them for use by both Visual Studio profile generators.
static const auto instances = VsSetupConfiguration::QueryInstances();
for (auto const& instance : instances)
{
try
{
if (!IsInstanceValid(instance))
{
continue;
}
auto DevShell{ CreateProfile(GetProfileGuidSeed(instance)) };
DevShell.Name(GetProfileName(instance));
DevShell.Commandline(GetProfileCommandLine(instance));
DevShell.StartingDirectory(instance.GetInstallationPath());
DevShell.Icon(GetProfileIconPath());
profiles.emplace_back(DevShell);
}
CATCH_LOG();
}
return profiles;
}
Profile BaseVisualStudioGenerator::CreateProfile(const std::wstring_view seed)
{
const winrt::guid profileGuid{ Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID,
gsl::as_bytes(gsl::make_span(seed))) };
auto newProfile = winrt::make_self<implementation::Profile>(profileGuid);
newProfile->Origin(OriginTag::Generated);
return *newProfile;
}

View file

@ -0,0 +1,40 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- BaseVisualStudioGenerator
Abstract:
- Base generator for Visual Studio Developer shell profiles
Author(s):
- Charles Willis - October 2020
--*/
#pragma once
#include "IDynamicProfileGenerator.h"
#include "VsSetupConfiguration.h"
namespace Microsoft::Terminal::Settings::Model
{
class BaseVisualStudioGenerator : public IDynamicProfileGenerator
{
public:
// Inherited via IDynamicProfileGenerator
std::wstring_view GetNamespace() override = 0;
std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() override;
protected:
virtual bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance& instance) const = 0;
virtual std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const = 0;
virtual std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const = 0;
virtual std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const = 0;
virtual std::wstring GetProfileIconPath() const = 0;
private:
winrt::Microsoft::Terminal::Settings::Model::Profile CreateProfile(const std::wstring_view instanceId);
};
};

View file

@ -9,6 +9,8 @@
#include "AzureCloudShellGenerator.h"
#include "PowershellCoreProfileGenerator.h"
#include "VsDevCmdGenerator.h"
#include "VsDevShellGenerator.h"
#include "WslDistroGenerator.h"
using namespace ::Microsoft::Terminal::Settings::Model;
@ -51,6 +53,8 @@ CascadiaSettings::CascadiaSettings(const bool addDynamicProfiles) :
_profileGenerators.emplace_back(std::make_unique<PowershellCoreProfileGenerator>());
_profileGenerators.emplace_back(std::make_unique<WslDistroGenerator>());
_profileGenerators.emplace_back(std::make_unique<AzureCloudShellGenerator>());
_profileGenerators.emplace_back(std::make_unique<VsDevCmdGenerator>());
_profileGenerators.emplace_back(std::make_unique<VsDevShellGenerator>());
}
}

View file

@ -10,6 +10,5 @@ namespace Microsoft.Terminal.Settings.Model
String FilePath { get; };
Windows.Foundation.Collections.IVector<String> AllowedCommandlines { get; set; };
}
}

View file

@ -50,8 +50,8 @@ static constexpr std::string_view StartupActionsKey{ "startupActions" };
static constexpr std::string_view FocusFollowMouseKey{ "focusFollowMouse" };
static constexpr std::string_view WindowingBehaviorKey{ "windowingBehavior" };
static constexpr std::string_view TrimBlockSelectionKey{ "trimBlockSelection" };
static constexpr std::string_view AlwaysShowTrayIconKey{ "alwaysShowTrayIcon" };
static constexpr std::string_view MinimizeToTrayKey{ "minimizeToTray" };
static constexpr std::string_view AlwaysShowNotificationIconKey{ "alwaysShowNotificationIcon" };
static constexpr std::string_view MinimizeToNotificationAreaKey{ "minimizeToNotificationArea" };
static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };
@ -135,8 +135,8 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_WindowingBehavior = _WindowingBehavior;
globals->_TrimBlockSelection = _TrimBlockSelection;
globals->_DetectURLs = _DetectURLs;
globals->_MinimizeToTray = _MinimizeToTray;
globals->_AlwaysShowTrayIcon = _AlwaysShowTrayIcon;
globals->_MinimizeToNotificationArea = _MinimizeToNotificationArea;
globals->_AlwaysShowNotificationIcon = _AlwaysShowNotificationIcon;
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
globals->_validDefaultProfile = _validDefaultProfile;
@ -331,9 +331,9 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, DetectURLsKey, _DetectURLs);
JsonUtils::GetValueForKey(json, MinimizeToTrayKey, _MinimizeToTray);
JsonUtils::GetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea);
JsonUtils::GetValueForKey(json, AlwaysShowTrayIconKey, _AlwaysShowTrayIcon);
JsonUtils::GetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon);
// This is a helper lambda to get the keybindings and commands out of both
// and array of objects. We'll use this twice, once on the legacy
@ -432,8 +432,8 @@ Json::Value GlobalAppSettings::ToJson() const
JsonUtils::SetValueForKey(json, WindowingBehaviorKey, _WindowingBehavior);
JsonUtils::SetValueForKey(json, TrimBlockSelectionKey, _TrimBlockSelection);
JsonUtils::SetValueForKey(json, DetectURLsKey, _DetectURLs);
JsonUtils::SetValueForKey(json, MinimizeToTrayKey, _MinimizeToTray);
JsonUtils::SetValueForKey(json, AlwaysShowTrayIconKey, _AlwaysShowTrayIcon);
JsonUtils::SetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea);
JsonUtils::SetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon);
// clang-format on
json[JsonKey(ActionsKey)] = _actionMap->ToJson();

View file

@ -100,8 +100,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::WindowingMode, WindowingBehavior, Model::WindowingMode::UseNew);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, TrimBlockSelection, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DetectURLs, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, MinimizeToTray, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowTrayIcon, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, MinimizeToNotificationArea, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowNotificationIcon, false);
private:
guid _defaultProfile;

View file

@ -81,8 +81,8 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(WindowingMode, WindowingBehavior);
INHERITABLE_SETTING(Boolean, TrimBlockSelection);
INHERITABLE_SETTING(Boolean, DetectURLs);
INHERITABLE_SETTING(Boolean, MinimizeToTray);
INHERITABLE_SETTING(Boolean, AlwaysShowTrayIcon);
INHERITABLE_SETTING(Boolean, MinimizeToNotificationArea);
INHERITABLE_SETTING(Boolean, AlwaysShowNotificationIcon);
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);

View file

@ -12,9 +12,9 @@
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="BaseVisualStudioGenerator.h" />
<ClInclude Include="DefaultTerminal.h">
<DependentUpon>DefaultTerminal.idl</DependentUpon>
</ClInclude>
@ -36,9 +36,6 @@
<ClInclude Include="ApplicationState.h">
<DependentUpon>ApplicationState.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ElevatedState.h">
<DependentUpon>ElevatedState.idl</DependentUpon>
</ClInclude>
<ClInclude Include="CascadiaSettings.h">
<DependentUpon>CascadiaSettings.idl</DependentUpon>
</ClInclude>
@ -81,10 +78,14 @@
<ClInclude Include="TerminalWarnings.h">
<DependentUpon>TerminalWarnings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="VsDevCmdGenerator.h" />
<ClInclude Include="VsDevShellGenerator.h" />
<ClInclude Include="VsSetupConfiguration.h" />
<ClInclude Include="WslDistroGenerator.h" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="BaseVisualStudioGenerator.cpp" />
<ClCompile Include="DefaultTerminal.cpp">
<DependentUpon>DefaultTerminal.idl</DependentUpon>
</ClCompile>
@ -112,9 +113,6 @@
<ClCompile Include="ApplicationState.cpp">
<DependentUpon>ApplicationState.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ElevatedState.cpp">
<DependentUpon>ElevatedState.idl</DependentUpon>
</ClCompile>
<ClCompile Include="CascadiaSettings.cpp">
<DependentUpon>CascadiaSettings.idl</DependentUpon>
</ClCompile>
@ -151,6 +149,9 @@
<ClCompile Include="EnumMappings.cpp">
<DependentUpon>EnumMappings.idl</DependentUpon>
</ClCompile>
<ClCompile Include="VsDevCmdGenerator.cpp" />
<ClCompile Include="VsDevShellGenerator.cpp" />
<ClCompile Include="VsSetupConfiguration.cpp" />
<ClCompile Include="WslDistroGenerator.cpp" />
<!-- You _NEED_ to include this file and the jsoncpp IncludePath (below) if
you want to use jsoncpp -->
@ -164,7 +165,6 @@
<Midl Include="ActionArgs.idl" />
<Midl Include="ActionMap.idl" />
<Midl Include="ApplicationState.idl" />
<Midl Include="ElevatedState.idl" />
<Midl Include="CascadiaSettings.idl" />
<Midl Include="ColorScheme.idl" />
<Midl Include="Command.idl" />
@ -243,13 +243,13 @@
</ItemDefinitionGroup>
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets'))" />
</Target>
<!-- This target will take our defaults.json and stamp it into a .h file that
we can include in the code directly. This way, we don't need to worry about
@ -265,6 +265,6 @@
<Target Name="_TerminalAppGenerateUserSettingsH" Inputs="userDefaults.json" Outputs="Generated Files\userDefaults.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile userDefaults.json -OutPath '&quot;Generated Files\userDefaults.h&quot;' -VariableName UserSettingsJson" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
<Import Project="..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="Exists('..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />
</Project>

View file

@ -34,6 +34,18 @@
<ClCompile Include="IconPathConverter.cpp" />
<ClCompile Include="DefaultTerminal.cpp" />
<ClCompile Include="FileUtils.cpp" />
<ClCompile Include="BaseVisualStudioGenerator.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
<ClCompile Include="VsDevCmdGenerator.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
<ClCompile Include="VsDevShellGenerator.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
<ClCompile Include="VsSetupConfiguration.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@ -68,6 +80,18 @@
<ClInclude Include="DefaultTerminal.h" />
<ClInclude Include="FileUtils.h" />
<ClInclude Include="HashUtils.h" />
<ClInclude Include="BaseVisualStudioGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="VsDevCmdGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="VsDevShellGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="VsSetupConfiguration.h">
<Filter>profileGeneration</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="ActionArgs.idl" />

View file

@ -469,9 +469,6 @@
<value>Windows Console Host</value>
<comment>Name describing the usage of the classic windows console as the terminal UI. (`conhost.exe`)</comment>
</data>
<data name="MinimizeToTrayCommandKey" xml:space="preserve">
<value>Minimize current window to tray</value>
</data>
<data name="QuitCommandKey" xml:space="preserve">
<value>Quit the Terminal</value>
</data>

View file

@ -369,12 +369,16 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::ResizeDirection)
};
// Possible SplitState values
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SplitState)
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SplitDirection)
{
JSON_MAPPINGS(3) = {
pair_type{ "vertical", ValueType::Vertical },
pair_type{ "horizontal", ValueType::Horizontal },
JSON_MAPPINGS(7) = {
pair_type{ "auto", ValueType::Automatic },
pair_type{ "up", ValueType::Up },
pair_type{ "right", ValueType::Right },
pair_type{ "down", ValueType::Down },
pair_type{ "left", ValueType::Left },
pair_type{ "vertical", ValueType::Right },
pair_type{ "horizontal", ValueType::Down },
};
};

View file

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "VsDevCmdGenerator.h"
using namespace Microsoft::Terminal::Settings::Model;
std::wstring VsDevCmdGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const
{
std::wstring name{ L"Developer Command Prompt for VS " };
name.append(instance.GetProfileNameSuffix());
return name;
}
std::wstring VsDevCmdGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const
{
std::wstring commandLine{ L"cmd.exe /k \"" + instance.GetDevCmdScriptPath() + L"\"" };
return commandLine;
}

View file

@ -0,0 +1,50 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- VsDevCmdGenerator
Abstract:
- Dynamic profile generator for Visual Studio Developer Command Prompt
Author(s):
- Charles Willis - October 2020
--*/
#pragma once
#include "BaseVisualStudioGenerator.h"
namespace Microsoft::Terminal::Settings::Model
{
class VsDevCmdGenerator : public BaseVisualStudioGenerator
{
public:
std::wstring_view GetNamespace() override
{
return std::wstring_view{ L"Windows.Terminal.VisualStudio.CommandPrompt" };
}
bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance&) const override
{
// We only support version of VS from 15.0.
// Per heaths: The [ISetupConfiguration] COM server only supports Visual Studio 15.0 and newer anyway.
// Eliding the version range will improve the discovery performance by not having to parse or compare the versions.
return true;
}
std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const override
{
return L"VsDevCmd" + instance.GetInstanceId();
}
std::wstring GetProfileIconPath() const override
{
return L"ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png";
}
std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const override;
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const override;
};
};

View file

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "VsDevShellGenerator.h"
#include "VsSetupConfiguration.h"
using namespace Microsoft::Terminal::Settings::Model;
std::wstring VsDevShellGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const
{
std::wstring name{ L"Developer PowerShell for VS " };
name.append(instance.GetProfileNameSuffix());
return name;
}
std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const
{
// The triple-quotes are a PowerShell path escape sequence that can safely be stored in a JSON object.
// The "SkipAutomaticLocation" parameter will prevent "Enter-VsDevShell" from automatically setting the shell path
// so the path in the profile will be used instead.
std::wstring commandLine{ L"powershell.exe -NoExit -Command \"& {" };
commandLine.append(L"Import-Module \"\"\"" + instance.GetDevShellModulePath() + L"\"\"\";");
commandLine.append(L"Enter-VsDevShell " + instance.GetInstanceId() + L" -SkipAutomaticLocation");
commandLine.append(L"}\"");
return commandLine;
}

View file

@ -0,0 +1,47 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- VsDevShellGenerator
Abstract:
- Dynamic profile generator for Visual Studio Developer PowerShell
Author(s):
- Charles Willis - October 2020
--*/
#pragma once
#include "BaseVisualStudioGenerator.h"
namespace Microsoft::Terminal::Settings::Model
{
class VsDevShellGenerator : public BaseVisualStudioGenerator
{
public:
std::wstring_view GetNamespace() override
{
return std::wstring_view{ L"Windows.Terminal.VisualStudio.Powershell" };
}
bool IsInstanceValid(const VsSetupConfiguration::VsSetupInstance& instance) const override
{
return instance.VersionInRange(L"[16.2,)");
}
std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance) const override
{
return L"VsDevShell" + instance.GetInstanceId();
}
std::wstring GetProfileIconPath() const override
{
return L"ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png";
}
std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const override;
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const override;
};
};

View file

@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "VsSetupConfiguration.h"
using namespace Microsoft::Terminal::Settings::Model;
std::vector<VsSetupConfiguration::VsSetupInstance> VsSetupConfiguration::QueryInstances()
{
std::vector<VsSetupInstance> instances;
// SetupConfiguration is only registered if Visual Studio is installed
ComPtrSetupQuery pQuery{ wil::CoCreateInstanceNoThrow<SetupConfiguration, ISetupConfiguration2>() };
if (pQuery == nullptr)
{
return instances;
}
// Enumerate all valid instances of Visual Studio
wil::com_ptr<IEnumSetupInstances> e;
THROW_IF_FAILED(pQuery->EnumInstances(&e));
ComPtrSetupInstance rgpInstance;
auto result = e->Next(1, &rgpInstance, nullptr);
while (S_OK == result)
{
// wrap the COM pointers in a friendly interface
instances.emplace_back(VsSetupInstance{ pQuery, rgpInstance });
result = e->Next(1, &rgpInstance, nullptr);
}
THROW_IF_FAILED(result);
return instances;
}
/// <summary>
/// Takes a relative path under a Visual Studio installation and returns the absolute path.
/// </summary>
std::wstring VsSetupConfiguration::ResolvePath(ISetupInstance* pInst, std::wstring_view relativePath)
{
wil::unique_bstr bstrAbsolutePath;
THROW_IF_FAILED(pInst->ResolvePath(relativePath.data(), &bstrAbsolutePath));
return bstrAbsolutePath.get();
}
/// <summary>
/// Determines whether a Visual Studio installation version falls within a specified range.
/// The range is specified as a string, ex: "[15.0.0.0,)", "[15.0.0.0, 16.7.0.0)
/// </summary>
bool VsSetupConfiguration::InstallationVersionInRange(ISetupConfiguration2* pQuery, ISetupInstance* pInst, std::wstring_view range)
{
const auto helper = wil::com_query<ISetupHelper>(pQuery);
// VS versions in a string format such as "16.3.0.0" can be easily compared
// by parsing them into 64-bit unsigned integers using the stable algorithm
// provided by ParseVersion and ParseVersionRange
unsigned long long minVersion{ 0 };
unsigned long long maxVersion{ 0 };
THROW_IF_FAILED(helper->ParseVersionRange(range.data(), &minVersion, &maxVersion));
wil::unique_bstr bstrVersion;
THROW_IF_FAILED(pInst->GetInstallationVersion(&bstrVersion));
unsigned long long ullVersion{ 0 };
THROW_IF_FAILED(helper->ParseVersion(bstrVersion.get(), &ullVersion));
return ullVersion >= minVersion && ullVersion <= maxVersion;
}
std::wstring VsSetupConfiguration::GetInstallationVersion(ISetupInstance* pInst)
{
wil::unique_bstr bstrInstallationVersion;
THROW_IF_FAILED(pInst->GetInstallationVersion(&bstrInstallationVersion));
return bstrInstallationVersion.get();
}
std::wstring VsSetupConfiguration::GetInstallationPath(ISetupInstance* pInst)
{
wil::unique_bstr bstrInstallationPath;
THROW_IF_FAILED(pInst->GetInstallationPath(&bstrInstallationPath));
return bstrInstallationPath.get();
}
/// <summary>
/// The instance id is unique for each Visual Studio installation on a system.
/// The instance id is generated by the Visual Studio setup engine and varies from system to system.
/// </summary>
std::wstring VsSetupConfiguration::GetInstanceId(ISetupInstance* pInst)
{
wil::unique_bstr bstrInstanceId;
THROW_IF_FAILED(pInst->GetInstanceId(&bstrInstanceId));
return bstrInstanceId.get();
}
std::wstring VsSetupConfiguration::GetStringProperty(ISetupPropertyStore* pProps, std::wstring_view name)
{
if (pProps == nullptr)
{
return std::wstring{};
}
wil::unique_variant var;
if (FAILED(pProps->GetValue(name.data(), &var)))
{
return std::wstring{};
}
return var.bstrVal;
}

View file

@ -0,0 +1,234 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- VsSetupConfiguration
Abstract:
- Encapsulates the Visual Studio Setup Configuration COM APIs
Author(s):
- Charles Willis - October 2020
--*/
#pragma once
#include "Setup.Configuration.h"
namespace Microsoft::Terminal::Settings::Model
{
/// <summary>
/// See https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration?view=visualstudiosdk-2019
/// </summary>
class VsSetupConfiguration
{
typedef wil::com_ptr<ISetupConfiguration2> ComPtrSetupQuery;
typedef wil::com_ptr<ISetupHelper> ComPtrSetupHelper;
typedef wil::com_ptr<ISetupInstance> ComPtrSetupInstance;
typedef wil::com_ptr<ISetupInstance2> ComPtrSetupInstance2;
typedef wil::com_ptr<ISetupPropertyStore> ComPtrPropertyStore;
typedef wil::com_ptr<ISetupPackageReference> ComPtrPackageReference;
typedef wil::com_ptr<ISetupInstanceCatalog> ComPtrInstanceCatalog;
typedef ComPtrPropertyStore ComPtrCustomPropertyStore;
typedef ComPtrPropertyStore ComPtrCatalogPropertyStore;
public:
struct VsSetupInstance
{
std::wstring ResolvePath(std::wstring_view relativePath) const
{
return VsSetupConfiguration::ResolvePath(inst.get(), relativePath);
}
std::wstring GetDevShellModulePath() const
{
// The path of Microsoft.VisualStudio.DevShell.dll changed in 16.3
if (VersionInRange(L"[16.3,"))
{
return ResolvePath(L"Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll");
}
return ResolvePath(L"Common7\\Tools\\vsdevshell\\Microsoft.VisualStudio.DevShell.dll");
}
std::wstring GetDevCmdScriptPath() const
{
return ResolvePath(L"Common7\\Tools\\VsDevCmd.bat");
}
bool VersionInRange(std::wstring_view range) const
{
return InstallationVersionInRange(query.get(), inst.get(), range);
}
std::wstring GetVersion() const
{
return GetInstallationVersion(inst.get());
}
std::wstring GetInstallationPath() const
{
return VsSetupConfiguration::GetInstallationPath(inst.get());
}
std::wstring GetInstanceId() const
{
return VsSetupConfiguration::GetInstanceId(inst.get());
}
ComPtrPropertyStore GetInstancePropertyStore() const
{
ComPtrPropertyStore properties;
inst.query_to<ISetupPropertyStore>(&properties);
return properties;
}
ComPtrCustomPropertyStore GetCustomPropertyStore() const
{
ComPtrSetupInstance2 instance2;
inst.query_to<ISetupInstance2>(&instance2);
ComPtrCustomPropertyStore properties;
if (FAILED(instance2->GetProperties(&properties)))
{
return nullptr;
}
return properties;
}
ComPtrCatalogPropertyStore GetCatalogPropertyStore() const
{
ComPtrInstanceCatalog instanceCatalog;
inst.query_to<ISetupInstanceCatalog>(&instanceCatalog);
ComPtrCatalogPropertyStore properties;
if (FAILED(instanceCatalog->GetCatalogInfo(&properties)))
{
return nullptr;
}
return properties;
}
std::wstring GetProfileNameSuffix() const
{
return profileNameSuffix;
}
private:
friend class VsSetupConfiguration;
VsSetupInstance(ComPtrSetupQuery pQuery, ComPtrSetupInstance pInstance) :
query(std::move(pQuery)),
inst(std::move(pInstance)),
profileNameSuffix(BuildProfileNameSuffix())
{
}
ComPtrSetupQuery query;
ComPtrSetupInstance inst;
std::wstring profileNameSuffix;
std::wstring BuildProfileNameSuffix() const
{
ComPtrCatalogPropertyStore catalogProperties = GetCatalogPropertyStore();
if (catalogProperties != nullptr)
{
std::wstring suffix;
std::wstring productLine{ GetProductLineVersion(catalogProperties.get()) };
suffix.append(productLine);
ComPtrCustomPropertyStore customProperties = GetCustomPropertyStore();
if (customProperties != nullptr)
{
std::wstring nickname{ GetNickname(customProperties.get()) };
if (!nickname.empty())
{
suffix.append(L" (" + nickname + L")");
}
else
{
ComPtrPropertyStore instanceProperties = GetInstancePropertyStore();
suffix.append(GetChannelNameSuffixTag(instanceProperties.get()));
}
}
else
{
ComPtrPropertyStore instanceProperties = GetInstancePropertyStore();
suffix.append(GetChannelNameSuffixTag(instanceProperties.get()));
}
return suffix;
}
return GetVersion();
}
static std::wstring GetChannelNameSuffixTag(ISetupPropertyStore* instanceProperties)
{
std::wstring tag;
std::wstring channelName{ GetChannelName(instanceProperties) };
if (channelName.empty())
{
return channelName;
}
if (channelName != L"Release")
{
tag.append(L" [" + channelName + L"]");
}
return tag;
}
static std::wstring GetChannelId(ISetupPropertyStore* instanceProperties)
{
return GetStringProperty(instanceProperties, L"channelId");
}
static std::wstring GetChannelName(ISetupPropertyStore* instanceProperties)
{
std::wstring channelId{ GetChannelId(instanceProperties) };
if (channelId.empty())
{
return channelId;
}
std::wstring channelName;
// channelId is in the format <ProductName>.<MajorVersion>.<ChannelName>
size_t pos = channelId.rfind(L".");
if (pos != std::wstring::npos)
{
channelName.append(channelId.substr(pos + 1));
}
return channelName;
}
static std::wstring GetNickname(ISetupPropertyStore* customProperties)
{
return GetStringProperty(customProperties, L"nickname");
}
static std::wstring GetProductLineVersion(ISetupPropertyStore* customProperties)
{
return GetStringProperty(customProperties, L"productLineVersion");
}
};
static std::vector<VsSetupInstance> QueryInstances();
private:
static bool InstallationVersionInRange(ISetupConfiguration2* pQuery, ISetupInstance* pInst, std::wstring_view range);
static std::wstring ResolvePath(ISetupInstance* pInst, std::wstring_view relativePath);
static std::wstring GetInstallationVersion(ISetupInstance* pInst);
static std::wstring GetInstallationPath(ISetupInstance* pInst);
static std::wstring GetInstanceId(ISetupInstance* pInst);
static std::wstring GetStringProperty(ISetupPropertyStore* pProps, std::wstring_view name);
};
};

View file

@ -29,8 +29,8 @@
"disableAnimations": false,
"startupActions": "",
"focusFollowMouse": false,
"minimizeToTray": false,
"alwaysShowTrayIcon": false,
"minimizeToNotificationArea": false,
"alwaysShowNotificationIcon": false,
"profiles":
[
@ -337,8 +337,10 @@
// Pane Management
{ "command": "closePane", "keys": "ctrl+shift+w" },
{ "command": { "action": "splitPane", "split": "horizontal" }, "keys": "alt+shift+-" },
{ "command": { "action": "splitPane", "split": "vertical" }, "keys": "alt+shift+plus" },
{ "command": { "action": "splitPane", "split": "up" } },
{ "command": { "action": "splitPane", "split": "down" }, "keys": "alt+shift+-" },
{ "command": { "action": "splitPane", "split": "left" } },
{ "command": { "action": "splitPane", "split": "right" }, "keys": "alt+shift+plus" },
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" },
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" },
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" },
@ -393,6 +395,7 @@
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+numpad_plus" },
{ "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+numpad_minus" },
{ "command": "resetFontSize", "keys": "ctrl+0" },
{ "command": "resetFontSize", "keys": "ctrl+numpad_0" },
// Other commands
{
@ -431,10 +434,16 @@
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "auto" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" }
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "up" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" }
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "left" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "right" }
}
]
}

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -73,8 +73,8 @@ namespace RemotingUnitTests
Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; }
void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error{}; }
void Summon(const Remoting::SummonWindowBehavior& /*args*/) { throw winrt::hresult_error{}; };
void RequestShowTrayIcon() { throw winrt::hresult_error{}; };
void RequestHideTrayIcon() { throw winrt::hresult_error{}; };
void RequestShowNotificationIcon() { throw winrt::hresult_error{}; };
void RequestHideNotificationIcon() { throw winrt::hresult_error{}; };
void RequestQuitAll() { throw winrt::hresult_error{}; };
void Quit() { throw winrt::hresult_error{}; };
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs);
@ -83,8 +83,8 @@ namespace RemotingUnitTests
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs);
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
};
@ -140,11 +140,16 @@ namespace RemotingUnitTests
TEST_METHOD(TestSummonMostRecentIsQuake);
TEST_METHOD(TestSummonAfterWindowClose);
TEST_CLASS_SETUP(ClassSetup)
{
return true;
}
static void _closePeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID);
static void _killPeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID);
@ -164,6 +169,19 @@ namespace RemotingUnitTests
}
};
// Helper to tell the monarch that a peasant is closing, this emulates when
// a peasant is closed normally instead of when it crashes.
void RemotingTests::_closePeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID)
{
if (peasantID <= 0)
{
return;
}
m->SignalClose(peasantID);
}
// Helper to replace the specified peasant in a monarch with a
// "DeadPeasant", which will emulate what happens when the peasant process
// dies.
@ -2378,4 +2396,127 @@ namespace RemotingUnitTests
}
}
void RemotingTests::TestSummonAfterWindowClose()
{
Log::Comment(L"Test that we can summon a window on the current desktop,"
L" when the MRU window on that desktop closes normally.");
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
constexpr auto monarch0PID = 12345u;
constexpr auto peasant1PID = 23456u;
constexpr auto peasant2PID = 34567u;
constexpr auto peasant3PID = 45678u;
auto m0 = make_private<Remoting::implementation::Monarch>(monarch0PID);
auto p1 = make_private<Remoting::implementation::Peasant>(peasant1PID);
auto p2 = make_private<Remoting::implementation::Peasant>(peasant2PID);
auto p3 = make_private<Remoting::implementation::Peasant>(peasant3PID);
p1->WindowName(L"one");
p2->WindowName(L"two");
p3->WindowName(L"three");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
VERIFY_ARE_EQUAL(0, p3->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
m0->AddPeasant(*p3);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(3, p3->GetID());
VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
bool p1ExpectedToBeSummoned = false;
bool p2ExpectedToBeSummoned = false;
bool p3ExpectedToBeSummoned = false;
p1->SummonRequested([&](auto&&, auto&&) {
Log::Comment(L"p1 summoned");
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
});
p2->SummonRequested([&](auto&&, auto&&) {
Log::Comment(L"p2 summoned");
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
});
p3->SummonRequested([&](auto&&, auto&&) {
Log::Comment(L"p3 summoned");
VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
});
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
guid2,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the third peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
guid1,
winrt::clock().now() };
p3->ActivateWindow(activatedArgs);
}
Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
auto manager = winrt::make_self<MockDesktopManager>();
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
if (hwnd == peasant1PID || hwnd == peasant3PID)
{
*result = true;
}
else if (hwnd == peasant2PID)
{
*result = false;
}
else
{
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
}
return S_OK;
};
manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
Remoting::SummonWindowSelectionArgs args;
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
p3ExpectedToBeSummoned = true;
args.OnCurrentDesktop(true);
m0->SummonWindow(args);
VERIFY_IS_TRUE(args.FoundMatch());
Log::Comment(L"Close window 3. Window 1 is now the MRU on desktop 1.");
RemotingTests::_closePeasant(m0, p3->GetID());
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
p1ExpectedToBeSummoned = true;
p2ExpectedToBeSummoned = false;
p3ExpectedToBeSummoned = false;
args.FoundMatch(false);
args.OnCurrentDesktop(true);
m0->SummonWindow(args);
VERIFY_IS_TRUE(args.FoundMatch());
}
}

View file

@ -63,7 +63,7 @@ AppHost::AppHost() noexcept :
// Update our own internal state tracking if we're in quake mode or not.
_IsQuakeWindowChanged(nullptr, nullptr);
_window->SetMinimizeToTrayBehavior(_logic.GetMinimizeToTray());
_window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea());
// Tell the window to callback to us when it's about to handle a WM_CREATE
auto pfn = std::bind(&AppHost::_HandleCreateWindow,
@ -335,13 +335,13 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*
// - <none>
void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/)
{
if (_windowManager.IsMonarch() && _trayIcon)
if (_windowManager.IsMonarch() && _notificationIcon)
{
_DestroyTrayIcon();
_DestroyNotificationIcon();
}
else if (_window->IsQuakeWindow())
{
_HideTrayIconRequested();
_HideNotificationIconRequested();
}
_window->Close();
@ -678,9 +678,9 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s
if (_windowManager.DoesQuakeWindowExist() ||
_window->IsQuakeWindow() ||
(_logic.GetAlwaysShowTrayIcon() || _logic.GetMinimizeToTray()))
(_logic.GetAlwaysShowNotificationIcon() || _logic.GetMinimizeToNotificationArea()))
{
_CreateTrayIcon();
_CreateNotificationIcon();
}
// Set the number of open windows (so we know if we are the last window)
@ -691,8 +691,8 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s
_windowManager.WindowClosed([this](auto&&, auto&&) { _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); });
// These events are coming from peasants that become or un-become quake windows.
_windowManager.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequested(); });
_windowManager.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequested(); });
_windowManager.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequested(); });
_windowManager.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequested(); });
// If the monarch receives a QuitAll event it will signal this event to be
// ran before each peasant is closed.
_windowManager.QuitAllRequested({ this, &AppHost::_QuitAllRequested });
@ -982,12 +982,12 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
{
_setupGlobalHotkeys();
// If we're monarch, we need to check some conditions to show the tray icon.
// If there's a Quake window somewhere, we'll want to keep the tray icon.
// There's two settings - MinimizeToTray and AlwaysShowTrayIcon. If either
// one of them are true, we want to make sure there's a tray icon.
// If both are false, we want to remove our icon from the tray.
// When we remove our icon from the tray, we'll also want to re-summon
// If we're monarch, we need to check some conditions to show the notification icon.
// If there's a Quake window somewhere, we'll want to keep the notification icon.
// There's two settings - MinimizeToNotificationArea and AlwaysShowNotificationIcon. If either
// one of them are true, we want to make sure there's a notification icon.
// If both are false, we want to remove our icon from the notification area.
// When we remove our icon from the notification area, we'll also want to re-summon
// any hidden windows, but right now we're not keeping track of who's hidden,
// so just summon them all. Tracking the work to do a "summon all minimized" in
// GH#10448
@ -995,36 +995,36 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
{
if (!_windowManager.DoesQuakeWindowExist())
{
if (!_trayIcon && (_logic.GetMinimizeToTray() || _logic.GetAlwaysShowTrayIcon()))
if (!_notificationIcon && (_logic.GetMinimizeToNotificationArea() || _logic.GetAlwaysShowNotificationIcon()))
{
_CreateTrayIcon();
_CreateNotificationIcon();
}
else if (_trayIcon && !_logic.GetMinimizeToTray() && !_logic.GetAlwaysShowTrayIcon())
else if (_notificationIcon && !_logic.GetMinimizeToNotificationArea() && !_logic.GetAlwaysShowNotificationIcon())
{
_windowManager.SummonAllWindows();
_DestroyTrayIcon();
_DestroyNotificationIcon();
}
}
}
_window->SetMinimizeToTrayBehavior(_logic.GetMinimizeToTray());
_window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea());
}
void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
// We want the quake window to be accessible through the tray icon.
// This means if there's a quake window _somewhere_, we want the tray icon
// to show regardless of the tray icon settings.
// This also means we'll need to destroy the tray icon if it was created
// We want the quake window to be accessible through the notification icon.
// This means if there's a quake window _somewhere_, we want the notification icon
// to show regardless of the notification icon settings.
// This also means we'll need to destroy the notification icon if it was created
// specifically for the quake window. If not, it should not be destroyed.
if (!_window->IsQuakeWindow() && _logic.IsQuakeWindow())
{
_ShowTrayIconRequested();
_ShowNotificationIconRequested();
}
else if (_window->IsQuakeWindow() && !_logic.IsQuakeWindow())
{
_HideTrayIconRequested();
_HideNotificationIconRequested();
}
_window->IsQuakeWindow(_logic.IsQuakeWindow());
@ -1070,81 +1070,81 @@ void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&,
}
// Method Description:
// - Creates a Tray Icon and hooks up its handlers
// - Creates a Notification Icon and hooks up its handlers
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppHost::_CreateTrayIcon()
void AppHost::_CreateNotificationIcon()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
_trayIcon = std::make_unique<TrayIcon>(_window->GetHandle());
_notificationIcon = std::make_unique<NotificationIcon>(_window->GetHandle());
// Hookup the handlers, save the tokens for revoking if settings change.
_ReAddTrayIconToken = _window->NotifyReAddTrayIcon([this]() { _trayIcon->ReAddTrayIcon(); });
_TrayIconPressedToken = _window->NotifyTrayIconPressed([this]() { _trayIcon->TrayIconPressed(); });
_ShowTrayContextMenuToken = _window->NotifyShowTrayContextMenu([this](til::point coord) { _trayIcon->ShowTrayContextMenu(coord, _windowManager.GetPeasantInfos()); });
_TrayMenuItemSelectedToken = _window->NotifyTrayMenuItemSelected([this](HMENU hm, UINT idx) { _trayIcon->TrayMenuItemSelected(hm, idx); });
_trayIcon->SummonWindowRequested([this](auto& args) { _windowManager.SummonWindow(args); });
_ReAddNotificationIconToken = _window->NotifyReAddNotificationIcon([this]() { _notificationIcon->ReAddNotificationIcon(); });
_NotificationIconPressedToken = _window->NotifyNotificationIconPressed([this]() { _notificationIcon->NotificationIconPressed(); });
_ShowNotificationIconContextMenuToken = _window->NotifyShowNotificationIconContextMenu([this](til::point coord) { _notificationIcon->ShowContextMenu(coord, _windowManager.GetPeasantInfos()); });
_NotificationIconMenuItemSelectedToken = _window->NotifyNotificationIconMenuItemSelected([this](HMENU hm, UINT idx) { _notificationIcon->MenuItemSelected(hm, idx); });
_notificationIcon->SummonWindowRequested([this](auto& args) { _windowManager.SummonWindow(args); });
}
}
// Method Description:
// - Deletes our tray icon if we have one.
// - Deletes our notification icon if we have one.
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppHost::_DestroyTrayIcon()
void AppHost::_DestroyNotificationIcon()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
_window->NotifyReAddTrayIcon(_ReAddTrayIconToken);
_window->NotifyTrayIconPressed(_TrayIconPressedToken);
_window->NotifyShowTrayContextMenu(_ShowTrayContextMenuToken);
_window->NotifyTrayMenuItemSelected(_TrayMenuItemSelectedToken);
_window->NotifyReAddNotificationIcon(_ReAddNotificationIconToken);
_window->NotifyNotificationIconPressed(_NotificationIconPressedToken);
_window->NotifyShowNotificationIconContextMenu(_ShowNotificationIconContextMenuToken);
_window->NotifyNotificationIconMenuItemSelected(_NotificationIconMenuItemSelectedToken);
_trayIcon->RemoveIconFromTray();
_trayIcon = nullptr;
_notificationIcon->RemoveIconFromNotificationArea();
_notificationIcon = nullptr;
}
}
void AppHost::_ShowTrayIconRequested()
void AppHost::_ShowNotificationIconRequested()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (_windowManager.IsMonarch())
{
if (!_trayIcon)
if (!_notificationIcon)
{
_CreateTrayIcon();
_CreateNotificationIcon();
}
}
else
{
_windowManager.RequestShowTrayIcon();
_windowManager.RequestShowNotificationIcon();
}
}
}
void AppHost::_HideTrayIconRequested()
void AppHost::_HideNotificationIconRequested()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (_windowManager.IsMonarch())
{
// Destroy it only if our settings allow it
if (_trayIcon &&
!_logic.GetAlwaysShowTrayIcon() &&
!_logic.GetMinimizeToTray())
if (_notificationIcon &&
!_logic.GetAlwaysShowNotificationIcon() &&
!_logic.GetMinimizeToNotificationArea())
{
_DestroyTrayIcon();
_DestroyNotificationIcon();
}
}
else
{
_windowManager.RequestHideTrayIcon();
_windowManager.RequestHideNotificationIcon();
}
}
}

View file

@ -3,7 +3,7 @@
#include "pch.h"
#include "NonClientIslandWindow.h"
#include "TrayIcon.h"
#include "NotificationIcon.h"
class AppHost
{
@ -97,13 +97,13 @@ private:
void _QuitAllRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _CreateTrayIcon();
void _DestroyTrayIcon();
void _ShowTrayIconRequested();
void _HideTrayIconRequested();
std::unique_ptr<TrayIcon> _trayIcon;
winrt::event_token _ReAddTrayIconToken;
winrt::event_token _TrayIconPressedToken;
winrt::event_token _ShowTrayContextMenuToken;
winrt::event_token _TrayMenuItemSelectedToken;
void _CreateNotificationIcon();
void _DestroyNotificationIcon();
void _ShowNotificationIconRequested();
void _HideNotificationIconRequested();
std::unique_ptr<NotificationIcon> _notificationIcon;
winrt::event_token _ReAddNotificationIconToken;
winrt::event_token _NotificationIconPressedToken;
winrt::event_token _ShowNotificationIconContextMenuToken;
winrt::event_token _NotificationIconMenuItemSelectedToken;
};

View file

@ -5,4 +5,4 @@
// Custom window messages
#define CM_UPDATE_TITLE (WM_USER)
#define CM_NOTIFY_FROM_TRAY (WM_USER + 1)
#define CM_NOTIFY_FROM_NOTIFICATION_AREA (WM_USER + 1)

View file

@ -6,7 +6,7 @@
#include "../types/inc/Viewport.hpp"
#include "resource.h"
#include "icon.h"
#include "TrayIcon.h"
#include "NotificationIcon.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
@ -570,20 +570,20 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
}
}
}
case CM_NOTIFY_FROM_TRAY:
case CM_NOTIFY_FROM_NOTIFICATION_AREA:
{
switch (LOWORD(lparam))
{
case NIN_SELECT:
case NIN_KEYSELECT:
{
_NotifyTrayIconPressedHandlers();
_NotifyNotificationIconPressedHandlers();
return 0;
}
case WM_CONTEXTMENU:
{
const til::point eventPoint{ GET_X_LPARAM(wparam), GET_Y_LPARAM(wparam) };
_NotifyShowTrayContextMenuHandlers(eventPoint);
_NotifyShowNotificationIconContextMenuHandlers(eventPoint);
return 0;
}
}
@ -591,17 +591,17 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
}
case WM_MENUCOMMAND:
{
_NotifyTrayMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam);
_NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam);
return 0;
}
default:
// We'll want to receive this message when explorer.exe restarts
// so that we can re-add our icon to the tray.
// so that we can re-add our icon to the notification area.
// This unfortunately isn't a switch case because we register the
// message at runtime.
if (message == WM_TASKBARCREATED)
{
_NotifyReAddTrayIconHandlers();
_NotifyReAddNotificationIconHandlers();
return 0;
}
}
@ -628,7 +628,7 @@ void IslandWindow::OnResize(const UINT width, const UINT height)
void IslandWindow::OnMinimize()
{
// TODO GH#1989 Stop rendering island content when the app is minimized.
if (_minimizeToTray)
if (_minimizeToNotificationArea)
{
HideWindow();
}
@ -1640,9 +1640,9 @@ void IslandWindow::HideWindow()
ShowWindow(GetHandle(), SW_HIDE);
}
void IslandWindow::SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept
void IslandWindow::SetMinimizeToNotificationAreaBehavior(bool MinimizeToNotificationArea) noexcept
{
_minimizeToTray = minimizeToTray;
_minimizeToNotificationArea = MinimizeToNotificationArea;
}
// Method Description:

View file

@ -49,7 +49,7 @@ public:
void HideWindow();
void SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept;
void SetMinimizeToNotificationAreaBehavior(bool MinimizeToNotificationArea) noexcept;
void OpenSystemMenu(const std::optional<int> mouseX, const std::optional<int> mouseY) const noexcept;
@ -58,11 +58,11 @@ public:
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
WINRT_CALLBACK(WindowActivated, winrt::delegate<void()>);
WINRT_CALLBACK(HotkeyPressed, winrt::delegate<void(long)>);
WINRT_CALLBACK(NotifyTrayIconPressed, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyNotificationIconPressed, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyShowTrayContextMenu, winrt::delegate<void(til::point)>);
WINRT_CALLBACK(NotifyTrayMenuItemSelected, winrt::delegate<void(HMENU, UINT)>);
WINRT_CALLBACK(NotifyReAddTrayIcon, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyShowNotificationIconContextMenu, winrt::delegate<void(til::point)>);
WINRT_CALLBACK(NotifyNotificationIconMenuItemSelected, winrt::delegate<void(HMENU, UINT)>);
WINRT_CALLBACK(NotifyReAddNotificationIcon, winrt::delegate<void()>);
WINRT_CALLBACK(WindowMoved, winrt::delegate<void()>);
@ -127,7 +127,7 @@ protected:
void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);
bool _minimizeToTray{ false };
bool _minimizeToNotificationArea{ false };
private:
// This minimum width allows for width the tabs fit

View file

@ -3,7 +3,7 @@
#include "pch.h"
#include "icon.h"
#include "TrayIcon.h"
#include "NotificationIcon.h"
#include "CustomWindowMessages.h"
#include <ScopedResourceLoader.h>
@ -12,54 +12,54 @@
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Microsoft::Terminal;
TrayIcon::TrayIcon(const HWND owningHwnd) :
NotificationIcon::NotificationIcon(const HWND owningHwnd) :
_owningHwnd{ owningHwnd }
{
CreateTrayIcon();
CreateNotificationIcon();
}
TrayIcon::~TrayIcon()
NotificationIcon::~NotificationIcon()
{
RemoveIconFromTray();
RemoveIconFromNotificationArea();
}
void TrayIcon::_CreateWindow()
void NotificationIcon::_CreateWindow()
{
WNDCLASSW wc{};
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = wil::GetModuleInstanceHandle();
wc.lpszClassName = L"TRAY_ICON_HOSTING_WINDOW_CLASS";
wc.lpszClassName = L"NOTIFICATION_ICON_HOSTING_WINDOW_CLASS";
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DefWindowProcW;
wc.hIcon = static_cast<HICON>(GetActiveAppIconHandle(true));
RegisterClass(&wc);
_trayIconHwnd = wil::unique_hwnd(CreateWindowW(wc.lpszClassName,
wc.lpszClassName,
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_MESSAGE,
nullptr,
wc.hInstance,
nullptr));
_notificationIconHwnd = wil::unique_hwnd(CreateWindowW(wc.lpszClassName,
wc.lpszClassName,
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_MESSAGE,
nullptr,
wc.hInstance,
nullptr));
WINRT_VERIFY(_trayIconHwnd);
WINRT_VERIFY(_notificationIconHwnd);
}
// Method Description:
// - Creates and adds an icon to the notification tray.
// - Creates and adds an icon to the notification area.
// If an icon already exists, update the HWND associated
// to the icon with this window's HWND.
// Arguments:
// - <unused>
// Return Value:
// - <none>
void TrayIcon::CreateTrayIcon()
void NotificationIcon::CreateNotificationIcon()
{
if (!_trayIconHwnd)
if (!_notificationIconHwnd)
{
// Creating a disabled, non visible window just so we can set it
// as the foreground window when showing the context menu.
@ -71,7 +71,7 @@ void TrayIcon::CreateTrayIcon()
NOTIFYICONDATA nid{};
nid.cbSize = sizeof(NOTIFYICONDATA);
// This HWND will receive the callbacks sent by the tray icon.
// This HWND will receive the callbacks sent by the notification icon.
nid.hWnd = _owningHwnd;
// App-defined identifier of the icon. The HWND and ID are used
@ -80,7 +80,7 @@ void TrayIcon::CreateTrayIcon()
// going to be showing one so the ID doesn't really matter.
nid.uID = 1;
nid.uCallbackMessage = CM_NOTIFY_FROM_TRAY;
nid.uCallbackMessage = CM_NOTIFY_FROM_NOTIFICATION_AREA;
// AppName happens to be in CascadiaPackage's Resources.
ScopedResourceLoader loader{ L"Resources" };
@ -98,7 +98,7 @@ void TrayIcon::CreateTrayIcon()
nid.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NIM_SETVERSION, &nid);
_trayIconData = nid;
_notificationIconData = nid;
}
// Method Description:
@ -109,14 +109,14 @@ void TrayIcon::CreateTrayIcon()
// - peasants: The map of all peasants that should be available in the context menu.
// Return Value:
// - <none>
void TrayIcon::ShowTrayContextMenu(const til::point& coord,
const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
void NotificationIcon::ShowContextMenu(const til::point& coord,
const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
{
if (const auto hMenu = _CreateTrayContextMenu(peasants))
if (const auto hMenu = _CreateContextMenu(peasants))
{
// We'll need to set our window to the foreground before calling
// TrackPopupMenuEx or else the menu won't dismiss when clicking away.
SetForegroundWindow(_trayIconHwnd.get());
SetForegroundWindow(_notificationIconHwnd.get());
// User can select menu items with the left and right buttons.
UINT uFlags = TPM_RIGHTBUTTON;
@ -137,12 +137,12 @@ void TrayIcon::ShowTrayContextMenu(const til::point& coord,
}
// Method Description:
// - This creates the context menu for our tray icon.
// - This creates the context menu for our notification icon.
// Arguments:
// - peasants: A map of all peasants' ID to their window name.
// Return Value:
// - The handle to the newly created context menu.
HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
HMENU NotificationIcon::_CreateContextMenu(const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
{
auto hMenu = CreatePopupMenu();
if (hMenu)
@ -155,7 +155,7 @@ HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Termi
SetMenuInfo(hMenu, &mi);
// Focus Current Terminal Window
AppendMenu(hMenu, MF_STRING, gsl::narrow<UINT_PTR>(TrayMenuItemAction::FocusTerminal), RS_(L"TrayIconFocusTerminal").c_str());
AppendMenu(hMenu, MF_STRING, gsl::narrow<UINT_PTR>(NotificationIconMenuItemAction::FocusTerminal), RS_(L"NotificationIconFocusTerminal").c_str());
AppendMenu(hMenu, MF_SEPARATOR, 0, L"");
// Submenu for Windows
@ -183,10 +183,10 @@ HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Termi
submenuInfo.cbSize = sizeof(MENUINFO);
submenuInfo.fMask = MIM_MENUDATA;
submenuInfo.dwStyle = MNS_NOTIFYBYPOS;
submenuInfo.dwMenuData = (UINT_PTR)TrayMenuItemAction::SummonWindow;
submenuInfo.dwMenuData = (UINT_PTR)NotificationIconMenuItemAction::SummonWindow;
SetMenuInfo(submenu, &submenuInfo);
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)submenu, RS_(L"TrayIconWindowSubmenu").c_str());
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)submenu, RS_(L"NotificationIconWindowSubmenu").c_str());
}
}
return hMenu;
@ -194,13 +194,13 @@ HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Termi
// Method Description:
// - This is the handler for when one of the menu items are selected within
// the tray icon's context menu.
// the notification icon's context menu.
// Arguments:
// - menu: The handle to the menu that holds the menu item that was selected.
// - menuItemIndex: The index of the menu item within the given menu.
// Return Value:
// - <none>
void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
void NotificationIcon::MenuItemSelected(const HMENU menu, const UINT menuItemIndex)
{
// Check the menu's data for a specific action.
MENUINFO mi{};
@ -209,7 +209,7 @@ void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
GetMenuInfo(menu, &mi);
if (mi.dwMenuData)
{
if (gsl::narrow<TrayMenuItemAction>(mi.dwMenuData) == TrayMenuItemAction::SummonWindow)
if (gsl::narrow<NotificationIconMenuItemAction>(mi.dwMenuData) == NotificationIconMenuItemAction::SummonWindow)
{
winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{};
args.WindowID(GetMenuItemID(menu, menuItemIndex));
@ -222,10 +222,10 @@ void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
}
// Now check the menu item itself for an action.
const auto action = gsl::narrow<TrayMenuItemAction>(GetMenuItemID(menu, menuItemIndex));
const auto action = gsl::narrow<NotificationIconMenuItemAction>(GetMenuItemID(menu, menuItemIndex));
switch (action)
{
case TrayMenuItemAction::FocusTerminal:
case NotificationIconMenuItemAction::FocusTerminal:
{
winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{};
args.SummonBehavior().ToggleVisibility(false);
@ -238,12 +238,12 @@ void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
}
// Method Description:
// - This is the handler for when the tray icon itself is left-clicked.
// - This is the handler for when the notification icon itself is left-clicked.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TrayIcon::TrayIconPressed()
void NotificationIcon::NotificationIconPressed()
{
// No name in the args means summon the mru window.
winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{};
@ -254,24 +254,24 @@ void TrayIcon::TrayIconPressed()
}
// Method Description:
// - Re-add a tray icon using our currently saved tray icon data.
// - Re-add a notification icon using our currently saved notification icon data.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TrayIcon::ReAddTrayIcon()
void NotificationIcon::ReAddNotificationIcon()
{
Shell_NotifyIcon(NIM_ADD, &_trayIconData);
Shell_NotifyIcon(NIM_SETVERSION, &_trayIconData);
Shell_NotifyIcon(NIM_ADD, &_notificationIconData);
Shell_NotifyIcon(NIM_SETVERSION, &_notificationIconData);
}
// Method Description:
// - Deletes our tray icon.
// - Deletes our notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TrayIcon::RemoveIconFromTray()
void NotificationIcon::RemoveIconFromNotificationArea()
{
Shell_NotifyIcon(NIM_DELETE, &_trayIconData);
Shell_NotifyIcon(NIM_DELETE, &_notificationIconData);
}

View file

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "../cascadia/inc/cppwinrt_utils.h"
// This enumerates all the possible actions
// that our notification icon context menu could do.
enum class NotificationIconMenuItemAction
{
FocusTerminal, // Focus the MRU terminal.
SummonWindow
};
class NotificationIcon
{
public:
NotificationIcon() = delete;
NotificationIcon(const HWND owningHwnd);
~NotificationIcon();
void CreateNotificationIcon();
void RemoveIconFromNotificationArea();
void ReAddNotificationIcon();
void NotificationIconPressed();
void ShowContextMenu(const til::point& coord, const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
void MenuItemSelected(const HMENU menu, const UINT menuItemIndex);
WINRT_CALLBACK(SummonWindowRequested, winrt::delegate<void(winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs)>);
private:
void _CreateWindow();
HMENU _CreateContextMenu(const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
wil::unique_hwnd _notificationIconHwnd;
HWND _owningHwnd;
NOTIFYICONDATA _notificationIconData;
};

View file

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "../cascadia/inc/cppwinrt_utils.h"
// This enumerates all the possible actions
// that our tray icon context menu could do.
enum class TrayMenuItemAction
{
FocusTerminal, // Focus the MRU terminal.
SummonWindow
};
class TrayIcon
{
public:
TrayIcon() = delete;
TrayIcon(const HWND owningHwnd);
~TrayIcon();
void CreateTrayIcon();
void RemoveIconFromTray();
void ReAddTrayIcon();
void TrayIconPressed();
void ShowTrayContextMenu(const til::point& coord, const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
void TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex);
WINRT_CALLBACK(SummonWindowRequested, winrt::delegate<void(winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs)>);
private:
void _CreateWindow();
HMENU _CreateTrayContextMenu(const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
wil::unique_hwnd _trayIconHwnd;
HWND _owningHwnd;
NOTIFYICONDATA _trayIconData;
};

View file

@ -47,7 +47,7 @@
<ClInclude Include="CustomWindowMessages.h" />
<ClInclude Include="IslandWindow.h" />
<ClInclude Include="NonClientIslandWindow.h" />
<ClInclude Include="TrayIcon.h" />
<ClInclude Include="NotificationIcon.h" />
<ClInclude Include="VirtualDesktopUtils.h" />
</ItemGroup>
<ItemGroup>
@ -58,7 +58,7 @@
<ClCompile Include="AppHost.cpp" />
<ClCompile Include="IslandWindow.cpp" />
<ClCompile Include="NonClientIslandWindow.cpp" />
<ClCompile Include="TrayIcon.cpp" />
<ClCompile Include="NotificationIcon.cpp" />
<ClCompile Include="VirtualDesktopUtils.cpp" />
<ClCompile Include="icon.cpp" />
</ItemGroup>

View file

@ -25,7 +25,7 @@ TRACELOGGING_DEFINE_PROVIDER(
// !! BODGY !!
// Manually use the resources from TerminalApp as our resources.
// The WindowsTerminal project doesn't actually build a Resources.resw file, but
// we still need to be able to localize strings for the tray icon menu. Anything
// we still need to be able to localize strings for the notification icon menu. Anything
// you want localized for WindowsTerminal.exe should be stuck in
// ...\TerminalApp\Resources\en-US\Resources.resw
#include <LibraryResources.h>

View file

@ -83,7 +83,8 @@
<!-- For ALL build types-->
<PropertyGroup Label="Configuration">
<PlatformToolset>v142</PlatformToolset>
<PlatformToolset Condition="$(MSBuildVersion) &lt; '17.0.0'">v142</PlatformToolset>
<PlatformToolset Condition="$(MSBuildVersion) &gt;= '17.0.0'">v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<LinkIncremental>false</LinkIncremental>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>

View file

@ -65,8 +65,8 @@
</feature>
<feature>
<name>Feature_TrayIcon</name>
<description>Controls whether the Tray Icon and related settings (aka. MinimizeToTray and AlwaysShowTrayIcon) are enabled</description>
<name>Feature_NotificationIcon</name>
<description>Controls whether the Notification Icon and related settings (aka. MinimizeToNotificationArea and AlwaysShowNotificationIcon) are enabled</description>
<stage>AlwaysEnabled</stage>
<alwaysDisabledReleaseTokens/>
</feature>

View file

@ -349,14 +349,29 @@ class UiaTextRangeTests
_pTextBuffer = &_pScreenInfo->GetTextBuffer();
_pUiaData = &gci.renderData;
// fill text buffer with text
for (UINT i = 0; i < _pTextBuffer->TotalRowCount(); ++i)
// GH#6986: document end now limits the navigation to be
// within the document end bounds _as opposed to_ the buffer bounds.
// As a result, let's populate the buffer partially to define a document end.
// Additionally, add spaces to create "words" in the buffer.
// LOAD BEARING: make sure we fill it halfway so that we can reuse most of
// the variables from the generated tests.
// fill first half of text buffer with text
for (UINT i = 0; i < _pTextBuffer->TotalRowCount() / 2; ++i)
{
ROW& row = _pTextBuffer->GetRowByOffset(i);
auto& charRow = row.GetCharRow();
for (auto& cell : charRow)
{
cell.Char() = L' ';
if (i % 2 == 0)
{
cell.Char() = L' ';
}
else
{
cell.Char() = L'X';
}
}
}
@ -719,9 +734,13 @@ class UiaTextRangeTests
TEST_METHOD(CanMoveByCharacter)
{
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().RightInclusive();
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
// clang-format off
const std::vector<MoveTest> testData
{
@ -749,6 +768,18 @@ class UiaTextRangeTests
}
},
MoveTest{
L"can't move past the end of the 'document'",
documentEnd,
documentEnd,
5,
{
0,
documentEnd,
documentEnd,
}
},
MoveTest{
L"can move to a new row when necessary when moving forward",
{ lastColumnIndex, 0 },
@ -782,7 +813,7 @@ class UiaTextRangeTests
int amountMoved;
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, test.start, test.end));
utr->Move(TextUnit::TextUnit_Character, test.moveAmt, &amountMoved);
THROW_IF_FAILED(utr->Move(TextUnit::TextUnit_Character, test.moveAmt, &amountMoved));
VERIFY_ARE_EQUAL(test.expected.moveAmt, amountMoved);
VERIFY_ARE_EQUAL(test.expected.start, utr->_start);
@ -795,6 +826,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
// clang-format off
const std::vector<MoveTest> testData
{
@ -810,15 +845,27 @@ class UiaTextRangeTests
}
},
MoveTest{
L"can't move past the end of the 'document'",
documentEnd,
documentEnd,
5,
{
0,
documentEnd,
documentEnd,
}
},
MoveTest{
L"can move backward from bottom row",
{0, bottomRow},
{lastColumnIndex, bottomRow},
{0, documentEnd.Y},
{lastColumnIndex, documentEnd.Y},
-3,
{
-3,
{0, bottomRow - 3},
{0, bottomRow - 2}
{0, base::ClampSub(documentEnd.Y, 3)},
{0, base::ClampSub(documentEnd.Y, 3)}
}
},
@ -868,6 +915,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const SHORT bottomRow = static_cast<SHORT>(_pTextBuffer->TotalRowCount() - 1);
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().RightInclusive(), _pTextBuffer->GetLastNonSpaceCharacter().Y };
// clang-format off
const std::vector<MoveEndpointTest> testData
{
@ -910,6 +961,19 @@ class UiaTextRangeTests
}
},
MoveEndpointTest{
L"can't move _end past the end of the document",
{0, 0},
documentEnd,
5,
TextPatternRangeEndpoint_End,
{
1,
{0,0},
{0, base::ClampAdd(documentEnd.Y,1)}
}
},
MoveEndpointTest{
L"_start follows _end when passed during movement",
{5, 0},
@ -925,40 +989,40 @@ class UiaTextRangeTests
MoveEndpointTest{
L"can't move _end past the beginning of the document when _end is positioned at the end",
{0, bottomRow},
{0, bottomRow+1},
{0, documentEnd.Y},
{0, base::ClampAdd(documentEnd.Y,1)},
1,
TextPatternRangeEndpoint_End,
{
0,
{0, bottomRow},
{0, bottomRow+1},
{0, documentEnd.Y},
{0, base::ClampAdd(documentEnd.Y,1)},
}
},
MoveEndpointTest{
L"can partially move _end to the end of the document when it is closer than the move count requested",
{0, 0},
{lastColumnIndex - 3, bottomRow},
{base::ClampSub(lastColumnIndex, 3), documentEnd.Y},
5,
TextPatternRangeEndpoint_End,
{
4,
{0, 0},
{0, bottomRow+1},
{0, base::ClampAdd(documentEnd.Y,1)},
}
},
MoveEndpointTest{
L"can't move _start past the end of the document",
{lastColumnIndex - 4, bottomRow},
{0, bottomRow+1},
{base::ClampSub(lastColumnIndex, 4), documentEnd.Y},
{0, base::ClampAdd(documentEnd.Y,1)},
5,
TextPatternRangeEndpoint_Start,
{
5,
{0, bottomRow+1},
{0, bottomRow+1},
{0, base::ClampAdd(documentEnd.Y,1)},
{0, base::ClampAdd(documentEnd.Y,1)},
}
},
@ -997,6 +1061,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
// clang-format off
const std::vector<MoveEndpointTest> testData
{
@ -1067,36 +1135,36 @@ class UiaTextRangeTests
},
MoveEndpointTest{
L"can move _end forwards when it's on the bottom row",
L"can't move _end forwards when it's on the bottom row (past doc end)",
{0, 0},
{lastColumnIndex - 3, bottomRow},
1,
TextPatternRangeEndpoint_End,
1,
0,
{0, 0},
{0, bottomRow+1}
documentEnd
},
MoveEndpointTest{
L"can't move _end forwards when it's at the end of the document already",
L"can't move _end forwards when it's at the end of the buffer already (past doc end)",
{0, 0},
{0, bottomRow+1},
1,
TextPatternRangeEndpoint_End,
0,
{0, 0},
{0, bottomRow+1}
documentEnd
},
MoveEndpointTest{
L"moving _start forward when it's already on the bottom row creates a degenerate range at the document end",
L"moving _start forward when it's already on the bottom row (past doc end) creates a degenerate range at the document end",
{0, bottomRow},
{lastColumnIndex, bottomRow},
1,
TextPatternRangeEndpoint_Start,
1,
{0, bottomRow+1},
{0, bottomRow+1}
0,
documentEnd,
documentEnd
},
MoveEndpointTest{
@ -1132,6 +1200,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
// clang-format off
const std::vector<MoveEndpointTest> testData =
{
@ -1144,7 +1216,7 @@ class UiaTextRangeTests
{
1,
{0, 4},
{0, bottomRow+1}
documentEnd
}
},
@ -1162,7 +1234,7 @@ class UiaTextRangeTests
},
MoveEndpointTest{
L"can't move _end forward when it's already at the end of the document",
L"can't move _end forward when it's already at the end of the buffer (past doc end)",
{3, 2},
{0, bottomRow+1},
1,
@ -1170,7 +1242,7 @@ class UiaTextRangeTests
{
0,
{3, 2},
{0, bottomRow+1}
documentEnd
}
},
@ -1208,8 +1280,8 @@ class UiaTextRangeTests
TextPatternRangeEndpoint_Start,
{
1,
{0, bottomRow+1},
{0, bottomRow+1}
documentEnd,
documentEnd
}
}
};
@ -1235,51 +1307,64 @@ class UiaTextRangeTests
// GH#7664: When attempting to expand to an enclosing unit
// at the end exclusive, the UTR should refuse to move past
// the end.
const auto lastNonspaceCharPos{ _pTextBuffer->GetLastNonSpaceCharacter() };
const COORD documentEnd{ 0, lastNonspaceCharPos.Y + 1 };
const til::point endInclusive{ bufferEnd };
// Iterate over each TextUnit. If the we don't support
// Iterate over each TextUnit. If we don't support
// the given TextUnit, we're supposed to fallback
// to the last one that was defined anyways.
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:textUnit", L"{0, 1, 2, 3, 4, 5, 6}")
END_TEST_METHOD_PROPERTIES();
int textUnit;
VERIFY_SUCCEEDED(TestData::TryGetValue(L"textUnit", textUnit), L"Get textUnit variant");
Microsoft::WRL::ComPtr<UiaTextRange> utr;
for (int unit = TextUnit::TextUnit_Character; unit != TextUnit::TextUnit_Document; ++unit)
{
Log::Comment(NoThrowString().Format(L"%s", toString(static_cast<TextUnit>(unit))));
Log::Comment(NoThrowString().Format(L"%s", toString(static_cast<TextUnit>(textUnit))));
// Create a degenerate UTR at EndExclusive
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive));
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(static_cast<TextUnit>(unit)));
// Create a degenerate UTR at EndExclusive
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, bufferEnd, endExclusive));
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(static_cast<TextUnit>(textUnit)));
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
}
VERIFY_ARE_EQUAL(documentEnd, til::point{ utr->_end });
}
TEST_METHOD(MovementAtExclusiveEnd)
{
// GH#7663: When attempting to move from end exclusive,
// the UTR should refuse to move past the end.
const auto lastLineStart{ bufferEndLeft };
const auto secondToLastLinePos{ point_offset_by_line(lastLineStart, bufferSize, -1) };
const auto secondToLastCharacterPos{ point_offset_by_char(bufferEnd, bufferSize, -1) };
const auto endInclusive{ bufferEnd };
// write "temp" at (2,2)
_pTextBuffer->Reset();
const til::point writeTarget{ 2, 2 };
_pTextBuffer->Write({ L"temp" }, writeTarget);
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEndInclusive{ base::ClampSub<short, short>(static_cast<short>(bufferSize.right()), 1), _pTextBuffer->GetLastNonSpaceCharacter().Y };
const COORD documentEndExclusive{ static_cast<short>(bufferSize.left()), base::ClampAdd(documentEndInclusive.Y, 1) };
const COORD lastLineStart{ static_cast<short>(bufferSize.left()), documentEndInclusive.Y };
const auto secondToLastLinePos{ point_offset_by_line(lastLineStart, bufferSize, -1) };
const COORD secondToLastCharacterPos{ documentEndInclusive.X - 1, documentEndInclusive.Y };
// Iterate over each TextUnit. If we don't support
// the given TextUnit, we're supposed to fallback
// to the last one that was defined anyways.
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:textUnit", L"{0, 1, 2, 3, 4, 5, 6}")
TEST_METHOD_PROPERTY(L"Data:degenerate", L"{false, true}")
TEST_METHOD_PROPERTY(L"Data:atDocumentEnd", L"{false, true}")
END_TEST_METHOD_PROPERTIES();
int unit;
bool degenerate;
bool atDocumentEnd;
VERIFY_SUCCEEDED(TestData::TryGetValue(L"textUnit", unit), L"Get TextUnit variant");
VERIFY_SUCCEEDED(TestData::TryGetValue(L"degenerate", degenerate), L"Get degenerate variant");
VERIFY_SUCCEEDED(TestData::TryGetValue(L"atDocumentEnd", atDocumentEnd), L"Get atDocumentEnd variant");
TextUnit textUnit{ static_cast<TextUnit>(unit) };
Microsoft::WRL::ComPtr<UiaTextRange> utr;
@ -1287,17 +1372,22 @@ class UiaTextRangeTests
Log::Comment(NoThrowString().Format(L"Forward by %s", toString(textUnit)));
// Create an UTR at EndExclusive
const auto utrEnd{ atDocumentEnd ? documentEndExclusive : endExclusive };
if (degenerate)
{
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endExclusive, endExclusive));
// UTR: (exclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
}
else
{
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive));
// UTR: (inclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
}
THROW_IF_FAILED(utr->Move(textUnit, 1, &moveAmt));
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
VERIFY_ARE_EQUAL(0, moveAmt);
// Verify expansion works properly
@ -1305,33 +1395,35 @@ class UiaTextRangeTests
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(textUnit));
if (textUnit <= TextUnit::TextUnit_Character)
{
VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
}
else if (textUnit <= TextUnit::TextUnit_Word)
{
VERIFY_ARE_EQUAL(writeTarget, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
}
else if (textUnit <= TextUnit::TextUnit_Line)
{
VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(lastLineStart, utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
}
else // textUnit <= TextUnit::TextUnit_Document:
{
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
}
// reset the UTR
if (degenerate)
{
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endExclusive, endExclusive));
const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
}
else
{
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive));
const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
}
// Verify that moving backwards still works properly
@ -1345,26 +1437,26 @@ class UiaTextRangeTests
// - degenerate --> it moves with _start to stay degenerate
// - !degenerate --> it excludes the last char, to select the second to last char
VERIFY_ARE_EQUAL(-1, moveAmt);
VERIFY_ARE_EQUAL(degenerate ? endInclusive : secondToLastCharacterPos, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? documentEndInclusive : secondToLastCharacterPos, utr->_start);
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_end);
}
else if (textUnit <= TextUnit::TextUnit_Word)
{
VERIFY_ARE_EQUAL(-1, moveAmt);
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
VERIFY_ARE_EQUAL(degenerate ? origin : writeTarget, til::point{ utr->_end });
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? origin : writeTarget, til::point{ utr->_end });
}
else if (textUnit <= TextUnit::TextUnit_Line)
{
VERIFY_ARE_EQUAL(-1, moveAmt);
VERIFY_ARE_EQUAL(degenerate ? lastLineStart : secondToLastLinePos, til::point{ utr->_start });
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? lastLineStart : secondToLastLinePos, til::point{ utr->_start });
VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_end });
}
else // textUnit <= TextUnit::TextUnit_Document:
{
VERIFY_ARE_EQUAL(degenerate ? -1 : 0, moveAmt);
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? -1 : 0, moveAmt);
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
VERIFY_ARE_EQUAL(degenerate ? origin : endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? origin : documentEndExclusive, til::point{ utr->_end });
}
}
@ -1829,37 +1921,38 @@ class UiaTextRangeTests
TEST_METHOD(GeneratedMovementTests)
{
// Populate the buffer with...
// - 9 segments of alternating text
// - 10 segments of alternating text
// - up to half of the buffer (vertically)
// It'll look something like this
// +---------------------------+
// |XXX XXX XXX XXX XXX|
// |XXX XXX XXX XXX XXX|
// |XXX XXX XXX XXX XXX|
// |XXX XXX XXX XXX XXX|
// |XXX XXX XXX XXX XXX|
// | |
// | |
// | |
// | |
// | |
// +---------------------------+
// +------------------------------+
// |XXX XXX XXX XXX XXX |
// |XXX XXX XXX XXX XXX |
// |XXX XXX XXX XXX XXX |
// |XXX XXX XXX XXX XXX |
// |XXX XXX XXX XXX XXX |
// | |
// | |
// | |
// | |
// | |
// +------------------------------+
{
short i = 0;
auto iter{ _pTextBuffer->GetCellDataAt(bufferSize.origin()) };
const auto segment{ bufferSize.width() / 9 };
const auto segment{ bufferSize.width() / 10 };
bool fill{ true };
while (iter.Pos() != docEnd)
{
bool fill{ true };
if (i % segment == 0)
if (iter.Pos().X == bufferSize.left())
{
fill = true;
}
else if (i % segment == 0)
{
fill = !fill;
}
if (fill)
{
_pTextBuffer->Write({ L"X" }, iter.Pos());
}
_pTextBuffer->Write({ fill ? L"X" : L" " }, iter.Pos());
++i;
++iter;

View file

@ -280,52 +280,50 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) noexc
void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
{
const auto& buffer = _pData->GetTextBuffer();
const auto bufferSize = _getBufferSize();
const auto bufferEnd = bufferSize.EndExclusive();
const auto bufferSize{ buffer.GetSize() };
const auto documentEnd{ _getDocumentEnd() };
// If we're past document end,
// set us to ONE BEFORE the document end.
// This allows us to expand properly.
if (bufferSize.CompareInBounds(_start, documentEnd, true) >= 0)
{
_start = documentEnd;
bufferSize.DecrementInBounds(_start, true);
}
if (unit == TextUnit_Character)
{
_start = buffer.GetGlyphStart(_start);
_end = buffer.GetGlyphEnd(_start);
_start = buffer.GetGlyphStart(_start, documentEnd);
_end = buffer.GetGlyphEnd(_start, documentEnd);
}
else if (unit <= TextUnit_Word)
{
// expand to word
_start = buffer.GetWordStart(_start, _wordDelimiters, true);
_end = buffer.GetWordEnd(_start, _wordDelimiters, true);
// GetWordEnd may return the actual end of the TextBuffer.
// If so, just set it to this value of bufferEnd
if (!bufferSize.IsInBounds(_end))
{
_end = bufferEnd;
}
_start = buffer.GetWordStart(_start, _wordDelimiters, true, documentEnd);
_end = buffer.GetWordEnd(_start, _wordDelimiters, true, documentEnd);
}
else if (unit <= TextUnit_Line)
{
if (_start == bufferEnd)
// expand to line
_start.X = 0;
if (_start.Y == documentEnd.y())
{
// Special case: if we are at the bufferEnd,
// move _start back one, instead of _end forward
_start.X = 0;
_start.Y = base::ClampSub(_start.Y, 1);
_end = bufferEnd;
// we're on the last line
_end = documentEnd;
bufferSize.IncrementInBounds(_end, true);
}
else
{
// expand to line
_start.X = 0;
_end.X = 0;
_end.Y = base::ClampAdd(_start.Y, 1);
}
}
else
{
// TODO GH#6986: properly handle "end of buffer" as last character
// instead of last cell
// expand to document
_start = bufferSize.Origin();
_end = bufferSize.EndExclusive();
_end = documentEnd;
}
}
@ -608,7 +606,7 @@ try
*ppRetVal = nullptr;
const std::wstring queryText{ text, SysStringLen(text) };
const auto bufferSize = _getBufferSize();
const auto bufferSize = _getOptimizedBufferSize();
const auto sensitivity = ignoreCase ? Search::Sensitivity::CaseInsensitive : Search::Sensitivity::CaseSensitive;
auto searchDirection = Search::Direction::Forward;
@ -1016,11 +1014,24 @@ try
_pData->UnlockConsole();
});
// We can abstract this movement by moving _start
// GH#7342: check if we're past the documentEnd
// If so, clamp each endpoint to the end of the document.
constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
const auto bufferSize{ _pData->GetTextBuffer().GetSize() };
const COORD documentEnd = _getDocumentEnd();
if (bufferSize.CompareInBounds(_start, documentEnd, true) > 0)
{
_start = documentEnd;
}
if (bufferSize.CompareInBounds(_end, documentEnd, true) > 0)
{
_end = documentEnd;
}
const auto wasDegenerate = IsDegenerate();
if (count != 0)
{
// We can abstract this movement by moving _start
constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
const auto preventBoundary = !wasDegenerate;
if (unit == TextUnit::TextUnit_Character)
{
@ -1028,13 +1039,7 @@ try
}
else if (unit <= TextUnit::TextUnit_Word)
{
// TODO GH#10925: passing in "true" instead of "preventBoundary"
// We still need to go through the process of writing
// tests, finding failing cases, and fixing them.
// For now, just use true because we've been doing that so far.
// The tests at the time of writing don't report any failures
// if we use one over the other.
_moveEndpointByUnitWord(count, endpoint, pRetVal, true);
_moveEndpointByUnitWord(count, endpoint, pRetVal, preventBoundary);
}
else if (unit <= TextUnit::TextUnit_Line)
{
@ -1080,6 +1085,26 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin
_pData->UnlockConsole();
});
// GH#7342: check if we're past the documentEnd
// If so, clamp each endpoint to the end of the document.
const auto bufferSize{ _pData->GetTextBuffer().GetSize() };
auto documentEnd = bufferSize.EndExclusive();
try
{
documentEnd = _getDocumentEnd();
}
CATCH_LOG();
if (bufferSize.CompareInBounds(_start, documentEnd, true) > 0)
{
_start = documentEnd;
}
if (bufferSize.CompareInBounds(_end, documentEnd, true) > 0)
{
_end = documentEnd;
}
try
{
if (unit == TextUnit::TextUnit_Character)
@ -1307,7 +1332,7 @@ const unsigned int UiaTextRangeBase::_getViewportHeight(const SMALL_RECT viewpor
// - <none>
// Return Value:
// - A viewport representing the portion of the TextBuffer that has valid text
const Viewport UiaTextRangeBase::_getBufferSize() const noexcept
const Viewport UiaTextRangeBase::_getOptimizedBufferSize() const noexcept
{
// we need to add 1 to the X/Y of textBufferEnd
// because we want the returned viewport to include this COORD
@ -1318,6 +1343,20 @@ const Viewport UiaTextRangeBase::_getBufferSize() const noexcept
return Viewport::FromDimensions({ 0, 0 }, width, height);
}
// We consider the "document end" to be the line beneath the cursor or
// last legible character (whichever is further down). In the event where
// the last legible character is on the last line of the buffer,
// we use the "end exclusive" position (left-most point on a line one past the end of the buffer).
// NOTE: "end exclusive" is naturally computed using the heuristic above.
const til::point UiaTextRangeBase::_getDocumentEnd() const
{
const auto optimizedBufferSize{ _getOptimizedBufferSize() };
const auto& buffer{ _pData->GetTextBuffer() };
const auto lastCharPos{ buffer.GetLastNonSpaceCharacter(optimizedBufferSize) };
const auto cursorPos{ buffer.GetCursor().GetPosition() };
return { optimizedBufferSize.Left(), std::max(lastCharPos.Y, cursorPos.Y) + 1 };
}
// Routine Description:
// - adds the relevant coordinate points from the row to coords.
// - it is assumed that startAnchor and endAnchor are within the same row
@ -1388,19 +1427,20 @@ void UiaTextRangeBase::_moveEndpointByUnitCharacter(_In_ const int moveCount,
bool success = true;
til::point target = GetEndpoint(endpoint);
const auto documentEnd{ _getDocumentEnd() };
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
{
switch (moveDirection)
{
case MovementDirection::Forward:
success = buffer.MoveToNextGlyph(target, allowBottomExclusive);
success = buffer.MoveToNextGlyph(target, allowBottomExclusive, documentEnd);
if (success)
{
(*pAmountMoved)++;
}
break;
case MovementDirection::Backward:
success = buffer.MoveToPreviousGlyph(target);
success = buffer.MoveToPreviousGlyph(target, documentEnd);
if (success)
{
(*pAmountMoved)--;
@ -1441,10 +1481,9 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
const bool allowBottomExclusive = !preventBufferEnd;
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
const auto& buffer = _pData->GetTextBuffer();
const auto bufferSize = _getBufferSize();
const auto bufferSize = buffer.GetSize();
const auto bufferOrigin = bufferSize.Origin();
const auto bufferEnd = bufferSize.EndExclusive();
const auto lastCharPos = buffer.GetLastNonSpaceCharacter(bufferSize);
const auto documentEnd = _getDocumentEnd();
auto resultPos = GetEndpoint(endpoint);
auto nextPos = resultPos;
@ -1457,18 +1496,18 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
{
case MovementDirection::Forward:
{
if (nextPos == bufferEnd)
if (bufferSize.CompareInBounds(nextPos, documentEnd, true) >= 0)
{
success = false;
}
else if (buffer.MoveToNextWord(nextPos, _wordDelimiters, lastCharPos))
else if (buffer.MoveToNextWord(nextPos, _wordDelimiters, documentEnd))
{
resultPos = nextPos;
(*pAmountMoved)++;
}
else if (allowBottomExclusive)
{
resultPos = bufferEnd;
resultPos = documentEnd;
(*pAmountMoved)++;
}
else
@ -1529,10 +1568,18 @@ void UiaTextRangeBase::_moveEndpointByUnitLine(_In_ const int moveCount,
const bool allowBottomExclusive = !preventBoundary;
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
const auto bufferSize = _getBufferSize();
const auto bufferSize = _getOptimizedBufferSize();
auto documentEnd{ bufferSize.EndExclusive() };
try
{
documentEnd = _getDocumentEnd();
}
CATCH_LOG();
bool success = true;
auto resultPos = GetEndpoint(endpoint);
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
{
auto nextPos = resultPos;
@ -1540,22 +1587,29 @@ void UiaTextRangeBase::_moveEndpointByUnitLine(_In_ const int moveCount,
{
case MovementDirection::Forward:
{
// can't move past end
if (nextPos.Y >= bufferSize.BottomInclusive())
if (nextPos.Y >= documentEnd.Y)
{
if (preventBoundary || nextPos == bufferSize.EndExclusive())
{
success = false;
break;
}
// Corner Case: we're past the limit
// Clamp us to the limit
resultPos = documentEnd;
success = false;
}
nextPos.X = bufferSize.RightInclusive();
success = bufferSize.IncrementInBounds(nextPos, allowBottomExclusive);
if (success)
else if (preventBoundary && nextPos.Y == base::ClampSub(documentEnd.Y, 1))
{
resultPos = nextPos;
(*pAmountMoved)++;
// Corner Case: we're just before the limit
// and we're not allowed onto the exclusive end.
// Fail to move.
success = false;
}
else
{
nextPos.X = bufferSize.RightInclusive();
success = bufferSize.IncrementInBounds(nextPos, allowBottomExclusive);
if (success)
{
resultPos = nextPos;
(*pAmountMoved)++;
}
}
break;
}
@ -1621,15 +1675,21 @@ void UiaTextRangeBase::_moveEndpointByUnitDocument(_In_ const int moveCount,
}
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
const auto bufferSize = _getBufferSize();
const auto bufferSize = _getOptimizedBufferSize();
const auto target = GetEndpoint(endpoint);
switch (moveDirection)
{
case MovementDirection::Forward:
{
const auto documentEnd = bufferSize.EndExclusive();
if (preventBoundary || target == documentEnd)
auto documentEnd{ bufferSize.EndExclusive() };
try
{
documentEnd = _getDocumentEnd();
}
CATCH_LOG();
if (preventBoundary || bufferSize.CompareInBounds(target, documentEnd, true) >= 0)
{
return;
}

View file

@ -149,7 +149,8 @@ namespace Microsoft::Console::Types
virtual const COORD _getScreenFontSize() const;
const unsigned int _getViewportHeight(const SMALL_RECT viewport) const noexcept;
const Viewport _getBufferSize() const noexcept;
const Viewport _getOptimizedBufferSize() const noexcept;
const til::point _getDocumentEnd() const;
void _getBoundingRect(const til::rectangle textRect, _Inout_ std::vector<double>& coords) const;

View file

@ -94,4 +94,5 @@ namespace Microsoft::Console::Utils
GUID CreateV5Uuid(const GUID& namespaceGuid, const gsl::span<const gsl::byte> name);
bool IsElevated();
}

View file

@ -5,6 +5,8 @@
#include "inc/utils.hpp"
#include "inc/colorTable.hpp"
#include <wil/token_helpers.h >
using namespace Microsoft::Console;
// Routine Description:
@ -559,3 +561,33 @@ GUID Utils::CreateV5Uuid(const GUID& namespaceGuid, const gsl::span<const gsl::b
::memcpy_s(&newGuid, sizeof(GUID), buffer.data(), sizeof(GUID));
return EndianSwap(newGuid);
}
bool Utils::IsElevated()
{
static bool isElevated = []() {
try
{
wil::unique_handle processToken{ GetCurrentProcessToken() };
const auto elevationType = wil::get_token_information<TOKEN_ELEVATION_TYPE>(processToken.get());
const auto elevationState = wil::get_token_information<TOKEN_ELEVATION>(processToken.get());
if (elevationType == TokenElevationTypeDefault && elevationState.TokenIsElevated)
{
// In this case, the user has UAC entirely disabled. This is sort of
// weird, we treat this like the user isn't an admin at all. There's no
// separation of powers, so the things we normally want to gate on
// "having special powers" doesn't apply.
//
// See GH#7754, GH#11096
return false;
}
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
}
}();
return isElevated;
}

View file

@ -21,7 +21,6 @@ $result = "// Copyright (c) Microsoft Corporation.
// These were generated by tools\TestTableWriter\GenerateTests.ps1
// Read tools\TestTableWriter\README.md for more details"
# TODO: THIS IS PROBABLY WRONG. Bottom/Right are exclusive (I think?)
# 1. Define a few helpful variables to make life easier.
$result += "
// Define a few helpful variables
@ -29,11 +28,16 @@ constexpr til::rectangle bufferSize{ 0, 0, 80, 300 };
constexpr short midX{ 40 };
constexpr short midY{ 150 };
constexpr short midPopulatedY{ 75 };
constexpr short segment0{ 0 };
constexpr short segment1{ 16 };
constexpr short segment2{ 32 };
constexpr short segment3{ 48 };
constexpr short segment4{ 64 };
constexpr til::point origin{ 0, 0 };
constexpr til::point midTop{ midX, 0 };
constexpr til::point midHistory{ midX, midPopulatedY };
constexpr til::point midDocEnd{ midX, midY };
constexpr til::point lastCharPos{ 79, midY };
constexpr til::point lastCharPos{ 72, midY };
constexpr til::point docEnd{ 0, midY + 1 };
constexpr til::point midEmptySpace{ midX, midY + midPopulatedY };
constexpr til::point bufferEnd{ 79, 299 };
@ -55,17 +59,33 @@ foreach ($test in $tests)
$vars.Remove("origin") > $null;
$vars.Remove("midTop") > $null;
$vars.Remove("midHistory") > $null;
$vars.Remove("midDocEnd") > $null;
$vars.Remove("lastCharPos") > $null;
$vars.Remove("docEnd") > $null;
$vars.Remove("midEmptySpace") > $null;
$vars.Remove("bufferEnd") > $null;
$vars.Remove("endExclusive") > $null;
# 3.b. Now all of the remaining vars can be deduced from standard vars
foreach ($var in $vars)
{
# Extract the standard var from the name
$standardVar = $var.Contains("Left") ? $var.Split("Left") : $var.Substring(0, $var.length - 3);
# Figure out what heuristic to use
$segmentHeuristic = $var.Contains("segment");
$leftHeuristic = $var.Contains("Left");
$movementHeuristic = $var -match ".*\d+.*";
# i. Contains number --> requires movement
if ($var -match ".*\d+.*")
# i. Contains "segment" --> define point at the beginning of a text segment
if ($segmentHeuristic)
{
$result += "constexpr til::point {0}{{ {1}, {2}.y() }};" -f $var, $var.Substring(0, 8), $var.Substring(9, $var.Length - $var.IndexOf("L") - 1);
}
# ii. Contains number --> requires movement
elseif ($movementHeuristic)
{
# everything excluding last 3 characters denotes the standard variable
# we're based on.
$standardVar = $var.Substring(0, $var.length - 3);
# 3rd to last character denotes the movement direction
# P --> plus/forwards
# M --> minus/backwards
@ -101,10 +121,11 @@ foreach ($var in $vars)
Default { Write-Host "Error: unknown variable movement type" -ForegroundColor Red }
}
}
# ii. Contains "Left" --> set X to left
elseif ($var.Contains("Left"))
# iii. Contains "Left" --> set X to left
elseif ($leftHeuristic)
{
$result += "constexpr til::point " + $var + "{ bufferSize.left(), " + $standardVar + ".y() };";
$standardVar = $var.Split("Left")[0]
$result += "constexpr til::point {0}{{ bufferSize.left(), {1}.y() }};" -f $var, $standardVar;
}
$result += "`n";
}

View file

@ -44,21 +44,21 @@ The Test Table Writer was written as a method to generate UI Automation tests fo
- "Command Arguments" --> `$(TargetPath) /name:*uiatextrange*generated* /inproc`
# Position chart
The text buffer is assumed to be partially filled. Specifically, the top half of the text buffer contains text, and each row is filled with 9 segments of alternating text. For visualization,
The text buffer is assumed to be partially filled. Specifically, the top half of the text buffer contains text, and each row is filled with 10 segments of alternating text. For visualization,
the ascii diagram below shows what the text buffer may look like.
```
+---------------------------+
|1XX XXX X2X XXX XXX|
|XXX XXX XXX XXX XXX|
|XXX XXX X3X XXX XXX|
|XXX XXX XXX XXX XXX|
|XXX XXX X4X XXX XX5|
|6 |
| |
| 7 |
| |
| 8|
+---------------------------+
+------------------------------+
|1XX XXX X2X XXX XXX |
|XXX XXX XXX XXX XXX |
|XXX XXX X3X XXX XXX |
|XXX XXX XXX XXX XXX |
|XXX XXX X4X XXX XX5 |
|6 |
| |
| 7 |
| |
| 8|
+------------------------------+
9
```
The following positions are being tested:
@ -84,6 +84,11 @@ Each position above already has a predefined variable name. However, a few heuri
- `C`: move by character. For simplicity, assumes that each character is one-cell wide.
- `<name>P<number>L`, `<name>M<number>L`:
- same as above, except move by line. For simplicity, assumes that you won't hit a buffer boundary.
- `segment#L<name>`
- This is mainly used for word navigation to target a segment of text in the buffer.
- `segment#` refers to the beginning of a segment of text in a row. The leftmost run of text is `segment0`, whereas the rightmost run of text is `segment4`.
- `L<name>` refers to the line number we're targeting, relative to the `<name>` variable. For example, `Lorigin` means that the y-position should be that of `origin`.
- Overall, this allows us to target the beginning of segments of text. `segment0Lorigin` and `segment0LmidTop` both refer to the beginning of the first segment of text on the top line (aka `origin`).
# Helpful terms and concepts
- *degenerate*: the text range encompasses no text. Also, means the start and end endpoints are the same.

View file

@ -62,195 +62,280 @@ FALSE,3,TextUnit_Line,5,midHistory,midHistoryP1C,5,midHistoryP5L,midHistoryP6L,F
TRUE,1,TextUnit_Document,-5,origin,origin,0,origin,origin,FALSE
TRUE,1,TextUnit_Document,-1,origin,origin,0,origin,origin,FALSE
TRUE,1,TextUnit_Document,0,origin,origin,0,origin,origin,FALSE
TRUE,1,TextUnit_Document,1,origin,origin,1,endExclusive,endExclusive,FALSE
TRUE,1,TextUnit_Document,5,origin,origin,1,endExclusive,endExclusive,FALSE
FALSE,1,TextUnit_Document,-5,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,-1,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,0,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,1,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,5,origin,originP1C,0,origin,endExclusive,FALSE
TRUE,1,TextUnit_Document,1,origin,origin,1,docEnd,docEnd,FALSE
TRUE,1,TextUnit_Document,5,origin,origin,1,docEnd,docEnd,FALSE
FALSE,1,TextUnit_Document,-5,origin,originP1C,0,origin,docEnd,FALSE
FALSE,1,TextUnit_Document,-1,origin,originP1C,0,origin,docEnd,FALSE
FALSE,1,TextUnit_Document,0,origin,originP1C,0,origin,docEnd,FALSE
FALSE,1,TextUnit_Document,1,origin,originP1C,0,origin,docEnd,FALSE
FALSE,1,TextUnit_Document,5,origin,originP1C,0,origin,docEnd,FALSE
TRUE,2,TextUnit_Document,-5,midTop,midTop,-1,origin,origin,FALSE
TRUE,2,TextUnit_Document,-1,midTop,midTop,-1,origin,origin,FALSE
TRUE,2,TextUnit_Document,0,midTop,midTop,0,midTop,midTop,FALSE
TRUE,2,TextUnit_Document,1,midTop,midTop,1,endExclusive,endExclusive,FALSE
TRUE,2,TextUnit_Document,5,midTop,midTop,1,endExclusive,endExclusive,FALSE
FALSE,2,TextUnit_Document,-5,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,-1,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,0,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,1,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,5,midTop,midTopP1C,0,origin,endExclusive,FALSE
TRUE,2,TextUnit_Document,1,midTop,midTop,1,docEnd,docEnd,FALSE
TRUE,2,TextUnit_Document,5,midTop,midTop,1,docEnd,docEnd,FALSE
FALSE,2,TextUnit_Document,-5,midTop,midTopP1C,0,origin,docEnd,FALSE
FALSE,2,TextUnit_Document,-1,midTop,midTopP1C,0,origin,docEnd,FALSE
FALSE,2,TextUnit_Document,0,midTop,midTopP1C,0,origin,docEnd,FALSE
FALSE,2,TextUnit_Document,1,midTop,midTopP1C,0,origin,docEnd,FALSE
FALSE,2,TextUnit_Document,5,midTop,midTopP1C,0,origin,docEnd,FALSE
TRUE,3,TextUnit_Document,-5,midHistory,midHistory,-1,origin,origin,FALSE
TRUE,3,TextUnit_Document,-1,midHistory,midHistory,-1,origin,origin,FALSE
TRUE,3,TextUnit_Document,0,midHistory,midHistory,0,midHistory,midHistory,FALSE
TRUE,3,TextUnit_Document,1,midHistory,midHistory,1,endExclusive,endExclusive,FALSE
TRUE,3,TextUnit_Document,5,midHistory,midHistory,1,endExclusive,endExclusive,FALSE
FALSE,3,TextUnit_Document,-5,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,-1,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,0,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,1,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,5,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
TRUE,8,TextUnit_Character,-5,bufferEnd,bufferEnd,-5,bufferEndM5C,bufferEndM5C,FALSE
TRUE,8,TextUnit_Character,-1,bufferEnd,bufferEnd,-1,bufferEndM1C,bufferEndM1C,FALSE
TRUE,8,TextUnit_Character,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
TRUE,8,TextUnit_Character,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
TRUE,8,TextUnit_Character,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
FALSE,8,TextUnit_Character,-5,bufferEnd,endExclusive,-5,bufferEndM5C,bufferEndM4C,FALSE
FALSE,8,TextUnit_Character,-1,bufferEnd,endExclusive,-1,bufferEndM1C,bufferEnd,FALSE
FALSE,8,TextUnit_Character,0,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
FALSE,8,TextUnit_Character,1,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
FALSE,8,TextUnit_Character,5,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
TRUE,8,TextUnit_Line,-5,bufferEnd,bufferEnd,-5,bufferEndM4L,bufferEndM4L,FALSE
TRUE,8,TextUnit_Line,-1,bufferEnd,bufferEnd,-1,bufferEndLeft,bufferEndLeft,FALSE
TRUE,8,TextUnit_Line,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
TRUE,8,TextUnit_Line,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
TRUE,8,TextUnit_Line,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
FALSE,8,TextUnit_Line,-5,bufferEnd,endExclusive,-5,bufferEndM5L,bufferEndM4L,TRUE
FALSE,8,TextUnit_Line,-1,bufferEnd,endExclusive,-1,bufferEndM1L,bufferEndLeft,TRUE
FALSE,8,TextUnit_Line,0,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
FALSE,8,TextUnit_Line,1,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
FALSE,8,TextUnit_Line,5,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
TRUE,3,TextUnit_Document,1,midHistory,midHistory,1,docEnd,docEnd,FALSE
TRUE,3,TextUnit_Document,5,midHistory,midHistory,1,docEnd,docEnd,FALSE
FALSE,3,TextUnit_Document,-5,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,-1,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,0,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,1,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,5,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
TRUE,8,TextUnit_Character,-5,bufferEnd,bufferEnd,-5,docEndM5C,docEndM5C,FALSE
TRUE,8,TextUnit_Character,-1,bufferEnd,bufferEnd,-1,docEndM1C,docEndM1C,FALSE
TRUE,8,TextUnit_Character,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Character,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Character,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Character,-5,bufferEnd,endExclusive,-5,docEndM5C,docEndM5C,FALSE
FALSE,8,TextUnit_Character,-1,bufferEnd,endExclusive,-1,docEndM1C,docEndM1C,FALSE
FALSE,8,TextUnit_Character,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Character,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Character,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Line,-5,bufferEnd,bufferEnd,-5,docEndM5L,docEndM5L,FALSE
TRUE,8,TextUnit_Line,-1,bufferEnd,bufferEnd,-1,docEndM1L,docEndM1L,FALSE
TRUE,8,TextUnit_Line,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Line,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Line,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Line,-5,bufferEnd,endExclusive,-5,docEndM5L,docEndM5L,FALSE
FALSE,8,TextUnit_Line,-1,bufferEnd,endExclusive,-1,docEndM1L,docEndM1L,FALSE
FALSE,8,TextUnit_Line,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Line,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Line,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Document,-5,bufferEnd,bufferEnd,-1,origin,origin,FALSE
TRUE,8,TextUnit_Document,-1,bufferEnd,bufferEnd,-1,origin,origin,FALSE
TRUE,8,TextUnit_Document,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
TRUE,8,TextUnit_Document,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
TRUE,8,TextUnit_Document,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
FALSE,8,TextUnit_Document,-5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,-1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,0,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
TRUE,9,TextUnit_Character,-5,endExclusive,endExclusive,-5,bufferEndM4C,bufferEndM4C,FALSE
TRUE,9,TextUnit_Character,-1,endExclusive,endExclusive,-1,bufferEnd,bufferEnd,FALSE
TRUE,9,TextUnit_Character,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Character,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Character,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Line,-5,endExclusive,endExclusive,-5,bufferEndM4L,bufferEndM4L,FALSE
TRUE,9,TextUnit_Line,-1,endExclusive,endExclusive,-1,bufferEndLeft,bufferEndLeft,FALSE
TRUE,9,TextUnit_Line,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Line,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Line,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,8,TextUnit_Document,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Document,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Document,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Document,-5,bufferEnd,endExclusive,-1,origin,origin,FALSE
FALSE,8,TextUnit_Document,-1,bufferEnd,endExclusive,-1,origin,origin,FALSE
FALSE,8,TextUnit_Document,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Document,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Document,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Character,-5,endExclusive,endExclusive,-5,docEndM5C,docEndM5C,FALSE
TRUE,9,TextUnit_Character,-1,endExclusive,endExclusive,-1,docEndM1C,docEndM1C,FALSE
TRUE,9,TextUnit_Character,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Character,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Character,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Line,-5,endExclusive,endExclusive,-5,docEndM5L,docEndM5L,FALSE
TRUE,9,TextUnit_Line,-1,endExclusive,endExclusive,-1,docEndM1L,docEndM1L,FALSE
TRUE,9,TextUnit_Line,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Line,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Line,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Document,-5,endExclusive,endExclusive,-1,origin,origin,FALSE
TRUE,9,TextUnit_Document,-1,endExclusive,endExclusive,-1,origin,origin,FALSE
TRUE,9,TextUnit_Document,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Document,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Document,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,4,TextUnit_Character,-5,midDocEnd,midDocEnd,-5,midDocEndM5C,midDocEndM5C,TRUE
TRUE,4,TextUnit_Character,-1,midDocEnd,midDocEnd,-1,midDocEndM1C,midDocEndM1C,TRUE
TRUE,4,TextUnit_Character,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
TRUE,4,TextUnit_Character,1,midDocEnd,midDocEnd,1,midDocEndP1C,midDocEndP1C,TRUE
TRUE,4,TextUnit_Character,5,midDocEnd,midDocEnd,5,midDocEndP5C,midDocEndP5C,TRUE
FALSE,4,TextUnit_Character,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5C,midDocEndM4C,TRUE
FALSE,4,TextUnit_Character,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1C,midDocEnd,TRUE
FALSE,4,TextUnit_Character,0,midDocEnd,midDocEndP1C,0,midDocEnd,midDocEndP1C,TRUE
FALSE,4,TextUnit_Character,1,midDocEnd,midDocEndP1C,1,midDocEndP1C,midDocEndP2C,TRUE
FALSE,4,TextUnit_Character,5,midDocEnd,midDocEndP1C,5,midDocEndP5C,midDocEndP6C,TRUE
TRUE,4,TextUnit_Line,-5,midDocEnd,midDocEnd,-5,midDocEndM4L,midDocEndM4L,TRUE
TRUE,4,TextUnit_Line,-1,midDocEnd,midDocEnd,-1,midDocEndLeft,midDocEndLeft,TRUE
TRUE,4,TextUnit_Line,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
TRUE,4,TextUnit_Line,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
TRUE,4,TextUnit_Line,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
FALSE,4,TextUnit_Line,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5L,midDocEndM4L,TRUE
FALSE,4,TextUnit_Line,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1L,midDocEndLeft,TRUE
FALSE,4,TextUnit_Line,0,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
FALSE,4,TextUnit_Line,1,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
FALSE,4,TextUnit_Line,5,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
TRUE,4,TextUnit_Document,-5,midDocEnd,midDocEnd,-1,origin,origin,TRUE
TRUE,4,TextUnit_Document,-1,midDocEnd,midDocEnd,-1,origin,origin,TRUE
TRUE,4,TextUnit_Document,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
TRUE,4,TextUnit_Document,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
TRUE,4,TextUnit_Document,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
FALSE,4,TextUnit_Document,-5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,-1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,0,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
TRUE,5,TextUnit_Character,-5,lastCharPos,lastCharPos,-5,lastCharPosM5C,lastCharPosM5C,TRUE
TRUE,5,TextUnit_Character,-1,lastCharPos,lastCharPos,-1,lastCharPosM1C,lastCharPosM1C,TRUE
TRUE,5,TextUnit_Character,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
TRUE,5,TextUnit_Character,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
TRUE,5,TextUnit_Character,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
FALSE,5,TextUnit_Character,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5C,lastCharPosM4C,TRUE
FALSE,5,TextUnit_Character,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1C,lastCharPos,TRUE
FALSE,5,TextUnit_Character,0,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
FALSE,5,TextUnit_Character,1,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
FALSE,5,TextUnit_Character,5,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
TRUE,5,TextUnit_Line,-5,lastCharPos,lastCharPos,-5,lastCharPosM4L,lastCharPosM4L,TRUE
TRUE,5,TextUnit_Line,-1,lastCharPos,lastCharPos,-1,lastCharPosLeft,lastCharPosLeft,TRUE
TRUE,5,TextUnit_Line,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
TRUE,5,TextUnit_Line,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
TRUE,5,TextUnit_Line,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
FALSE,5,TextUnit_Line,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5L,lastCharPosM4L,TRUE
FALSE,5,TextUnit_Line,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1L,lastCharPosLeft,TRUE
FALSE,5,TextUnit_Line,0,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
FALSE,5,TextUnit_Line,1,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
FALSE,5,TextUnit_Line,5,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
TRUE,5,TextUnit_Document,-5,lastCharPos,lastCharPos,-1,origin,origin,TRUE
TRUE,5,TextUnit_Document,-1,lastCharPos,lastCharPos,-1,origin,origin,TRUE
TRUE,5,TextUnit_Document,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
TRUE,5,TextUnit_Document,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
TRUE,5,TextUnit_Document,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
FALSE,5,TextUnit_Document,-5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,-1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,0,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
TRUE,6,TextUnit_Character,-5,docEnd,docEnd,-5,docEndM5C,docEndM5C,TRUE
TRUE,6,TextUnit_Character,-1,docEnd,docEnd,-1,docEndM1C,docEndM1C,TRUE
TRUE,6,TextUnit_Character,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Character,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Character,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Character,-5,docEnd,docEndP1C,-5,docEndM5C,docEndM5C,TRUE
FALSE,6,TextUnit_Character,-1,docEnd,docEndP1C,-1,docEndM1C,docEndM1C,TRUE
FALSE,6,TextUnit_Character,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Character,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Character,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Line,-5,docEnd,docEnd,-5,docEndM4L,docEndM4L,TRUE
TRUE,6,TextUnit_Line,-1,docEnd,docEnd,-1,docEndLeft,docEndLeft,TRUE
TRUE,6,TextUnit_Line,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Line,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Line,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Line,-5,docEnd,docEndP1C,-5,docEndM4L,docEndM4L,TRUE
FALSE,6,TextUnit_Line,-1,docEnd,docEndP1C,-1,docEndLeft,docEndLeft,TRUE
FALSE,6,TextUnit_Line,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Line,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Line,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Document,-5,docEnd,docEnd,-1,origin,origin,TRUE
TRUE,6,TextUnit_Document,-1,docEnd,docEnd,-1,origin,origin,TRUE
TRUE,6,TextUnit_Document,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Document,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Document,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Document,-5,docEnd,docEndP1C,-1,origin,origin,TRUE
FALSE,6,TextUnit_Document,-1,docEnd,docEndP1C,-1,origin,origin,TRUE
FALSE,6,TextUnit_Document,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Document,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Document,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpace,-5,docEndM5C,docEndM5C,TRUE
TRUE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpace,-1,docEndM1C,docEndM1C,TRUE
TRUE,7,TextUnit_Character,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Character,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Character,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5C,docEndM5C,TRUE
FALSE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1C,docEndM1C,TRUE
FALSE,7,TextUnit_Character,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Character,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Character,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpace,-5,docEndM4L,docEndM4L,TRUE
TRUE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpace,-1,docEndLeft,docEndLeft,TRUE
TRUE,7,TextUnit_Line,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Line,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Line,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM4L,docEndM4L,TRUE
FALSE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndLeft,docEndLeft,TRUE
FALSE,7,TextUnit_Line,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Line,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Line,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE
TRUE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE
TRUE,7,TextUnit_Document,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Document,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Document,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE
FALSE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE
FALSE,7,TextUnit_Document,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Document,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Document,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
TRUE,9,TextUnit_Document,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Document,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Document,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,4,TextUnit_Character,-5,midDocEnd,midDocEnd,-5,midDocEndM5C,midDocEndM5C,FALSE
TRUE,4,TextUnit_Character,-1,midDocEnd,midDocEnd,-1,midDocEndM1C,midDocEndM1C,FALSE
TRUE,4,TextUnit_Character,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Character,1,midDocEnd,midDocEnd,1,midDocEndP1C,midDocEndP1C,FALSE
TRUE,4,TextUnit_Character,5,midDocEnd,midDocEnd,5,midDocEndP5C,midDocEndP5C,FALSE
FALSE,4,TextUnit_Character,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5C,midDocEndM4C,FALSE
FALSE,4,TextUnit_Character,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1C,midDocEnd,FALSE
FALSE,4,TextUnit_Character,0,midDocEnd,midDocEndP1C,0,midDocEnd,midDocEndP1C,FALSE
FALSE,4,TextUnit_Character,1,midDocEnd,midDocEndP1C,1,midDocEndP1C,midDocEndP2C,FALSE
FALSE,4,TextUnit_Character,5,midDocEnd,midDocEndP1C,5,midDocEndP5C,midDocEndP6C,FALSE
TRUE,4,TextUnit_Line,-5,midDocEnd,midDocEnd,-5,midDocEndM4L,midDocEndM4L,FALSE
TRUE,4,TextUnit_Line,-1,midDocEnd,midDocEnd,-1,midDocEndLeft,midDocEndLeft,FALSE
TRUE,4,TextUnit_Line,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Line,1,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
TRUE,4,TextUnit_Line,5,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
FALSE,4,TextUnit_Line,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5L,midDocEndM4L,FALSE
FALSE,4,TextUnit_Line,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1L,midDocEndLeft,FALSE
FALSE,4,TextUnit_Line,0,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
FALSE,4,TextUnit_Line,1,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
FALSE,4,TextUnit_Line,5,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
TRUE,4,TextUnit_Document,-5,midDocEnd,midDocEnd,-1,origin,origin,FALSE
TRUE,4,TextUnit_Document,-1,midDocEnd,midDocEnd,-1,origin,origin,FALSE
TRUE,4,TextUnit_Document,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Document,1,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
TRUE,4,TextUnit_Document,5,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
FALSE,4,TextUnit_Document,-5,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,-1,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,0,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,1,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,5,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
TRUE,5,TextUnit_Character,-5,lastCharPos,lastCharPos,-5,lastCharPosM5C,lastCharPosM5C,FALSE
TRUE,5,TextUnit_Character,-1,lastCharPos,lastCharPos,-1,lastCharPosM1C,lastCharPosM1C,FALSE
TRUE,5,TextUnit_Character,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Character,1,lastCharPos,lastCharPos,1,lastCharPosP1C,lastCharPosP1C,FALSE
TRUE,5,TextUnit_Character,5,lastCharPos,lastCharPos,5,lastCharPosP5C,lastCharPosP5C,FALSE
FALSE,5,TextUnit_Character,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5C,lastCharPosM4C,FALSE
FALSE,5,TextUnit_Character,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1C,lastCharPos,FALSE
FALSE,5,TextUnit_Character,0,lastCharPos,lastCharPosP1C,0,lastCharPos,lastCharPosP1C,FALSE
FALSE,5,TextUnit_Character,1,lastCharPos,lastCharPosP1C,1,lastCharPosP1C,lastCharPosP2C,FALSE
FALSE,5,TextUnit_Character,5,lastCharPos,lastCharPosP1C,5,lastCharPosP5C,lastCharPosP6C,FALSE
TRUE,5,TextUnit_Line,-5,lastCharPos,lastCharPos,-5,lastCharPosM4L,lastCharPosM4L,FALSE
TRUE,5,TextUnit_Line,-1,lastCharPos,lastCharPos,-1,lastCharPosLeft,lastCharPosLeft,FALSE
TRUE,5,TextUnit_Line,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Line,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
TRUE,5,TextUnit_Line,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
FALSE,5,TextUnit_Line,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5L,lastCharPosM4L,FALSE
FALSE,5,TextUnit_Line,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1L,lastCharPosLeft,FALSE
FALSE,5,TextUnit_Line,0,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
FALSE,5,TextUnit_Line,1,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
FALSE,5,TextUnit_Line,5,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
TRUE,5,TextUnit_Document,-5,lastCharPos,lastCharPos,-1,origin,origin,FALSE
TRUE,5,TextUnit_Document,-1,lastCharPos,lastCharPos,-1,origin,origin,FALSE
TRUE,5,TextUnit_Document,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Document,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
TRUE,5,TextUnit_Document,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
FALSE,5,TextUnit_Document,-5,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,-1,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,0,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,1,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,5,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
TRUE,6,TextUnit_Character,-5,docEnd,docEnd,-5,docEndM5C,docEndM5C,FALSE
TRUE,6,TextUnit_Character,-1,docEnd,docEnd,-1,docEndM1C,docEndM1C,FALSE
TRUE,6,TextUnit_Character,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Character,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Character,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Character,-5,docEnd,docEndP1C,-5,docEndM5C,docEndM5C,FALSE
FALSE,6,TextUnit_Character,-1,docEnd,docEndP1C,-1,docEndM1C,docEndM1C,FALSE
FALSE,6,TextUnit_Character,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Character,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Character,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Line,-5,docEnd,docEnd,-5,docEndM5L,docEndM5L,FALSE
TRUE,6,TextUnit_Line,-1,docEnd,docEnd,-1,docEndM1L,docEndM1L,FALSE
TRUE,6,TextUnit_Line,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Line,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Line,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Line,-5,docEnd,docEndP1C,-5,docEndM5L,docEndM5L,FALSE
FALSE,6,TextUnit_Line,-1,docEnd,docEndP1C,-1,docEndM1L,docEndM1L,FALSE
FALSE,6,TextUnit_Line,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Line,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Line,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Document,-5,docEnd,docEnd,-1,origin,origin,FALSE
TRUE,6,TextUnit_Document,-1,docEnd,docEnd,-1,origin,origin,FALSE
TRUE,6,TextUnit_Document,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Document,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Document,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Document,-5,docEnd,docEndP1C,-1,origin,origin,FALSE
FALSE,6,TextUnit_Document,-1,docEnd,docEndP1C,-1,origin,origin,FALSE
FALSE,6,TextUnit_Document,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Document,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Document,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpace,-5,docEndM5C,docEndM5C,FALSE
TRUE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpace,-1,docEndM1C,docEndM1C,FALSE
TRUE,7,TextUnit_Character,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Character,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Character,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5C,docEndM5C,FALSE
FALSE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1C,docEndM1C,FALSE
FALSE,7,TextUnit_Character,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Character,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Character,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpace,-5,docEndM5L,docEndM5L,FALSE
TRUE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpace,-1,docEndM1L,docEndM1L,FALSE
TRUE,7,TextUnit_Line,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Line,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Line,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5L,docEndM5L,FALSE
FALSE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1L,docEndM1L,FALSE
FALSE,7,TextUnit_Line,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Line,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Line,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpace,-1,origin,origin,FALSE
TRUE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpace,-1,origin,origin,FALSE
TRUE,7,TextUnit_Document,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Document,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Document,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,FALSE
FALSE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,FALSE
FALSE,7,TextUnit_Document,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Document,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Document,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
TRUE,1,TextUnit_Word,-5,origin,origin,0,origin,origin,FALSE
TRUE,1,TextUnit_Word,-1,origin,origin,0,origin,origin,FALSE
TRUE,1,TextUnit_Word,0,origin,origin,0,origin,origin,FALSE
TRUE,1,TextUnit_Word,1,origin,origin,1,segment1LmidTop,segment1LmidTop,FALSE
TRUE,1,TextUnit_Word,5,origin,origin,5,segment0LmidTopP1L,segment0LmidTopP1L,FALSE
FALSE,1,TextUnit_Word,-5,origin,originP1C,0,origin,segment1LmidTop,FALSE
FALSE,1,TextUnit_Word,-1,origin,originP1C,0,origin,segment1LmidTop,FALSE
FALSE,1,TextUnit_Word,0,origin,originP1C,0,origin,segment1LmidTop,FALSE
FALSE,1,TextUnit_Word,1,origin,originP1C,1,segment1LmidTop,segment2LmidTop,FALSE
FALSE,1,TextUnit_Word,5,origin,originP1C,5,segment0LmidTopP1L,segment1LmidTopP1L,FALSE
TRUE,2,TextUnit_Word,-5,midTop,midTop,-3,origin,origin,TRUE
TRUE,2,TextUnit_Word,-1,midTop,midTop,-1,segment2LmidTop,segment2LmidTop,TRUE
TRUE,2,TextUnit_Word,0,midTop,midTop,0,midTop,midTop,FALSE
TRUE,2,TextUnit_Word,1,midTop,midTop,1,segment3LmidTop,segment3LmidTop,FALSE
TRUE,2,TextUnit_Word,5,midTop,midTop,5,segment2LmidTopP1L,segment2LmidTopP1L,FALSE
FALSE,2,TextUnit_Word,-5,midTop,midTopP1C,-2,origin,segment1LmidTop,FALSE
FALSE,2,TextUnit_Word,-1,midTop,midTopP1C,-1,segment1LmidTop,segment2LmidTop,FALSE
FALSE,2,TextUnit_Word,0,midTop,midTopP1C,0,segment2LmidTop,segment3LmidTop,FALSE
FALSE,2,TextUnit_Word,1,midTop,midTopP1C,1,segment3LmidTop,segment4LmidTop,FALSE
FALSE,2,TextUnit_Word,5,midTop,midTopP1C,5,segment2LmidTopP1L,segment3LmidTopP1L,FALSE
TRUE,3,TextUnit_Word,-5,midHistory,midHistory,-5,segment3LmidHistoryM1L,segment3LmidHistoryM1L,TRUE
TRUE,3,TextUnit_Word,-1,midHistory,midHistory,-1,segment2LmidHistory,segment2LmidHistory,TRUE
TRUE,3,TextUnit_Word,0,midHistory,midHistory,0,midHistory,midHistory,FALSE
TRUE,3,TextUnit_Word,1,midHistory,midHistory,1,segment3LmidHistory,segment3LmidHistory,FALSE
TRUE,3,TextUnit_Word,5,midHistory,midHistory,5,segment2LmidHistoryP1L,segment2LmidHistoryP1L,FALSE
FALSE,3,TextUnit_Word,-5,midHistory,midHistoryP1C,-5,segment3LmidHistoryM1L,segment4LmidHistoryM1L,TRUE
FALSE,3,TextUnit_Word,-1,midHistory,midHistoryP1C,-1,segment1LmidHistory,segment2LmidHistory,FALSE
FALSE,3,TextUnit_Word,0,midHistory,midHistoryP1C,0,segment2LmidHistory,segment3LmidHistory,FALSE
FALSE,3,TextUnit_Word,1,midHistory,midHistoryP1C,1,segment3LmidHistory,segment4LmidHistory,FALSE
FALSE,3,TextUnit_Word,5,midHistory,midHistoryP1C,5,segment2LmidHistoryP1L,segment3LmidHistoryP1L,FALSE
TRUE,4,TextUnit_Word,-5,midDocEnd,midDocEnd,-5,segment3LmidDocEndM1L,segment3LmidDocEndM1L,TRUE
TRUE,4,TextUnit_Word,-1,midDocEnd,midDocEnd,-1,segment2LmidDocEnd,segment2LmidDocEnd,TRUE
TRUE,4,TextUnit_Word,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Word,1,midDocEnd,midDocEnd,1,segment3LmidDocEnd,segment3LmidDocEnd,FALSE
TRUE,4,TextUnit_Word,5,midDocEnd,midDocEnd,3,docEnd,docEnd,FALSE
FALSE,4,TextUnit_Word,-5,midDocEnd,midDocEndP1C,-5,segment3LmidDocEndM1L,segment4LmidDocEndM1L,TRUE
FALSE,4,TextUnit_Word,-1,midDocEnd,midDocEndP1C,-1,segment1LmidDocEnd,segment2LmidDocEnd,FALSE
FALSE,4,TextUnit_Word,0,midDocEnd,midDocEndP1C,0,segment2LmidDocEnd,segment3LmidDocEnd,FALSE
FALSE,4,TextUnit_Word,1,midDocEnd,midDocEndP1C,1,segment3LmidDocEnd,segment4LmidDocEnd,FALSE
FALSE,4,TextUnit_Word,5,midDocEnd,midDocEndP1C,2,segment4LmidDocEnd,docEnd,FALSE
TRUE,5,TextUnit_Word,-5,lastCharPos,lastCharPos,-5,lastCharPosLeft,lastCharPosLeft,TRUE
TRUE,5,TextUnit_Word,-1,lastCharPos,lastCharPos,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,5,TextUnit_Word,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Word,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
TRUE,5,TextUnit_Word,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
FALSE,5,TextUnit_Word,-5,lastCharPos,lastCharPosP1C,-5,segment4LlastCharPosM1L,lastCharPosLeft,FALSE
FALSE,5,TextUnit_Word,-1,lastCharPos,lastCharPosP1C,-1,segment3LmidDocEnd,segment4LmidDocEnd,FALSE
FALSE,5,TextUnit_Word,0,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
FALSE,5,TextUnit_Word,1,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
FALSE,5,TextUnit_Word,5,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
TRUE,6,TextUnit_Word,-5,docEnd,docEnd,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,6,TextUnit_Word,-1,docEnd,docEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,6,TextUnit_Word,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Word,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Word,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Word,-5,docEnd,docEndP1C,-5,midDocEndLeft,midDocEndLeft,TRUE
FALSE,6,TextUnit_Word,-1,docEnd,docEndP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
FALSE,6,TextUnit_Word,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Word,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Word,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpace,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpace,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,7,TextUnit_Word,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Word,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Word,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpaceP1C,-5,midDocEndLeft,midDocEndLeft,TRUE
FALSE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpaceP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
FALSE,7,TextUnit_Word,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Word,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Word,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Word,-5,bufferEnd,bufferEnd,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,8,TextUnit_Word,-1,bufferEnd,bufferEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,8,TextUnit_Word,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Word,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Word,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Word,-5,bufferEnd,endExclusive,-5,midDocEndLeft,midDocEndLeft,TRUE
FALSE,8,TextUnit_Word,-1,bufferEnd,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
FALSE,8,TextUnit_Word,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Word,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Word,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Word,-5,endExclusive,endExclusive,-5,midDocEndLeft,midDocEndLeft,TRUE
TRUE,9,TextUnit_Word,-1,endExclusive,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
TRUE,9,TextUnit_Word,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Word,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Word,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE

1 Degenerate Position TextUnit MoveAmount Start End Result_MoveAmount Result_Start Result_End Skip
62 TRUE 1 TextUnit_Document -5 origin origin 0 origin origin FALSE
63 TRUE 1 TextUnit_Document -1 origin origin 0 origin origin FALSE
64 TRUE 1 TextUnit_Document 0 origin origin 0 origin origin FALSE
65 TRUE 1 TextUnit_Document 1 origin origin 1 endExclusive docEnd endExclusive docEnd FALSE
66 TRUE 1 TextUnit_Document 5 origin origin 1 endExclusive docEnd endExclusive docEnd FALSE
67 FALSE 1 TextUnit_Document -5 origin originP1C 0 origin endExclusive docEnd FALSE
68 FALSE 1 TextUnit_Document -1 origin originP1C 0 origin endExclusive docEnd FALSE
69 FALSE 1 TextUnit_Document 0 origin originP1C 0 origin endExclusive docEnd FALSE
70 FALSE 1 TextUnit_Document 1 origin originP1C 0 origin endExclusive docEnd FALSE
71 FALSE 1 TextUnit_Document 5 origin originP1C 0 origin endExclusive docEnd FALSE
72 TRUE 2 TextUnit_Document -5 midTop midTop -1 origin origin FALSE
73 TRUE 2 TextUnit_Document -1 midTop midTop -1 origin origin FALSE
74 TRUE 2 TextUnit_Document 0 midTop midTop 0 midTop midTop FALSE
75 TRUE 2 TextUnit_Document 1 midTop midTop 1 endExclusive docEnd endExclusive docEnd FALSE
76 TRUE 2 TextUnit_Document 5 midTop midTop 1 endExclusive docEnd endExclusive docEnd FALSE
77 FALSE 2 TextUnit_Document -5 midTop midTopP1C 0 origin endExclusive docEnd FALSE
78 FALSE 2 TextUnit_Document -1 midTop midTopP1C 0 origin endExclusive docEnd FALSE
79 FALSE 2 TextUnit_Document 0 midTop midTopP1C 0 origin endExclusive docEnd FALSE
80 FALSE 2 TextUnit_Document 1 midTop midTopP1C 0 origin endExclusive docEnd FALSE
81 FALSE 2 TextUnit_Document 5 midTop midTopP1C 0 origin endExclusive docEnd FALSE
82 TRUE 3 TextUnit_Document -5 midHistory midHistory -1 origin origin FALSE
83 TRUE 3 TextUnit_Document -1 midHistory midHistory -1 origin origin FALSE
84 TRUE 3 TextUnit_Document 0 midHistory midHistory 0 midHistory midHistory FALSE
85 TRUE 3 TextUnit_Document 1 midHistory midHistory 1 endExclusive docEnd endExclusive docEnd FALSE
86 TRUE 3 TextUnit_Document 5 midHistory midHistory 1 endExclusive docEnd endExclusive docEnd FALSE
87 FALSE 3 TextUnit_Document -5 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
88 FALSE 3 TextUnit_Document -1 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
89 FALSE 3 TextUnit_Document 0 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
90 FALSE 3 TextUnit_Document 1 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
91 FALSE 3 TextUnit_Document 5 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
92 TRUE 8 TextUnit_Character -5 bufferEnd bufferEnd -5 bufferEndM5C docEndM5C bufferEndM5C docEndM5C FALSE
93 TRUE 8 TextUnit_Character -1 bufferEnd bufferEnd -1 bufferEndM1C docEndM1C bufferEndM1C docEndM1C FALSE
94 TRUE 8 TextUnit_Character 0 bufferEnd bufferEnd 0 bufferEnd docEnd bufferEnd docEnd FALSE
95 TRUE 8 TextUnit_Character 1 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
96 TRUE 8 TextUnit_Character 5 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
97 FALSE 8 TextUnit_Character -5 bufferEnd endExclusive -5 bufferEndM5C docEndM5C bufferEndM4C docEndM5C FALSE
98 FALSE 8 TextUnit_Character -1 bufferEnd endExclusive -1 bufferEndM1C docEndM1C bufferEnd docEndM1C FALSE
99 FALSE 8 TextUnit_Character 0 bufferEnd endExclusive 0 bufferEnd docEnd endExclusive docEnd FALSE
100 FALSE 8 TextUnit_Character 1 bufferEnd endExclusive 0 bufferEnd docEnd endExclusive docEnd FALSE
101 FALSE 8 TextUnit_Character 5 bufferEnd endExclusive 0 bufferEnd docEnd endExclusive docEnd FALSE
102 TRUE 8 TextUnit_Line -5 bufferEnd bufferEnd -5 bufferEndM4L docEndM5L bufferEndM4L docEndM5L FALSE
103 TRUE 8 TextUnit_Line -1 bufferEnd bufferEnd -1 bufferEndLeft docEndM1L bufferEndLeft docEndM1L FALSE
104 TRUE 8 TextUnit_Line 0 bufferEnd bufferEnd 0 bufferEnd docEnd bufferEnd docEnd FALSE
105 TRUE 8 TextUnit_Line 1 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
106 TRUE 8 TextUnit_Line 5 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
107 FALSE 8 TextUnit_Line -5 bufferEnd endExclusive -5 bufferEndM5L docEndM5L bufferEndM4L docEndM5L TRUE FALSE
108 FALSE 8 TextUnit_Line -1 bufferEnd endExclusive -1 bufferEndM1L docEndM1L bufferEndLeft docEndM1L TRUE FALSE
109 FALSE 8 TextUnit_Line 0 bufferEnd endExclusive 0 bufferEndLeft docEnd endExclusive docEnd TRUE FALSE
110 FALSE 8 TextUnit_Line 1 bufferEnd endExclusive 0 bufferEndLeft docEnd endExclusive docEnd TRUE FALSE
111 FALSE 8 TextUnit_Line 5 bufferEnd endExclusive 0 bufferEndLeft docEnd endExclusive docEnd TRUE FALSE
112 TRUE 8 TextUnit_Document -5 bufferEnd bufferEnd -1 origin origin FALSE
113 TRUE 8 TextUnit_Document -1 bufferEnd bufferEnd -1 origin origin FALSE
114 TRUE 8 TextUnit_Document 0 bufferEnd bufferEnd 0 bufferEnd docEnd bufferEnd docEnd FALSE
115 TRUE 8 TextUnit_Document 1 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
116 TRUE 8 TextUnit_Document 5 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
117 FALSE 8 TextUnit_Document -5 bufferEnd endExclusive 0 -1 origin endExclusive origin TRUE FALSE
118 FALSE 8 TextUnit_Document -1 bufferEnd endExclusive 0 -1 origin endExclusive origin TRUE FALSE
119 FALSE 8 TextUnit_Document 0 bufferEnd endExclusive 0 origin docEnd endExclusive docEnd TRUE FALSE
120 FALSE 8 TextUnit_Document 1 bufferEnd endExclusive 0 origin docEnd endExclusive docEnd TRUE FALSE
121 FALSE 8 TextUnit_Document 5 bufferEnd endExclusive 0 origin docEnd endExclusive docEnd TRUE FALSE
122 TRUE 9 TextUnit_Character -5 endExclusive endExclusive -5 bufferEndM4C docEndM5C bufferEndM4C docEndM5C FALSE
123 TRUE 9 TextUnit_Character -1 endExclusive endExclusive -1 bufferEnd docEndM1C bufferEnd docEndM1C FALSE
124 TRUE 9 TextUnit_Character 0 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
125 TRUE 9 TextUnit_Character 1 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
126 TRUE 9 TextUnit_Character 5 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
127 TRUE 9 TextUnit_Line -5 endExclusive endExclusive -5 bufferEndM4L docEndM5L bufferEndM4L docEndM5L FALSE
128 TRUE 9 TextUnit_Line -1 endExclusive endExclusive -1 bufferEndLeft docEndM1L bufferEndLeft docEndM1L FALSE
129 TRUE 9 TextUnit_Line 0 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
130 TRUE 9 TextUnit_Line 1 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
131 TRUE 9 TextUnit_Line 5 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
132 TRUE 9 TextUnit_Document -5 endExclusive endExclusive -1 origin origin FALSE
133 TRUE 9 TextUnit_Document -1 endExclusive endExclusive -1 origin origin FALSE
134 TRUE 9 TextUnit_Document 0 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
135 TRUE 9 TextUnit_Document 1 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
136 TRUE 9 TextUnit_Document 5 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
137 TRUE 4 TextUnit_Character -5 midDocEnd midDocEnd -5 midDocEndM5C midDocEndM5C TRUE FALSE
138 TRUE 4 TextUnit_Character -1 midDocEnd midDocEnd -1 midDocEndM1C midDocEndM1C TRUE FALSE
139 TRUE 4 TextUnit_Character 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd TRUE FALSE
140 TRUE 4 TextUnit_Character 1 midDocEnd midDocEnd 1 midDocEndP1C midDocEndP1C TRUE FALSE
141 TRUE 4 TextUnit_Character 5 midDocEnd midDocEnd 5 midDocEndP5C midDocEndP5C TRUE FALSE
142 FALSE 4 TextUnit_Character -5 midDocEnd midDocEndP1C -5 midDocEndM5C midDocEndM4C TRUE FALSE
143 FALSE 4 TextUnit_Character -1 midDocEnd midDocEndP1C -1 midDocEndM1C midDocEnd TRUE FALSE
144 FALSE 4 TextUnit_Character 0 midDocEnd midDocEndP1C 0 midDocEnd midDocEndP1C TRUE FALSE
145 FALSE 4 TextUnit_Character 1 midDocEnd midDocEndP1C 1 midDocEndP1C midDocEndP2C TRUE FALSE
146 FALSE 4 TextUnit_Character 5 midDocEnd midDocEndP1C 5 midDocEndP5C midDocEndP6C TRUE FALSE
147 TRUE 4 TextUnit_Line -5 midDocEnd midDocEnd -5 midDocEndM4L midDocEndM4L TRUE FALSE
148 TRUE 4 TextUnit_Line -1 midDocEnd midDocEnd -1 midDocEndLeft midDocEndLeft TRUE FALSE
149 TRUE 4 TextUnit_Line 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd TRUE FALSE
150 TRUE 4 TextUnit_Line 1 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
151 TRUE 4 TextUnit_Line 5 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
152 FALSE 4 TextUnit_Line -5 midDocEnd midDocEndP1C -5 midDocEndM5L midDocEndM4L TRUE FALSE
153 FALSE 4 TextUnit_Line -1 midDocEnd midDocEndP1C -1 midDocEndM1L midDocEndLeft TRUE FALSE
154 FALSE 4 TextUnit_Line 0 midDocEnd midDocEndP1C 0 midDocEndLeft docEnd TRUE FALSE
155 FALSE 4 TextUnit_Line 1 midDocEnd midDocEndP1C 0 midDocEndLeft docEnd TRUE FALSE
156 FALSE 4 TextUnit_Line 5 midDocEnd midDocEndP1C 0 midDocEndLeft docEnd TRUE FALSE
157 TRUE 4 TextUnit_Document -5 midDocEnd midDocEnd -1 origin origin TRUE FALSE
158 TRUE 4 TextUnit_Document -1 midDocEnd midDocEnd -1 origin origin TRUE FALSE
159 TRUE 4 TextUnit_Document 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd TRUE FALSE
160 TRUE 4 TextUnit_Document 1 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
161 TRUE 4 TextUnit_Document 5 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
162 FALSE 4 TextUnit_Document -5 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
163 FALSE 4 TextUnit_Document -1 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
164 FALSE 4 TextUnit_Document 0 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
165 FALSE 4 TextUnit_Document 1 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
166 FALSE 4 TextUnit_Document 5 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
167 TRUE 5 TextUnit_Character -5 lastCharPos lastCharPos -5 lastCharPosM5C lastCharPosM5C TRUE FALSE
168 TRUE 5 TextUnit_Character -1 lastCharPos lastCharPos -1 lastCharPosM1C lastCharPosM1C TRUE FALSE
169 TRUE 5 TextUnit_Character 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos TRUE FALSE
170 TRUE 5 TextUnit_Character 1 lastCharPos lastCharPos 1 docEnd lastCharPosP1C docEnd lastCharPosP1C TRUE FALSE
171 TRUE 5 TextUnit_Character 5 lastCharPos lastCharPos 1 5 docEnd lastCharPosP5C docEnd lastCharPosP5C TRUE FALSE
172 FALSE 5 TextUnit_Character -5 lastCharPos lastCharPosP1C -5 lastCharPosM5C lastCharPosM4C TRUE FALSE
173 FALSE 5 TextUnit_Character -1 lastCharPos lastCharPosP1C -1 lastCharPosM1C lastCharPos TRUE FALSE
174 FALSE 5 TextUnit_Character 0 lastCharPos lastCharPosP1C 0 lastCharPos docEnd lastCharPosP1C TRUE FALSE
175 FALSE 5 TextUnit_Character 1 lastCharPos lastCharPosP1C 0 1 lastCharPos lastCharPosP1C docEnd lastCharPosP2C TRUE FALSE
176 FALSE 5 TextUnit_Character 5 lastCharPos lastCharPosP1C 0 5 lastCharPos lastCharPosP5C docEnd lastCharPosP6C TRUE FALSE
177 TRUE 5 TextUnit_Line -5 lastCharPos lastCharPos -5 lastCharPosM4L lastCharPosM4L TRUE FALSE
178 TRUE 5 TextUnit_Line -1 lastCharPos lastCharPos -1 lastCharPosLeft lastCharPosLeft TRUE FALSE
179 TRUE 5 TextUnit_Line 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos TRUE FALSE
180 TRUE 5 TextUnit_Line 1 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
181 TRUE 5 TextUnit_Line 5 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
182 FALSE 5 TextUnit_Line -5 lastCharPos lastCharPosP1C -5 lastCharPosM5L lastCharPosM4L TRUE FALSE
183 FALSE 5 TextUnit_Line -1 lastCharPos lastCharPosP1C -1 lastCharPosM1L lastCharPosLeft TRUE FALSE
184 FALSE 5 TextUnit_Line 0 lastCharPos lastCharPosP1C 0 lastCharPosLeft docEnd TRUE FALSE
185 FALSE 5 TextUnit_Line 1 lastCharPos lastCharPosP1C 0 lastCharPosLeft docEnd TRUE FALSE
186 FALSE 5 TextUnit_Line 5 lastCharPos lastCharPosP1C 0 lastCharPosLeft docEnd TRUE FALSE
187 TRUE 5 TextUnit_Document -5 lastCharPos lastCharPos -1 origin origin TRUE FALSE
188 TRUE 5 TextUnit_Document -1 lastCharPos lastCharPos -1 origin origin TRUE FALSE
189 TRUE 5 TextUnit_Document 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos TRUE FALSE
190 TRUE 5 TextUnit_Document 1 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
191 TRUE 5 TextUnit_Document 5 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
192 FALSE 5 TextUnit_Document -5 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
193 FALSE 5 TextUnit_Document -1 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
194 FALSE 5 TextUnit_Document 0 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
195 FALSE 5 TextUnit_Document 1 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
196 FALSE 5 TextUnit_Document 5 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
197 TRUE 6 TextUnit_Character -5 docEnd docEnd -5 docEndM5C docEndM5C TRUE FALSE
198 TRUE 6 TextUnit_Character -1 docEnd docEnd -1 docEndM1C docEndM1C TRUE FALSE
199 TRUE 6 TextUnit_Character 0 docEnd docEnd 0 docEnd docEnd TRUE FALSE
200 TRUE 6 TextUnit_Character 1 docEnd docEnd 0 docEnd docEnd TRUE FALSE
201 TRUE 6 TextUnit_Character 5 docEnd docEnd 0 docEnd docEnd TRUE FALSE
202 FALSE 6 TextUnit_Character -5 docEnd docEndP1C -5 docEndM5C docEndM5C TRUE FALSE
203 FALSE 6 TextUnit_Character -1 docEnd docEndP1C -1 docEndM1C docEndM1C TRUE FALSE
204 FALSE 6 TextUnit_Character 0 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
205 FALSE 6 TextUnit_Character 1 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
206 FALSE 6 TextUnit_Character 5 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
207 TRUE 6 TextUnit_Line -5 docEnd docEnd -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
208 TRUE 6 TextUnit_Line -1 docEnd docEnd -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
209 TRUE 6 TextUnit_Line 0 docEnd docEnd 0 docEnd docEnd TRUE FALSE
210 TRUE 6 TextUnit_Line 1 docEnd docEnd 0 docEnd docEnd TRUE FALSE
211 TRUE 6 TextUnit_Line 5 docEnd docEnd 0 docEnd docEnd TRUE FALSE
212 FALSE 6 TextUnit_Line -5 docEnd docEndP1C -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
213 FALSE 6 TextUnit_Line -1 docEnd docEndP1C -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
214 FALSE 6 TextUnit_Line 0 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
215 FALSE 6 TextUnit_Line 1 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
216 FALSE 6 TextUnit_Line 5 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
217 TRUE 6 TextUnit_Document -5 docEnd docEnd -1 origin origin TRUE FALSE
218 TRUE 6 TextUnit_Document -1 docEnd docEnd -1 origin origin TRUE FALSE
219 TRUE 6 TextUnit_Document 0 docEnd docEnd 0 docEnd docEnd TRUE FALSE
220 TRUE 6 TextUnit_Document 1 docEnd docEnd 0 docEnd docEnd TRUE FALSE
221 TRUE 6 TextUnit_Document 5 docEnd docEnd 0 docEnd docEnd TRUE FALSE
222 FALSE 6 TextUnit_Document -5 docEnd docEndP1C -1 origin origin TRUE FALSE
223 FALSE 6 TextUnit_Document -1 docEnd docEndP1C -1 origin origin TRUE FALSE
224 FALSE 6 TextUnit_Document 0 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
225 FALSE 6 TextUnit_Document 1 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
226 FALSE 6 TextUnit_Document 5 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
227 TRUE 7 TextUnit_Character -5 midEmptySpace midEmptySpace -5 docEndM5C docEndM5C TRUE FALSE
228 TRUE 7 TextUnit_Character -1 midEmptySpace midEmptySpace -1 docEndM1C docEndM1C TRUE FALSE
229 TRUE 7 TextUnit_Character 0 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
230 TRUE 7 TextUnit_Character 1 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
231 TRUE 7 TextUnit_Character 5 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
232 FALSE 7 TextUnit_Character -5 midEmptySpace midEmptySpaceP1C -5 docEndM5C docEndM5C TRUE FALSE
233 FALSE 7 TextUnit_Character -1 midEmptySpace midEmptySpaceP1C -1 docEndM1C docEndM1C TRUE FALSE
234 FALSE 7 TextUnit_Character 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
235 FALSE 7 TextUnit_Character 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
236 FALSE 7 TextUnit_Character 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
237 TRUE 7 TextUnit_Line -5 midEmptySpace midEmptySpace -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
238 TRUE 7 TextUnit_Line -1 midEmptySpace midEmptySpace -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
239 TRUE 7 TextUnit_Line 0 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
240 TRUE 7 TextUnit_Line 1 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
241 TRUE 7 TextUnit_Line 5 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
242 FALSE 7 TextUnit_Line -5 midEmptySpace midEmptySpaceP1C -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
243 FALSE 7 TextUnit_Line -1 midEmptySpace midEmptySpaceP1C -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
244 FALSE 7 TextUnit_Line 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
245 FALSE 7 TextUnit_Line 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
246 FALSE 7 TextUnit_Line 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
247 TRUE 7 TextUnit_Document -5 midEmptySpace midEmptySpace -1 origin origin TRUE FALSE
248 TRUE 7 TextUnit_Document -1 midEmptySpace midEmptySpace -1 origin origin TRUE FALSE
249 TRUE 7 TextUnit_Document 0 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
250 TRUE 7 TextUnit_Document 1 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
251 TRUE 7 TextUnit_Document 5 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
252 FALSE 7 TextUnit_Document -5 midEmptySpace midEmptySpaceP1C -1 origin origin TRUE FALSE
253 FALSE 7 TextUnit_Document -1 midEmptySpace midEmptySpaceP1C -1 origin origin TRUE FALSE
254 FALSE 7 TextUnit_Document 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
255 FALSE 7 TextUnit_Document 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
256 FALSE 7 TextUnit_Document 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
257 TRUE 1 TextUnit_Word -5 origin origin 0 origin origin FALSE
258 TRUE 1 TextUnit_Word -1 origin origin 0 origin origin FALSE
259 TRUE 1 TextUnit_Word 0 origin origin 0 origin origin FALSE
260 TRUE 1 TextUnit_Word 1 origin origin 1 segment1LmidTop segment1LmidTop FALSE
261 TRUE 1 TextUnit_Word 5 origin origin 5 segment0LmidTopP1L segment0LmidTopP1L FALSE
262 FALSE 1 TextUnit_Word -5 origin originP1C 0 origin segment1LmidTop FALSE
263 FALSE 1 TextUnit_Word -1 origin originP1C 0 origin segment1LmidTop FALSE
264 FALSE 1 TextUnit_Word 0 origin originP1C 0 origin segment1LmidTop FALSE
265 FALSE 1 TextUnit_Word 1 origin originP1C 1 segment1LmidTop segment2LmidTop FALSE
266 FALSE 1 TextUnit_Word 5 origin originP1C 5 segment0LmidTopP1L segment1LmidTopP1L FALSE
267 TRUE 2 TextUnit_Word -5 midTop midTop -3 origin origin TRUE
268 TRUE 2 TextUnit_Word -1 midTop midTop -1 segment2LmidTop segment2LmidTop TRUE
269 TRUE 2 TextUnit_Word 0 midTop midTop 0 midTop midTop FALSE
270 TRUE 2 TextUnit_Word 1 midTop midTop 1 segment3LmidTop segment3LmidTop FALSE
271 TRUE 2 TextUnit_Word 5 midTop midTop 5 segment2LmidTopP1L segment2LmidTopP1L FALSE
272 FALSE 2 TextUnit_Word -5 midTop midTopP1C -2 origin segment1LmidTop FALSE
273 FALSE 2 TextUnit_Word -1 midTop midTopP1C -1 segment1LmidTop segment2LmidTop FALSE
274 FALSE 2 TextUnit_Word 0 midTop midTopP1C 0 segment2LmidTop segment3LmidTop FALSE
275 FALSE 2 TextUnit_Word 1 midTop midTopP1C 1 segment3LmidTop segment4LmidTop FALSE
276 FALSE 2 TextUnit_Word 5 midTop midTopP1C 5 segment2LmidTopP1L segment3LmidTopP1L FALSE
277 TRUE 3 TextUnit_Word -5 midHistory midHistory -5 segment3LmidHistoryM1L segment3LmidHistoryM1L TRUE
278 TRUE 3 TextUnit_Word -1 midHistory midHistory -1 segment2LmidHistory segment2LmidHistory TRUE
279 TRUE 3 TextUnit_Word 0 midHistory midHistory 0 midHistory midHistory FALSE
280 TRUE 3 TextUnit_Word 1 midHistory midHistory 1 segment3LmidHistory segment3LmidHistory FALSE
281 TRUE 3 TextUnit_Word 5 midHistory midHistory 5 segment2LmidHistoryP1L segment2LmidHistoryP1L FALSE
282 FALSE 3 TextUnit_Word -5 midHistory midHistoryP1C -5 segment3LmidHistoryM1L segment4LmidHistoryM1L TRUE
283 FALSE 3 TextUnit_Word -1 midHistory midHistoryP1C -1 segment1LmidHistory segment2LmidHistory FALSE
284 FALSE 3 TextUnit_Word 0 midHistory midHistoryP1C 0 segment2LmidHistory segment3LmidHistory FALSE
285 FALSE 3 TextUnit_Word 1 midHistory midHistoryP1C 1 segment3LmidHistory segment4LmidHistory FALSE
286 FALSE 3 TextUnit_Word 5 midHistory midHistoryP1C 5 segment2LmidHistoryP1L segment3LmidHistoryP1L FALSE
287 TRUE 4 TextUnit_Word -5 midDocEnd midDocEnd -5 segment3LmidDocEndM1L segment3LmidDocEndM1L TRUE
288 TRUE 4 TextUnit_Word -1 midDocEnd midDocEnd -1 segment2LmidDocEnd segment2LmidDocEnd TRUE
289 TRUE 4 TextUnit_Word 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd FALSE
290 TRUE 4 TextUnit_Word 1 midDocEnd midDocEnd 1 segment3LmidDocEnd segment3LmidDocEnd FALSE
291 TRUE 4 TextUnit_Word 5 midDocEnd midDocEnd 3 docEnd docEnd FALSE
292 FALSE 4 TextUnit_Word -5 midDocEnd midDocEndP1C -5 segment3LmidDocEndM1L segment4LmidDocEndM1L TRUE
293 FALSE 4 TextUnit_Word -1 midDocEnd midDocEndP1C -1 segment1LmidDocEnd segment2LmidDocEnd FALSE
294 FALSE 4 TextUnit_Word 0 midDocEnd midDocEndP1C 0 segment2LmidDocEnd segment3LmidDocEnd FALSE
295 FALSE 4 TextUnit_Word 1 midDocEnd midDocEndP1C 1 segment3LmidDocEnd segment4LmidDocEnd FALSE
296 FALSE 4 TextUnit_Word 5 midDocEnd midDocEndP1C 2 segment4LmidDocEnd docEnd FALSE
297 TRUE 5 TextUnit_Word -5 lastCharPos lastCharPos -5 lastCharPosLeft lastCharPosLeft TRUE
298 TRUE 5 TextUnit_Word -1 lastCharPos lastCharPos -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
299 TRUE 5 TextUnit_Word 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos FALSE
300 TRUE 5 TextUnit_Word 1 lastCharPos lastCharPos 1 docEnd docEnd FALSE
301 TRUE 5 TextUnit_Word 5 lastCharPos lastCharPos 1 docEnd docEnd FALSE
302 FALSE 5 TextUnit_Word -5 lastCharPos lastCharPosP1C -5 segment4LlastCharPosM1L lastCharPosLeft FALSE
303 FALSE 5 TextUnit_Word -1 lastCharPos lastCharPosP1C -1 segment3LmidDocEnd segment4LmidDocEnd FALSE
304 FALSE 5 TextUnit_Word 0 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
305 FALSE 5 TextUnit_Word 1 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
306 FALSE 5 TextUnit_Word 5 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
307 TRUE 6 TextUnit_Word -5 docEnd docEnd -5 midDocEndLeft midDocEndLeft TRUE
308 TRUE 6 TextUnit_Word -1 docEnd docEnd -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
309 TRUE 6 TextUnit_Word 0 docEnd docEnd 0 docEnd docEnd FALSE
310 TRUE 6 TextUnit_Word 1 docEnd docEnd 0 docEnd docEnd FALSE
311 TRUE 6 TextUnit_Word 5 docEnd docEnd 0 docEnd docEnd FALSE
312 FALSE 6 TextUnit_Word -5 docEnd docEndP1C -5 midDocEndLeft midDocEndLeft TRUE
313 FALSE 6 TextUnit_Word -1 docEnd docEndP1C -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
314 FALSE 6 TextUnit_Word 0 docEnd docEndP1C 0 docEnd docEnd FALSE
315 FALSE 6 TextUnit_Word 1 docEnd docEndP1C 0 docEnd docEnd FALSE
316 FALSE 6 TextUnit_Word 5 docEnd docEndP1C 0 docEnd docEnd FALSE
317 TRUE 7 TextUnit_Word -5 midEmptySpace midEmptySpace -5 midDocEndLeft midDocEndLeft TRUE
318 TRUE 7 TextUnit_Word -1 midEmptySpace midEmptySpace -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
319 TRUE 7 TextUnit_Word 0 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
320 TRUE 7 TextUnit_Word 1 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
321 TRUE 7 TextUnit_Word 5 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
322 FALSE 7 TextUnit_Word -5 midEmptySpace midEmptySpaceP1C -5 midDocEndLeft midDocEndLeft TRUE
323 FALSE 7 TextUnit_Word -1 midEmptySpace midEmptySpaceP1C -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
324 FALSE 7 TextUnit_Word 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
325 FALSE 7 TextUnit_Word 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
326 FALSE 7 TextUnit_Word 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
327 TRUE 8 TextUnit_Word -5 bufferEnd bufferEnd -5 midDocEndLeft midDocEndLeft TRUE
328 TRUE 8 TextUnit_Word -1 bufferEnd bufferEnd -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
329 TRUE 8 TextUnit_Word 0 bufferEnd bufferEnd 0 docEnd docEnd FALSE
330 TRUE 8 TextUnit_Word 1 bufferEnd bufferEnd 0 docEnd docEnd FALSE
331 TRUE 8 TextUnit_Word 5 bufferEnd bufferEnd 0 docEnd docEnd FALSE
332 FALSE 8 TextUnit_Word -5 bufferEnd endExclusive -5 midDocEndLeft midDocEndLeft TRUE
333 FALSE 8 TextUnit_Word -1 bufferEnd endExclusive -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
334 FALSE 8 TextUnit_Word 0 bufferEnd endExclusive 0 docEnd docEnd FALSE
335 FALSE 8 TextUnit_Word 1 bufferEnd endExclusive 0 docEnd docEnd FALSE
336 FALSE 8 TextUnit_Word 5 bufferEnd endExclusive 0 docEnd docEnd FALSE
337 TRUE 9 TextUnit_Word -5 endExclusive endExclusive -5 midDocEndLeft midDocEndLeft TRUE
338 TRUE 9 TextUnit_Word -1 endExclusive endExclusive -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
339 TRUE 9 TextUnit_Word 0 endExclusive endExclusive 0 docEnd docEnd FALSE
340 TRUE 9 TextUnit_Word 1 endExclusive endExclusive 0 docEnd docEnd FALSE
341 TRUE 9 TextUnit_Word 5 endExclusive endExclusive 0 docEnd docEnd FALSE