mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-19 13:06:47 +00:00
Compare commits
9 Commits
dev/duhowe
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f06016864 | ||
|
|
aba027ff5c | ||
|
|
cb531fe8ef | ||
|
|
466ba771ba | ||
|
|
1154428f9c | ||
|
|
197969255a | ||
|
|
34e973fd18 | ||
|
|
f990fc2fbb | ||
|
|
da1d3224a1 |
2
.github/actions/spelling/expect/expect.txt
vendored
2
.github/actions/spelling/expect/expect.txt
vendored
@@ -572,7 +572,6 @@ FGHIJ
|
||||
fgidx
|
||||
FGs
|
||||
FILEDESCRIPTION
|
||||
filehops
|
||||
FILESUBTYPE
|
||||
FILESYSPATH
|
||||
FILEW
|
||||
@@ -1773,7 +1772,6 @@ uldash
|
||||
uldb
|
||||
ULONGLONG
|
||||
ulwave
|
||||
Unaccess
|
||||
Unadvise
|
||||
unattend
|
||||
UNCPRIORITY
|
||||
|
||||
8
.github/workflows/spelling2.yml
vendored
8
.github/workflows/spelling2.yml
vendored
@@ -93,7 +93,7 @@ jobs:
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
with:
|
||||
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
|
||||
checkout: true
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: microsoft/terminal@main
|
||||
@@ -171,7 +171,7 @@ jobs:
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: microsoft/terminal@main
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: apply spelling updates
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
with:
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
checkout: true
|
||||
|
||||
@@ -860,6 +860,17 @@
|
||||
<Folder Name="/_Dependencies/Console/">
|
||||
<File Path="dep/Console/winconp.h" />
|
||||
</Folder>
|
||||
<Folder Name="/_Dependencies/gsl/">
|
||||
<File Path="dep/gsl/include/gsl/gsl" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_algorithm" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_assert" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_byte" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_util" />
|
||||
<File Path="dep/gsl/include/gsl/multi_span" />
|
||||
<File Path="dep/gsl/include/gsl/pointers" />
|
||||
<File Path="dep/gsl/include/gsl/span" />
|
||||
<File Path="dep/gsl/include/gsl/string_span" />
|
||||
</Folder>
|
||||
<Folder Name="/_Dependencies/wil/">
|
||||
<File Path="dep/wil/include/wil/common.h" />
|
||||
<File Path="dep/wil/include/wil/filesystem.h" />
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
- [Via Chocolatey (unofficial)](#via-chocolatey-unofficial)
|
||||
- [Via Scoop (unofficial)](#via-scoop-unofficial)
|
||||
- [Installing Windows Terminal Canary](#installing-windows-terminal-canary)
|
||||
- [Windows Terminal Roadmap](#windows-terminal-roadmap)
|
||||
- [Terminal \& Console Overview](#terminal--console-overview)
|
||||
- [Windows Terminal](#windows-terminal)
|
||||
- [The Windows Console Host](#the-windows-console-host)
|
||||
@@ -177,6 +178,11 @@ _Learn more about the [types of Windows Terminal distributions](https://learn.mi
|
||||
|
||||
---
|
||||
|
||||
## Windows Terminal Roadmap
|
||||
|
||||
The plan for the Windows Terminal [is described here](/doc/roadmap-2023.md) and
|
||||
will be updated as the project proceeds.
|
||||
|
||||
## Terminal & Console Overview
|
||||
|
||||
Please take a few minutes to review the overview below before diving into the
|
||||
|
||||
@@ -59,7 +59,6 @@ stages:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
buildPlatforms: [x64]
|
||||
buildConfigurations: [AuditMode]
|
||||
buildEverything: true
|
||||
@@ -83,7 +82,6 @@ stages:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
buildPlatforms:
|
||||
- ${{ platform }}
|
||||
buildConfigurations: [Release]
|
||||
|
||||
@@ -58,7 +58,7 @@ parameters:
|
||||
type: object
|
||||
default:
|
||||
name: SHINE-INT-S # By default, send jobs to the small agent pool.
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
demands: ImageOverride -equals SHINE-VS17-Latest
|
||||
|
||||
variables:
|
||||
- template: variables-nuget-package-version.yml
|
||||
@@ -81,7 +81,7 @@ stages:
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-L # Run the compilation on the large agent pool, rather than the default small one.
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
demands: ImageOverride -equals SHINE-VS17-Latest
|
||||
branding: ${{ parameters.branding }}
|
||||
buildTerminal: ${{ parameters.buildTerminal }}
|
||||
buildConPTY: ${{ parameters.buildConPTY }}
|
||||
|
||||
@@ -211,7 +211,7 @@ extends:
|
||||
ob_createvpack_verbose: true
|
||||
ob_createvpack_vpackdirectory: '$(JobOutputDirectory)\vpack'
|
||||
ob_createvpack_versionAs: string
|
||||
ob_createvpack_version: '$(XES_PACKAGEVERSIONNUMBER)'
|
||||
ob_createvpack_version: '$(XES_APPXMANIFESTVERSION)'
|
||||
ob_updateOSManifest_gitcheckinConfigPath: '$(Build.SourcesDirectory)\build\config\GitCheckin.json'
|
||||
# We're skipping the 'fetch' part of the OneBranch rules, but that doesn't mean
|
||||
# that it doesn't expect to have downloaded a manifest directly to some 'destination'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
steps:
|
||||
- pwsh: |-
|
||||
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -requires Microsoft.VisualStudio.Component.Vcpkg -property installationPath -latest
|
||||
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -requires Microsoft.VisualStudio.Component.Vcpkg -property installationPath
|
||||
If ([String]::IsNullOrEmpty($VsInstallRoot)) {
|
||||
Remove-Item -Recurse -Force dep/vcpkg -ErrorAction:Ignore
|
||||
git clone https://github.com/microsoft/vcpkg dep/vcpkg
|
||||
|
||||
@@ -5,4 +5,4 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
|
||||
@@ -5,7 +5,7 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
|
||||
set(VCPKG_CXX_FLAGS /fsanitize=address)
|
||||
set(VCPKG_C_FLAGS /fsanitize=address)
|
||||
|
||||
@@ -5,7 +5,7 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
|
||||
set(VCPKG_CXX_FLAGS /fsanitize=address)
|
||||
set(VCPKG_C_FLAGS /fsanitize=address)
|
||||
|
||||
@@ -5,7 +5,7 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
|
||||
set(VCPKG_CXX_FLAGS /fsanitize=address)
|
||||
set(VCPKG_C_FLAGS /fsanitize=address)
|
||||
|
||||
@@ -5,4 +5,4 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
|
||||
@@ -5,4 +5,4 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
|
||||
@@ -3174,11 +3174,6 @@
|
||||
"mingw"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"dragDropDelimiter": {
|
||||
"default": " ",
|
||||
"description": "The delimiter used when dropping multiple files onto the terminal.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -219,17 +219,6 @@ til::CoordType TextBuffer::_estimateOffsetOfLastCommittedRow() const noexcept
|
||||
return std::max(0, gsl::narrow_cast<til::CoordType>(lastRowOffset - 2));
|
||||
}
|
||||
|
||||
bool TextBuffer::_isRowCommitted(til::CoordType y) const noexcept
|
||||
{
|
||||
auto offset = (_firstRow + y + 1 /* account for the scratch row */) % _height;
|
||||
if (offset < 0)
|
||||
{
|
||||
offset += _height;
|
||||
}
|
||||
const auto row = _buffer.get() + _bufferRowStride * offset;
|
||||
return row < _commitWatermark;
|
||||
}
|
||||
|
||||
// Retrieves a row from the buffer by its offset from the first row of the text buffer
|
||||
// (what corresponds to the top row of the screen buffer).
|
||||
const ROW& TextBuffer::GetRowByOffset(const til::CoordType index) const
|
||||
@@ -945,10 +934,6 @@ void TextBuffer::ResetLineRenditionRange(const til::CoordType startRow, const ti
|
||||
|
||||
LineRendition TextBuffer::GetLineRendition(const til::CoordType row) const
|
||||
{
|
||||
if (!_isRowCommitted(row)) [[unlikely]]
|
||||
{
|
||||
return LineRendition::SingleWidth;
|
||||
}
|
||||
return GetRowByOffset(row).GetLineRendition();
|
||||
}
|
||||
|
||||
@@ -1156,20 +1141,7 @@ DelimiterClass TextBuffer::_GetDelimiterClassAt(const til::point pos, const std:
|
||||
return GetRowByOffset(realPos.y).DelimiterClassAt(realPos.x, wordDelimiters);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the start of the word at or before the given position
|
||||
// - When includeWhitespace is false, returns the start of the current delimiter class run
|
||||
// (selection behavior: stops at non-wrapped row boundaries)
|
||||
// - When includeWhitespace is true, also skips backward past any leading ControlChars
|
||||
// to include the preceding word (accessibility/UIA behavior: crosses all row boundaries)
|
||||
// Arguments:
|
||||
// - pos - the buffer position to start from
|
||||
// - wordDelimiters - characters considered as DelimiterClass::DelimiterChar
|
||||
// - includeWhitespace - when true, skip past leading whitespace to find the word start
|
||||
// - limitOptional - (optional) the last possible position in the buffer that can be explored
|
||||
// Return Value:
|
||||
// - The position of the first character of the word (inclusive)
|
||||
til::point TextBuffer::GetWordStart(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
til::point TextBuffer::GetWordStart2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.BottomInclusiveRightExclusive()) };
|
||||
@@ -1202,7 +1174,7 @@ til::point TextBuffer::GetWordStart(til::point pos, const std::wstring_view word
|
||||
// 1. move to the beginning of the delimiter class run
|
||||
// 2. (includeWhitespace) if we were on a ControlChar, go back one more delimiter class run
|
||||
const auto initialDelimiter = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters, includeWhitespace);
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters);
|
||||
if (!includeWhitespace || pos.x == bufferSize.Left())
|
||||
{
|
||||
// Special case:
|
||||
@@ -1213,26 +1185,12 @@ til::point TextBuffer::GetWordStart(til::point pos, const std::wstring_view word
|
||||
else if (initialDelimiter == DelimiterClass::ControlChar)
|
||||
{
|
||||
bufferSize.DecrementInExclusiveBounds(pos);
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters, includeWhitespace);
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the exclusive end of the word at or after the given position
|
||||
// - When includeWhitespace is false, returns the exclusive end of the current delimiter class run
|
||||
// (selection behavior: stops at non-wrapped row boundaries)
|
||||
// - When includeWhitespace is true, also skips forward past any trailing ControlChars
|
||||
// to include trailing whitespace (accessibility/UIA behavior: crosses all row boundaries)
|
||||
// - The result is clamped to limitOptional when provided
|
||||
// Arguments:
|
||||
// - pos - the buffer position to start from
|
||||
// - wordDelimiters - characters considered as DelimiterClass::DelimiterChar
|
||||
// - includeWhitespace - when true, skip past trailing whitespace to find the next word boundary
|
||||
// - limitOptional - (optional) the last possible position in the buffer that can be explored
|
||||
// Return Value:
|
||||
// - The exclusive end position of the word
|
||||
til::point TextBuffer::GetWordEnd(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
til::point TextBuffer::GetWordEnd2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.BottomInclusiveRightExclusive()) };
|
||||
@@ -1265,12 +1223,8 @@ til::point TextBuffer::GetWordEnd(til::point pos, const std::wstring_view wordDe
|
||||
// So the heuristic we use is:
|
||||
// 1. move to the end of the delimiter class run
|
||||
// 2. (includeWhitespace) if the next delimiter class run is a ControlChar, go forward one more delimiter class run
|
||||
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters, includeWhitespace);
|
||||
if (pos >= limit)
|
||||
{
|
||||
return limit;
|
||||
}
|
||||
else if (!includeWhitespace || pos.x == bufferSize.RightExclusive())
|
||||
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters);
|
||||
if (!includeWhitespace || pos.x == bufferSize.RightExclusive())
|
||||
{
|
||||
// Special case:
|
||||
// we're at the right boundary (and end of a delimiter class run),
|
||||
@@ -1281,8 +1235,7 @@ til::point TextBuffer::GetWordEnd(til::point pos, const std::wstring_view wordDe
|
||||
if (const auto nextDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
nextDelimClass == DelimiterClass::ControlChar)
|
||||
{
|
||||
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters, includeWhitespace);
|
||||
return std::min(pos, limit);
|
||||
return _GetDelimiterClassRunEnd(pos, wordDelimiters);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
@@ -1335,48 +1288,29 @@ bool TextBuffer::IsWordBoundary(const til::point pos, const std::wstring_view wo
|
||||
return prevDelimiterClass != currentDelimiterClass && currentDelimiterClass != DelimiterClass::ControlChar;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the start position for the current delimiter class run, scanning backward
|
||||
// - Stops when the delimiter class changes or a row boundary is reached
|
||||
// - When accessibilityMode is true, freely crosses non-wrapped row boundaries
|
||||
// - When accessibilityMode is false, only crosses wrap-forced row boundaries
|
||||
// Arguments:
|
||||
// - pos - the buffer position to start scanning from
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - accessibilityMode - when true, cross non-wrapped row boundaries freely
|
||||
// Return Value:
|
||||
// - The position of the first character in the current delimiter class run (inclusive)
|
||||
til::point TextBuffer::_GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode) const
|
||||
til::point TextBuffer::_GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
const auto initialDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
auto nextPos = pos;
|
||||
while (nextPos != bufferSize.Origin())
|
||||
for (auto nextPos = pos; nextPos != bufferSize.Origin(); pos = nextPos)
|
||||
{
|
||||
bufferSize.DecrementInExclusiveBounds(nextPos);
|
||||
|
||||
if (nextPos.x == bufferSize.RightExclusive())
|
||||
{
|
||||
// wrapped onto previous line
|
||||
// wrapped onto previous line,
|
||||
// check if it was forced to wrap
|
||||
const auto& row = GetRowByOffset(nextPos.y);
|
||||
if (!row.WasWrapForced() && !accessibilityMode)
|
||||
if (!row.WasWrapForced())
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
// In accessibility mode (or if row was wrap-forced), continue
|
||||
// across the row boundary. The actual last character of the
|
||||
// previous row will be checked on the next iteration.
|
||||
// Don't update pos to avoid storing a transient position
|
||||
}
|
||||
else if (_GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
{
|
||||
// if we changed delim class, we're done (don't apply move)
|
||||
return pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = nextPos;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
@@ -1386,8 +1320,7 @@ til::point TextBuffer::_GetDelimiterClassRunStart(til::point pos, const std::wst
|
||||
// Arguments:
|
||||
// - pos - the buffer position being within the current delimiter class
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - accessibilityMode - when true, cross non-wrapped row boundaries freely
|
||||
til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode) const
|
||||
til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
const auto initialDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
@@ -1400,16 +1333,7 @@ til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstri
|
||||
// wrapped onto next line,
|
||||
// check if it was forced to wrap or switched delimiter class
|
||||
const auto& row = GetRowByOffset(pos.y);
|
||||
if (accessibilityMode)
|
||||
{
|
||||
// In accessibility mode, always cross row boundaries,
|
||||
// but still stop if the delimiter class changes
|
||||
if (_GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
{
|
||||
return nextPos;
|
||||
}
|
||||
}
|
||||
else if (!row.WasWrapForced() || _GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
if (!row.WasWrapForced() || _GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
@@ -1424,6 +1348,283 @@ til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstri
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the til::point for the beginning of the word you are on
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - 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 til::point for the first character on the "word" (inclusive)
|
||||
til::point TextBuffer::GetWordStart(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
// Consider a buffer with this text in it:
|
||||
// " word other "
|
||||
// In selection (accessibilityMode = false),
|
||||
// a "word" is defined as the range between two delimiters
|
||||
// so the words in the example include [" ", "word", " ", "other", " "]
|
||||
// In accessibility (accessibilityMode = true),
|
||||
// a "word" includes the delimiters after a range of readable characters
|
||||
// so the words in the example include ["word ", "other "]
|
||||
// NOTE: the start anchor (this one) is inclusive, whereas the end anchor (GetWordEnd) is exclusive
|
||||
|
||||
#pragma warning(suppress : 26496)
|
||||
auto copy{ target };
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
if (target == bufferSize.Origin())
|
||||
{
|
||||
// can't expand left
|
||||
return target;
|
||||
}
|
||||
else if (target == bufferSize.EndExclusive())
|
||||
{
|
||||
// GH#7664: Treat EndExclusive as EndInclusive so
|
||||
// that it actually points to a space in the buffer
|
||||
copy = bufferSize.BottomRightInclusive();
|
||||
}
|
||||
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
{
|
||||
// if at/past the limit --> clamp to limit
|
||||
copy = limitOptional.value_or(bufferSize.BottomRightInclusive());
|
||||
}
|
||||
|
||||
if (accessibilityMode)
|
||||
{
|
||||
return _GetWordStartForAccessibility(copy, wordDelimiters);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _GetWordStartForSelection(copy, wordDelimiters);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordStart(). Get the til::point for the beginning of the word (accessibility definition) you are on
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - The til::point for the first character on the current/previous READABLE "word" (inclusive)
|
||||
til::point TextBuffer::_GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
auto result = target;
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
// ignore left boundary. Continue until readable text found
|
||||
while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
if (result == bufferSize.Origin())
|
||||
{
|
||||
//looped around and hit origin (no word between origin and target)
|
||||
return result;
|
||||
}
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
// make sure we expand to the left boundary or the beginning of the word
|
||||
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
if (result == bufferSize.Origin())
|
||||
{
|
||||
// first char in buffer is a RegularChar
|
||||
// we can't move any further back
|
||||
return result;
|
||||
}
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
// move off of delimiter
|
||||
bufferSize.IncrementInBounds(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordStart(). Get the til::point for the beginning of the word (selection definition) you are on
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - The til::point for the first character on the current word or delimiter run (stopped by the left margin)
|
||||
til::point TextBuffer::_GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
auto result = target;
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters);
|
||||
const bool isControlChar = initialDelimiter == DelimiterClass::ControlChar;
|
||||
|
||||
// expand left until we hit the left boundary or a different delimiter class
|
||||
while (result != bufferSize.Origin() && _GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter)
|
||||
{
|
||||
if (result.x == bufferSize.Left())
|
||||
{
|
||||
// Prevent wrapping to the previous line if the selection begins on whitespace
|
||||
if (isControlChar)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.y > 0)
|
||||
{
|
||||
// Prevent wrapping to the previous line if it was hard-wrapped (e.g. not forced by us to wrap)
|
||||
const auto& priorRow = GetRowByOffset(result.y - 1);
|
||||
if (!priorRow.WasWrapForced())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter)
|
||||
{
|
||||
// move off of delimiter
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the til::point for the beginning of the NEXT word
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - 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 til::point for the last character on the "word" (inclusive)
|
||||
til::point TextBuffer::GetWordEnd(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
// Consider a buffer with this text in it:
|
||||
// " word other "
|
||||
// In selection (accessibilityMode = false),
|
||||
// a "word" is defined as the range between two delimiters
|
||||
// so the words in the example include [" ", "word", " ", "other", " "]
|
||||
// In accessibility (accessibilityMode = true),
|
||||
// a "word" includes the delimiters after a range of readable characters
|
||||
// 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/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)
|
||||
{
|
||||
return _GetWordEndForAccessibility(target, wordDelimiters, limit);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _GetWordEndForSelection(target, wordDelimiters);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordEnd(). Get the til::point for the beginning of the next READABLE word
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - limit - the last "valid" position in the text buffer (to improve performance)
|
||||
// Return Value:
|
||||
// - The til::point for the first character of the next readable "word". If no next word, return one past the end of the buffer
|
||||
til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const
|
||||
{
|
||||
const auto bufferSize{ GetSize() };
|
||||
auto result{ target };
|
||||
|
||||
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
{
|
||||
// if we're already on/past the last RegularChar,
|
||||
// clamp result to that position
|
||||
result = limit;
|
||||
|
||||
// make the result exclusive
|
||||
bufferSize.IncrementInBounds(result, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (result != limit && result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
// Iterate through readable text
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
while (result != limit && result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
// expand to the beginning of the NEXT word
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
// Special case: we tried to move one past the end of the buffer
|
||||
// Manually increment onto the EndExclusive point.
|
||||
if (result == bufferSize.BottomRightInclusive())
|
||||
{
|
||||
bufferSize.IncrementInBounds(result, true);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordEnd(). Get the til::point for the beginning of the NEXT word
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - The til::point for the last character of the current word or delimiter run (stopped by right margin)
|
||||
til::point TextBuffer::_GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
auto result = target;
|
||||
const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters);
|
||||
const bool isControlChar = initialDelimiter == DelimiterClass::ControlChar;
|
||||
|
||||
// expand right until we hit the right boundary as a ControlChar or a different delimiter class
|
||||
while (result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter)
|
||||
{
|
||||
if (result.x == bufferSize.RightInclusive())
|
||||
{
|
||||
// Prevent wrapping to the next line if the selection begins on whitespace
|
||||
if (isControlChar)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Prevent wrapping to the next line if this one was hard-wrapped (e.g. not forced by us to wrap)
|
||||
const auto& row = GetRowByOffset(result.y);
|
||||
if (!row.WasWrapForced())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter)
|
||||
{
|
||||
// move off of delimiter
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextBuffer::_PruneHyperlinks()
|
||||
{
|
||||
// Check the old first row for hyperlink references
|
||||
@@ -1470,6 +1671,57 @@ void TextBuffer::_PruneHyperlinks()
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the position of the first character of the next word. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - 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 til::point for the first character on the "word" (inclusive)
|
||||
bool TextBuffer::MoveToNextWord(til::point& 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.
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
|
||||
|
||||
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the position of the first character of the previous word. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||
// - pos - The til::point for the first character on the "word" (inclusive)
|
||||
bool TextBuffer::MoveToPreviousWord(til::point& pos, std::wstring_view wordDelimiters) const
|
||||
{
|
||||
// move to the beginning of the current word
|
||||
auto copy{ GetWordStart(pos, wordDelimiters, true) };
|
||||
|
||||
if (!GetSize().DecrementInBounds(copy, true))
|
||||
{
|
||||
// can't move behind current word
|
||||
return false;
|
||||
}
|
||||
|
||||
// move to the beginning of the previous word
|
||||
pos = GetWordStart(copy, wordDelimiters, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the beginning of the current glyph/character. This is used for accessibility
|
||||
// Arguments:
|
||||
@@ -3527,34 +3779,3 @@ void TextBuffer::ManuallyMarkRowAsPrompt(til::CoordType y)
|
||||
attr.SetMarkAttributes(MarkKind::Prompt);
|
||||
}
|
||||
}
|
||||
|
||||
// This is an optimization used by the renderer to avoid scheduling a timer if not necessary;
|
||||
// unlike the renderer, we know the committed range of our own buffer.
|
||||
bool TextBuffer::ContainsBlinkAttributeInRegion(const Microsoft::Console::Types::Viewport& region) const
|
||||
{
|
||||
const auto top = region.Top();
|
||||
auto bottom = std::min(region.BottomInclusive(), _estimateOffsetOfLastCommittedRow());
|
||||
|
||||
for (auto row = top; row < bottom; ++row)
|
||||
{
|
||||
const auto& r = GetRowByOffset(row);
|
||||
for (const auto& attr : r.Attributes())
|
||||
{
|
||||
if (attr.IsBlinking())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextBuffer::IsGlyphDoubleWidthAt(const til::point at) const
|
||||
{
|
||||
if (!_isRowCommitted(at.y)) [[unlikely]]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _getRow(at.y).DbcsAttrAt(at.x) != DbcsAttribute::Single;
|
||||
}
|
||||
|
||||
@@ -172,10 +172,15 @@ public:
|
||||
void TriggerNewTextNotification(const std::wstring_view newText);
|
||||
void TriggerSelection();
|
||||
|
||||
til::point GetWordStart(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordEnd(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordStart(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordEnd(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
|
||||
til::point GetWordStart2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordEnd2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
|
||||
bool IsWordBoundary(const til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
bool MoveToNextWord(til::point& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
bool MoveToPreviousWord(til::point& pos, const std::wstring_view wordDelimiters) const;
|
||||
|
||||
til::point GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetGlyphEnd(const til::point pos, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
@@ -310,9 +315,6 @@ public:
|
||||
void SetScrollbarData(ScrollbarData mark, til::CoordType y);
|
||||
void ManuallyMarkRowAsPrompt(til::CoordType y);
|
||||
|
||||
bool ContainsBlinkAttributeInRegion(const Microsoft::Console::Types::Viewport& region) const;
|
||||
bool IsGlyphDoubleWidthAt(const til::point at) const;
|
||||
|
||||
private:
|
||||
void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes);
|
||||
void _commit(const std::byte* row);
|
||||
@@ -322,13 +324,16 @@ private:
|
||||
ROW& _getRowByOffsetDirect(size_t offset);
|
||||
ROW& _getRow(til::CoordType y) const;
|
||||
til::CoordType _estimateOffsetOfLastCommittedRow() const noexcept;
|
||||
bool _isRowCommitted(til::CoordType y) const noexcept;
|
||||
|
||||
void _SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept;
|
||||
void _ExpandTextRow(til::inclusive_rect& selectionRow) const;
|
||||
DelimiterClass _GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode = false) const;
|
||||
til::point _GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode = false) const;
|
||||
til::point _GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const;
|
||||
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
void _PruneHyperlinks();
|
||||
|
||||
std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive, const bool clipAtCursor = false) const;
|
||||
|
||||
@@ -34,16 +34,6 @@ class UTextAdapterTests
|
||||
{
|
||||
TEST_CLASS(UTextAdapterTests);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
wil::unique_hmodule icu{ LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) };
|
||||
if (!icu)
|
||||
{
|
||||
WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped, L"ICU is not present");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_METHOD(Unicode)
|
||||
{
|
||||
DummyRenderer renderer;
|
||||
|
||||
@@ -26,9 +26,6 @@ TARGETLIBS = \
|
||||
$(CONSOLE_OBJ_PATH)\types\lib\$(O)\ConTypes.lib \
|
||||
$(TARGETLIBS) \
|
||||
|
||||
DELAYLOAD = \
|
||||
icu.dll \
|
||||
|
||||
# -------------------------------------
|
||||
# Localization
|
||||
# -------------------------------------
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
ResourceKey="TabViewBackground" />
|
||||
|
||||
<SolidColorBrush x:Key="SettingsUiTabBrush"
|
||||
Color="#282828" />
|
||||
Color="#0c0c0c" />
|
||||
|
||||
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
|
||||
Color="{StaticResource SystemAccentColorDark2}" />
|
||||
@@ -211,7 +211,7 @@
|
||||
ResourceKey="TabViewBackground" />
|
||||
|
||||
<SolidColorBrush x:Key="SettingsUiTabBrush"
|
||||
Color="#F9F9F9" />
|
||||
Color="#ffffff" />
|
||||
|
||||
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
|
||||
Color="{StaticResource SystemAccentColorLight2}" />
|
||||
@@ -234,7 +234,7 @@
|
||||
ResourceKey="SystemColorButtonFaceColorBrush" />
|
||||
|
||||
<StaticResource x:Key="SettingsUiTabBrush"
|
||||
ResourceKey="SystemColorWindowBrush" />
|
||||
ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
|
||||
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
|
||||
Color="{StaticResource SystemColorHighlightColor}" />
|
||||
|
||||
@@ -801,7 +801,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
_RemoveTabs(tabsToRemove);
|
||||
|
||||
actionArgs.Handled(true);
|
||||
actionArgs.Handled(!tabsToRemove.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -837,7 +837,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// tab row, until you mouse over them. Probably has something to do
|
||||
// with tabs not resizing down until there's a mouse exit event.
|
||||
|
||||
actionArgs.Handled(true);
|
||||
actionArgs.Handled(!tabsToRemove.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1475,7 +1475,7 @@ namespace winrt::TerminalApp::implementation
|
||||
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes);
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
currentWorkingDirectory = control.WorkingDirectory();
|
||||
currentWorkingDirectory = control.CurrentWorkingDirectory();
|
||||
|
||||
if (shouldGetContext)
|
||||
{
|
||||
|
||||
@@ -496,24 +496,48 @@
|
||||
<value>Third-Party notices</value>
|
||||
<comment>A hyperlink name for the Terminal's third-party notices</comment>
|
||||
</data>
|
||||
<data name="QuitDialog.CloseButtonText" xml:space="preserve">
|
||||
<data name="ConfirmCloseDialog_Cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="QuitDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Close all</value>
|
||||
</data>
|
||||
<data name="QuitDialog.Title" xml:space="preserve">
|
||||
<data name="ConfirmCloseDialog_CloseAllTitle" xml:space="preserve">
|
||||
<value>Do you want to close all windows?</value>
|
||||
</data>
|
||||
<data name="CloseAllDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="CloseAllDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="ConfirmCloseDialog_CloseAllPrimary" xml:space="preserve">
|
||||
<value>Close all</value>
|
||||
</data>
|
||||
<data name="CloseAllDialog.Title" xml:space="preserve">
|
||||
<data name="ConfirmCloseDialog_WindowTitle" xml:space="preserve">
|
||||
<value>Do you want to close all tabs?</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_WindowPrimary" xml:space="preserve">
|
||||
<value>Close all</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_TabTitle" xml:space="preserve">
|
||||
<value>Do you want to close this tab?</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_TabPrimary" xml:space="preserve">
|
||||
<value>Close tab</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_PaneTitle" xml:space="preserve">
|
||||
<value>Do you want to close this pane?</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_PanePrimary" xml:space="preserve">
|
||||
<value>Close pane</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_MultipleTabsTitle" xml:space="preserve">
|
||||
<value>Do you want to close these tabs?</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_MultipleTabsPrimary" xml:space="preserve">
|
||||
<value>Close tabs</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_MultiplePanesTitle" xml:space="preserve">
|
||||
<value>Do you want to close these panes?</value>
|
||||
</data>
|
||||
<data name="ConfirmCloseDialog_MultiplePanesPrimary" xml:space="preserve">
|
||||
<value>Close panes</value>
|
||||
</data>
|
||||
<data name="DontAskAgainCheckBox.Content" xml:space="preserve">
|
||||
<value>Don't ask me again</value>
|
||||
</data>
|
||||
<data name="CloseReadOnlyDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
|
||||
@@ -391,10 +391,12 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes the tab (both TerminalControl and XAML) after prompting for approval
|
||||
// - Removes the tab (both TerminalControl and XAML) after prompting for approval.
|
||||
// Arguments:
|
||||
// - tab: the tab to remove
|
||||
winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested(winrt::TerminalApp::Tab tab)
|
||||
// - tab: the tab to remove.
|
||||
// - skipConfirmClose: if true, skip the confirmCloseOn check. Used when
|
||||
// an aggregate confirmation has already been shown (e.g. close other tabs).
|
||||
winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested(winrt::TerminalApp::Tab tab, bool skipConfirmClose)
|
||||
{
|
||||
winrt::com_ptr<TerminalPage> strong;
|
||||
|
||||
@@ -413,6 +415,24 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the per-tab confirmCloseOn check when the caller has already
|
||||
// shown an aggregate confirmation dialog (e.g. _RemoveTabs).
|
||||
if (!skipConfirmClose)
|
||||
{
|
||||
const auto tabImpl = _GetTabImpl(tab);
|
||||
if (tabImpl && _ShouldWarnOnCloseTab(tabImpl))
|
||||
{
|
||||
const auto weak = get_weak();
|
||||
|
||||
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::Tab);
|
||||
strong = weak.get();
|
||||
if (!strong || warningResult != ContentDialogResult::Primary)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto t = winrt::get_self<implementation::Tab>(tab);
|
||||
auto actions = t->BuildStartupActions(BuildStartupKind::None);
|
||||
_AddPreviouslyClosedPaneOrTab(std::move(actions));
|
||||
@@ -782,6 +802,22 @@ namespace winrt::TerminalApp::implementation
|
||||
if (const auto pane{ activeTab->GetActivePane() })
|
||||
{
|
||||
const auto weak = get_weak();
|
||||
|
||||
// Check if we should warn before closing a single pane
|
||||
// (only triggers on Always — Automatic doesn't warn for single pane)
|
||||
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
|
||||
if (setting == ConfirmCloseOn::Always)
|
||||
{
|
||||
// If this is the last pane, closing it closes the tab,
|
||||
// so use the tab dialog text instead.
|
||||
const auto kind = activeTab->GetLeafPaneCount() == 1 ? ConfirmCloseDialogKind::Tab : ConfirmCloseDialogKind::Pane;
|
||||
auto warningResult = co_await _ShowConfirmCloseDialog(kind);
|
||||
if (!weak.get() || warningResult != ContentDialogResult::Primary)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
if (co_await _PaneConfirmCloseReadOnly(pane))
|
||||
{
|
||||
if (const auto strong = weak.get())
|
||||
@@ -794,15 +830,38 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Close all panes with the given IDs sequentially.
|
||||
// - Closes provided panes one by one, showing a single aggregate
|
||||
// confirmation dialog upfront if the confirmCloseOn setting warrants it.
|
||||
// Arguments:
|
||||
// - weakTab: weak reference to the tab that the pane belongs to.
|
||||
// - weakTab: weak reference to the tab that the panes belong to.
|
||||
// - paneIds: collection of the IDs of the panes that are marked for removal.
|
||||
void TerminalPage::_ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds)
|
||||
safe_void_coroutine TerminalPage::_ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds)
|
||||
{
|
||||
// Show a single aggregate confirmation for closing multiple panes.
|
||||
if (_settings.GlobalSettings().ConfirmCloseOn() != ConfirmCloseOn::Never)
|
||||
{
|
||||
const auto weak = get_weak();
|
||||
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::MultiplePanes);
|
||||
if (!weak.get() || warningResult != ContentDialogResult::Primary)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
_CloseRemainingPanes(weakTab, std::move(paneIds));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Recursively closes panes by ID, chaining each close via the
|
||||
// ClosedByParent callback. Called after confirmation has already
|
||||
// been handled by _ClosePanes.
|
||||
// Arguments:
|
||||
// - weakTab: weak reference to the tab that the panes belong to.
|
||||
// - paneIds: remaining pane IDs to close.
|
||||
void TerminalPage::_CloseRemainingPanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds)
|
||||
{
|
||||
if (auto strongTab{ weakTab.get() })
|
||||
{
|
||||
// Close all unfocused panes one by one
|
||||
while (!paneIds.empty())
|
||||
{
|
||||
const auto id = paneIds.back();
|
||||
@@ -813,11 +872,10 @@ namespace winrt::TerminalApp::implementation
|
||||
pane->ClosedByParent([ids{ std::move(paneIds) }, weakThis{ get_weak() }, weakTab]() {
|
||||
if (auto strongThis{ weakThis.get() })
|
||||
{
|
||||
strongThis->_ClosePanes(weakTab, std::move(ids));
|
||||
strongThis->_CloseRemainingPanes(weakTab, std::move(ids));
|
||||
}
|
||||
});
|
||||
|
||||
// Close the pane which will eventually trigger the closed by parent event
|
||||
_HandleClosePaneRequested(pane);
|
||||
break;
|
||||
}
|
||||
@@ -840,19 +898,35 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Closes provided tabs one by one
|
||||
// - Closes provided tabs one by one, showing a single aggregate
|
||||
// confirmation dialog upfront if the confirmCloseOn setting warrants it.
|
||||
// Arguments:
|
||||
// - tabs - tabs to remove
|
||||
safe_void_coroutine TerminalPage::_RemoveTabs(const std::vector<winrt::TerminalApp::Tab> tabs)
|
||||
{
|
||||
if (tabs.empty())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
const auto weak = get_weak();
|
||||
|
||||
// Show a single aggregate confirmation instead of per-tab dialogs.
|
||||
if (_settings.GlobalSettings().ConfirmCloseOn() != ConfirmCloseOn::Never)
|
||||
{
|
||||
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::MultipleTabs);
|
||||
if (!weak.get() || warningResult != ContentDialogResult::Primary)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& tab : tabs)
|
||||
{
|
||||
winrt::Windows::Foundation::IAsyncAction action{ nullptr };
|
||||
if (const auto strong = weak.get())
|
||||
{
|
||||
action = _HandleCloseTabRequested(tab);
|
||||
action = _HandleCloseTabRequested(tab, true);
|
||||
}
|
||||
|
||||
if (!action)
|
||||
|
||||
@@ -884,26 +884,63 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Displays a dialog to warn the user that they are about to close all open windows.
|
||||
// Once the user clicks the OK button, shut down the application.
|
||||
// If cancel is clicked, the dialog will close.
|
||||
// - Displays the unified close confirmation dialog configured for the
|
||||
// given scenario. Resets the "don't ask me again" checkbox before showing.
|
||||
// If the user confirms and checked "don't ask me again", sets
|
||||
// confirmCloseOn to Never and writes settings to disk.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See _ShowDialog for details
|
||||
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowQuitDialog()
|
||||
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowConfirmCloseDialog(ConfirmCloseDialogKind kind)
|
||||
{
|
||||
return _ShowDialogHelper(L"QuitDialog");
|
||||
}
|
||||
// Load the dialog (triggers x:Load) and configure its strings.
|
||||
const auto dialog = FindName(L"ConfirmCloseDialog").as<ContentDialog>();
|
||||
switch (kind)
|
||||
{
|
||||
case ConfirmCloseDialogKind::CloseAll:
|
||||
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_CloseAllTitle")));
|
||||
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_CloseAllPrimary"));
|
||||
break;
|
||||
case ConfirmCloseDialogKind::Window:
|
||||
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_WindowTitle")));
|
||||
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_WindowPrimary"));
|
||||
break;
|
||||
case ConfirmCloseDialogKind::Tab:
|
||||
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_TabTitle")));
|
||||
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_TabPrimary"));
|
||||
break;
|
||||
case ConfirmCloseDialogKind::MultiplePanes:
|
||||
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_MultiplePanesTitle")));
|
||||
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_MultiplePanesPrimary"));
|
||||
break;
|
||||
case ConfirmCloseDialogKind::MultipleTabs:
|
||||
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_MultipleTabsTitle")));
|
||||
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_MultipleTabsPrimary"));
|
||||
break;
|
||||
case ConfirmCloseDialogKind::Pane:
|
||||
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_PaneTitle")));
|
||||
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_PanePrimary"));
|
||||
break;
|
||||
}
|
||||
dialog.CloseButtonText(RS_(L"ConfirmCloseDialog_Cancel"));
|
||||
|
||||
// Method Description:
|
||||
// - Displays a dialog for warnings found while closing the terminal app using
|
||||
// key binding with multiple tabs opened. Display messages to warn user
|
||||
// that more than 1 tab is opened, and once the user clicks the OK button, remove
|
||||
// all the tabs and shut down and app. If cancel is clicked, the dialog will close
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See _ShowDialog for details
|
||||
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowCloseWarningDialog()
|
||||
{
|
||||
return _ShowDialogHelper(L"CloseAllDialog");
|
||||
// BODGY: After a ContentDialog is dismissed, FindName() can no longer
|
||||
// resolve children inside it. Use Content() to get the checkbox directly.
|
||||
const auto checkbox = dialog.Content().as<CheckBox>();
|
||||
checkbox.IsChecked(false);
|
||||
|
||||
auto result = ContentDialogResult::None;
|
||||
if (auto presenter{ _dialogPresenter.get() })
|
||||
{
|
||||
result = co_await presenter.ShowDialog(dialog);
|
||||
}
|
||||
|
||||
if (result == ContentDialogResult::Primary && checkbox.IsChecked().Value())
|
||||
{
|
||||
_settings.GlobalSettings().ConfirmCloseOn(ConfirmCloseOn::Never);
|
||||
_settings.WriteSettingsToDisk();
|
||||
}
|
||||
|
||||
co_return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1600,7 +1637,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Replace the Starting directory with the CWD, if given
|
||||
const auto workingDirectory = control.WorkingDirectory();
|
||||
if (Utils::IsValidDirectory(workingDirectory.c_str()))
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
|
||||
}
|
||||
@@ -2209,12 +2247,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// signal that we want to close everything.
|
||||
safe_void_coroutine TerminalPage::RequestQuit()
|
||||
{
|
||||
if (!_displayingCloseDialog)
|
||||
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
|
||||
if (setting != ConfirmCloseOn::Never && !_displayingCloseDialog)
|
||||
{
|
||||
_displayingCloseDialog = true;
|
||||
|
||||
const auto weak = get_weak();
|
||||
auto warningResult = co_await _ShowQuitDialog();
|
||||
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::CloseAll);
|
||||
const auto strong = weak.get();
|
||||
if (!strong)
|
||||
{
|
||||
@@ -2227,9 +2266,9 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
QuitRequested.raise(nullptr, nullptr);
|
||||
}
|
||||
|
||||
QuitRequested.raise(nullptr, nullptr);
|
||||
}
|
||||
|
||||
void TerminalPage::PersistState()
|
||||
@@ -2307,12 +2346,75 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Close the terminal app. If there is more
|
||||
// than one tab opened, show a warning dialog.
|
||||
// - Determines whether a close-window action should show a confirmation
|
||||
// dialog, based on the confirmCloseOn setting and the current window state.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - true if a warning dialog should be shown before closing the window.
|
||||
bool TerminalPage::_ShouldWarnOnClose() const
|
||||
{
|
||||
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
|
||||
|
||||
switch (setting)
|
||||
{
|
||||
case ConfirmCloseOn::Always:
|
||||
return true;
|
||||
|
||||
case ConfirmCloseOn::Automatic:
|
||||
{
|
||||
// Warn if there's more than one tab.
|
||||
if (_HasMultipleTabs())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Warn if the one tab has more than one pane.
|
||||
if (_GetTabImpl(_tabs.GetAt(0))->GetLeafPaneCount() > 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case ConfirmCloseOn::Never:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether closing a specific tab should show a confirmation
|
||||
// dialog, based on the confirmCloseOn setting and the tab's state.
|
||||
// Arguments:
|
||||
// - tab: The tab being closed.
|
||||
// Return Value:
|
||||
// - true if a warning dialog should be shown before closing the tab.
|
||||
bool TerminalPage::_ShouldWarnOnCloseTab(const winrt::com_ptr<Tab>& tab) const
|
||||
{
|
||||
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
|
||||
|
||||
switch (setting)
|
||||
{
|
||||
case ConfirmCloseOn::Always:
|
||||
return true;
|
||||
|
||||
case ConfirmCloseOn::Automatic:
|
||||
// Warn if this tab has more than one pane.
|
||||
return tab->GetLeafPaneCount() > 1;
|
||||
|
||||
case ConfirmCloseOn::Never:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Close the terminal app. If the confirmCloseOn setting indicates we should
|
||||
// warn for the current window state, show a warning dialog.
|
||||
safe_void_coroutine TerminalPage::CloseWindow()
|
||||
{
|
||||
if (_HasMultipleTabs() &&
|
||||
_settings.GlobalSettings().ConfirmCloseAllTabs() &&
|
||||
if (_ShouldWarnOnClose() &&
|
||||
!_displayingCloseDialog)
|
||||
{
|
||||
if (_newTabButton && _newTabButton.Flyout())
|
||||
@@ -2321,7 +2423,14 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
_DismissTabContextMenus();
|
||||
_displayingCloseDialog = true;
|
||||
auto warningResult = co_await _ShowCloseWarningDialog();
|
||||
|
||||
const auto weak = get_weak();
|
||||
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::Window);
|
||||
if (!weak.get())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
_displayingCloseDialog = false;
|
||||
|
||||
if (warningResult != ContentDialogResult::Primary)
|
||||
@@ -2972,11 +3081,7 @@ namespace winrt::TerminalApp::implementation
|
||||
text = winrt::hstring{ Utils::TrimPaste(text) };
|
||||
}
|
||||
|
||||
// LOAD BEARING: Send an empty bracketed paste even if the clipboard was empty.
|
||||
// Bracketed Paste provides an application a way to know whether the
|
||||
// user pasted, even if there was no applicable content on it. This
|
||||
// behavior is observed in GNOME Terminal, among others.
|
||||
if (!bracketedPaste && text.empty())
|
||||
if (text.empty())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
@@ -3558,7 +3663,8 @@ namespace winrt::TerminalApp::implementation
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile);
|
||||
const auto workingDirectory = tabImpl->GetActiveTerminalControl().WorkingDirectory();
|
||||
if (Utils::IsValidDirectory(workingDirectory.c_str()))
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
|
||||
}
|
||||
@@ -3744,13 +3850,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// for nulls
|
||||
if (const auto& connection{ _duplicateConnectionForRestart(paneContent) })
|
||||
{
|
||||
// Reset the terminal's VT state before attaching the new connection.
|
||||
// The previous client may have left dirty modes (e.g., bracketed
|
||||
// paste, mouse tracking, alternate buffer, kitty keyboard) that
|
||||
// would corrupt input/output for the new shell process.
|
||||
const auto& termControl = paneContent.GetTermControl();
|
||||
termControl.HardResetWithoutErase();
|
||||
termControl.Connection(connection);
|
||||
paneContent.GetTermControl().Connection(connection);
|
||||
connection.Start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,16 @@ namespace winrt::TerminalApp::implementation
|
||||
ScrollDown = 1
|
||||
};
|
||||
|
||||
enum class ConfirmCloseDialogKind
|
||||
{
|
||||
Pane,
|
||||
Tab,
|
||||
MultiplePanes,
|
||||
MultipleTabs,
|
||||
Window,
|
||||
CloseAll
|
||||
};
|
||||
|
||||
struct RenameWindowRequestedArgs : RenameWindowRequestedArgsT<RenameWindowRequestedArgs>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::hstring, ProposedName);
|
||||
@@ -301,8 +311,7 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
|
||||
|
||||
void _ShowAboutDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowQuitDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseWarningDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowConfirmCloseDialog(ConfirmCloseDialogKind kind);
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseReadOnlyDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowMultiLinePasteWarningDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowLargePasteWarningDialog();
|
||||
@@ -349,7 +358,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
safe_void_coroutine _ExportTab(const Tab& tab, winrt::hstring filepath);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::Tab tab);
|
||||
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::Tab tab, bool skipConfirmClose = false);
|
||||
void _CloseTabAtIndex(uint32_t index);
|
||||
void _RemoveTab(const winrt::TerminalApp::Tab& tab);
|
||||
safe_void_coroutine _RemoveTabs(const std::vector<winrt::TerminalApp::Tab> tabs);
|
||||
@@ -400,9 +409,12 @@ namespace winrt::TerminalApp::implementation
|
||||
TerminalApp::Tab _GetTabByTabViewItem(const IInspectable& tabViewItem) const noexcept;
|
||||
|
||||
void _HandleClosePaneRequested(std::shared_ptr<Pane> pane);
|
||||
bool _ShouldWarnOnClose() const;
|
||||
bool _ShouldWarnOnCloseTab(const winrt::com_ptr<Tab>& tab) const;
|
||||
safe_void_coroutine _SetFocusedTab(const winrt::TerminalApp::Tab tab);
|
||||
safe_void_coroutine _CloseFocusedPane();
|
||||
void _ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds);
|
||||
safe_void_coroutine _ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds);
|
||||
void _CloseRemainingPanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds);
|
||||
winrt::Windows::Foundation::IAsyncOperation<bool> _PaneConfirmCloseReadOnly(std::shared_ptr<Pane> pane);
|
||||
void _AddPreviouslyClosedPaneOrTab(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>&& args);
|
||||
|
||||
|
||||
@@ -86,17 +86,12 @@
|
||||
Grid.Row="2"
|
||||
x:Load="False" />
|
||||
|
||||
<ContentDialog x:Name="QuitDialog"
|
||||
x:Uid="QuitDialog"
|
||||
<ContentDialog x:Name="ConfirmCloseDialog"
|
||||
Grid.Row="2"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary" />
|
||||
|
||||
<ContentDialog x:Name="CloseAllDialog"
|
||||
x:Uid="CloseAllDialog"
|
||||
Grid.Row="2"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary" />
|
||||
DefaultButton="Primary">
|
||||
<CheckBox x:Uid="DontAskAgainCheckBox" />
|
||||
</ContentDialog>
|
||||
|
||||
<ContentDialog x:Name="CloseReadOnlyDialog"
|
||||
x:Uid="CloseReadOnlyDialog"
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
args.Profile(::Microsoft::Console::Utils::GuidToString(_profile.Guid()));
|
||||
// If we know the user's working directory use it instead of the profile.
|
||||
if (const auto dir = _control.WorkingDirectory(); ::Microsoft::Console::Utils::IsValidDirectory(dir.c_str()))
|
||||
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
|
||||
{
|
||||
args.StartingDirectory(dir);
|
||||
}
|
||||
|
||||
@@ -355,12 +355,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ControlCore::HardResetWithoutErase()
|
||||
{
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_terminal->HardResetWithoutErase();
|
||||
}
|
||||
|
||||
bool ControlCore::Initialize(const float actualWidth,
|
||||
const float actualHeight,
|
||||
const float compositionScale)
|
||||
@@ -2081,10 +2075,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// the selection (we need to reset selection on double-click or
|
||||
// triple-click, so it captures the word or the line, rather than
|
||||
// extending the selection)
|
||||
// - GH#9608: VT mouse mode is enabled. In this mode, Shift is used
|
||||
// to override mouse input, so Shift+Click should start a fresh
|
||||
// selection rather than extending the previous one.
|
||||
if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition || _terminal->IsTrackingMouseInput()))
|
||||
if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition))
|
||||
{
|
||||
// Reset the selection
|
||||
_terminal->ClearSelection();
|
||||
@@ -2426,6 +2417,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return *context;
|
||||
}
|
||||
|
||||
winrt::hstring ControlCore::CurrentWorkingDirectory() const
|
||||
{
|
||||
return winrt::hstring{ _terminal->GetWorkingDirectory() };
|
||||
}
|
||||
|
||||
bool ControlCore::QuickFixesAvailable() const noexcept
|
||||
{
|
||||
return _cachedQuickFixes && _cachedQuickFixes.Size() > 0;
|
||||
|
||||
@@ -191,6 +191,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ContextMenuSelectCommand();
|
||||
void ContextMenuSelectOutput();
|
||||
|
||||
winrt::hstring CurrentWorkingDirectory() const;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ITerminalInput
|
||||
@@ -255,7 +257,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
TerminalConnection::ITerminalConnection Connection();
|
||||
void Connection(const TerminalConnection::ITerminalConnection& connection);
|
||||
void HardResetWithoutErase();
|
||||
|
||||
void AnchorContextMenu(til::point viewportRelativeCharacterPosition);
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ namespace Microsoft.Terminal.Control
|
||||
void ApplyAppearance(Boolean focused);
|
||||
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection;
|
||||
void HardResetWithoutErase();
|
||||
|
||||
IControlSettings Settings { get; };
|
||||
IControlAppearance FocusedAppearance { get; };
|
||||
|
||||
@@ -54,20 +54,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
self->Attached.raise(*self, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
// GH#14464: Mark mode and quick-edit (shift+arrow) selections update
|
||||
// the selection through ControlCore, bypassing SetEndSelectionPoint.
|
||||
// Listen for selection changes so _selectionNeedsToBeCopied is set
|
||||
// for ALL selection types, not just mouse drag.
|
||||
_core->UpdateSelectionMarkers([weakThis = get_weak()](auto&&, auto&&) {
|
||||
if (auto self{ weakThis.get() })
|
||||
{
|
||||
if (self->_core->HasSelection())
|
||||
{
|
||||
self->_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
uint64_t ControlInteractivity::Id()
|
||||
@@ -169,8 +155,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlInteractivity::GotFocus()
|
||||
{
|
||||
_focused = true;
|
||||
|
||||
if (_uiaEngine.get())
|
||||
{
|
||||
THROW_IF_FAILED(_uiaEngine->Enable());
|
||||
@@ -183,8 +167,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlInteractivity::LostFocus()
|
||||
{
|
||||
_focused = false;
|
||||
|
||||
if (_uiaEngine.get())
|
||||
{
|
||||
THROW_IF_FAILED(_uiaEngine->Disable());
|
||||
@@ -252,8 +234,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
PasteFromClipboard.raise(*this, std::move(args));
|
||||
}
|
||||
|
||||
void ControlInteractivity::PointerPressed(const uint32_t /*pointerId*/,
|
||||
Control::MouseButtonState buttonState,
|
||||
void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const uint64_t timestamp,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
@@ -266,10 +247,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto shiftEnabled = modifiers.IsShiftPressed();
|
||||
const auto ctrlEnabled = modifiers.IsCtrlPressed();
|
||||
|
||||
// Mark that this pointer event actually started within our bounds.
|
||||
// We'll need this later, for PointerMoved events.
|
||||
_pointerPressedInBounds = true;
|
||||
|
||||
// GH#9396: we prioritize hyper-link over VT mouse events
|
||||
auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point());
|
||||
if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
|
||||
@@ -308,10 +285,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
const auto isOnOriginalPosition = _lastMouseClickPosNoSelection == pixelPosition;
|
||||
|
||||
// Rounded coordinates for text selection.
|
||||
// Don't round in VT mouse mode; cell-level precision matters more
|
||||
const auto round = !_core->IsVtMouseModeEnabled();
|
||||
_core->LeftClickOnTerminal(_getTerminalPosition(til::point{ pixelPosition }, round),
|
||||
// Rounded coordinates for text selection
|
||||
_core->LeftClickOnTerminal(_getTerminalPosition(til::point{ pixelPosition }, true),
|
||||
multiClickMapper,
|
||||
altEnabled,
|
||||
shiftEnabled,
|
||||
@@ -321,15 +296,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (_core->HasSelection())
|
||||
{
|
||||
// GH#9787: if selection is active we don't want to track the touchdown position
|
||||
// so that dragging the mouse will extend the selection rather than starting the new one.
|
||||
// In VT mouse mode, keep tracking the touchdown point so that PointerMoved
|
||||
// can re-anchor the selection based on drag direction (the dx < 0 adjustment).
|
||||
// Without this, dragging left wouldn't include the initially clicked cell
|
||||
// because floored coordinates place the anchor on the cell's left edge.
|
||||
if (!_core->IsVtMouseModeEnabled())
|
||||
{
|
||||
_singleClickTouchdownPos = std::nullopt;
|
||||
}
|
||||
// so that dragging the mouse will extend the selection rather than starting the new one
|
||||
_singleClickTouchdownPos = std::nullopt;
|
||||
}
|
||||
}
|
||||
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
|
||||
@@ -346,41 +314,37 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
// GH#19942, GH#14464: Don't re-copy a selection that was
|
||||
// already copied via copyOnSelect on mouse-up. But DO copy
|
||||
// if the selection was made via mark mode or modified with
|
||||
// quick-edit keys (shift+arrow), since those paths never
|
||||
// triggered an automatic copy.
|
||||
const auto copied = (_selectionNeedsToBeCopied || !_core->CopyOnSelect()) &&
|
||||
CopySelectionToClipboard(shiftEnabled, false, _core->Settings().CopyFormatting());
|
||||
// Try to copy the text and clear the selection
|
||||
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, false, _core->Settings().CopyFormatting());
|
||||
_core->ClearSelection();
|
||||
if (_core->CopyOnSelect() || !copied)
|
||||
if (_core->CopyOnSelect() || !successfulCopy)
|
||||
{
|
||||
// CopyOnSelect: right-click always pastes.
|
||||
// Otherwise: no selection → paste.
|
||||
// CopyOnSelect: right click always pastes!
|
||||
// Otherwise: no selection --> paste
|
||||
RequestPasteTextFromClipboard();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlInteractivity::TouchPressed(const Core::Point contactPoint)
|
||||
void ControlInteractivity::TouchPressed(const winrt::Windows::Foundation::Point contactPoint)
|
||||
{
|
||||
_touchAnchor = contactPoint;
|
||||
}
|
||||
|
||||
bool ControlInteractivity::PointerMoved(const uint32_t /*pointerId*/,
|
||||
Control::MouseButtonState buttonState,
|
||||
bool ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition)
|
||||
const bool focused,
|
||||
const Core::Point pixelPosition,
|
||||
const bool pointerPressedInBounds)
|
||||
{
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
|
||||
// Returning true from this function indicates that the caller should do no further processing of this movement.
|
||||
bool handledCompletely = false;
|
||||
|
||||
// Short-circuit isReadOnly check to avoid warning dialog
|
||||
if (_focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
|
||||
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
|
||||
{
|
||||
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
|
||||
handledCompletely = true;
|
||||
@@ -389,7 +353,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// actually start _in_ the control bounds. Case in point - someone drags
|
||||
// a file into the bounds of the control. That shouldn't send the
|
||||
// selection into space.
|
||||
else if (_focused && _pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
|
||||
else if (focused && pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
|
||||
{
|
||||
if (_singleClickTouchdownPos)
|
||||
{
|
||||
@@ -435,9 +399,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return handledCompletely;
|
||||
}
|
||||
|
||||
void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint)
|
||||
void ControlInteractivity::TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
|
||||
const bool focused)
|
||||
{
|
||||
if (_focused &&
|
||||
if (focused &&
|
||||
_touchAnchor)
|
||||
{
|
||||
const auto anchor = _touchAnchor.value();
|
||||
@@ -471,14 +436,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ControlInteractivity::PointerReleased(const uint32_t /*pointerId*/,
|
||||
Control::MouseButtonState buttonState,
|
||||
void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition)
|
||||
{
|
||||
_pointerPressedInBounds = false;
|
||||
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
|
||||
// Short-circuit isReadOnly check to avoid warning dialog
|
||||
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
|
||||
@@ -720,9 +682,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - cursorPosition: in pixels, relative to the origin of the control
|
||||
void ControlInteractivity::SetEndSelectionPoint(const Core::Point pixelPosition)
|
||||
{
|
||||
// Don't round in VT mouse mode; cell-level precision matters more
|
||||
const auto round = !_core->IsVtMouseModeEnabled();
|
||||
_core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, round));
|
||||
_core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, true));
|
||||
_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,23 +51,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
|
||||
|
||||
#pragma region Input Methods
|
||||
void PointerPressed(const uint32_t pointerId,
|
||||
Control::MouseButtonState buttonState,
|
||||
void PointerPressed(Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const uint64_t timestamp,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition);
|
||||
void TouchPressed(const Core::Point contactPoint);
|
||||
void TouchPressed(const winrt::Windows::Foundation::Point contactPoint);
|
||||
|
||||
bool PointerMoved(const uint32_t pointerId,
|
||||
Control::MouseButtonState buttonState,
|
||||
bool PointerMoved(Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition);
|
||||
void TouchMoved(const Core::Point newTouchPoint);
|
||||
const bool focused,
|
||||
const Core::Point pixelPosition,
|
||||
const bool pointerPressedInBounds);
|
||||
void TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
|
||||
const bool focused);
|
||||
|
||||
void PointerReleased(const uint32_t pointerId,
|
||||
Control::MouseButtonState buttonState,
|
||||
void PointerReleased(Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition);
|
||||
@@ -115,7 +115,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
// If this is set, then we assume we are in the middle of panning the
|
||||
// viewport via touch input.
|
||||
std::optional<Core::Point> _touchAnchor;
|
||||
std::optional<winrt::Windows::Foundation::Point> _touchAnchor;
|
||||
|
||||
using Timestamp = uint64_t;
|
||||
|
||||
@@ -142,9 +142,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
uint64_t _id;
|
||||
static std::atomic<uint64_t> _nextId;
|
||||
|
||||
bool _focused{ false };
|
||||
bool _pointerPressedInBounds{ false };
|
||||
|
||||
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
|
||||
void _updateSystemParameterSettings() noexcept;
|
||||
|
||||
|
||||
@@ -36,24 +36,24 @@ namespace Microsoft.Terminal.Control
|
||||
void RequestPasteTextFromClipboard();
|
||||
void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point);
|
||||
|
||||
void PointerPressed(UInt32 pointerId,
|
||||
MouseButtonState buttonState,
|
||||
void PointerPressed(MouseButtonState buttonState,
|
||||
UInt32 pointerUpdateKind,
|
||||
UInt64 timestamp,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers,
|
||||
Microsoft.Terminal.Core.Point pixelPosition);
|
||||
void TouchPressed(Microsoft.Terminal.Core.Point contactPoint);
|
||||
void TouchPressed(Windows.Foundation.Point contactPoint);
|
||||
|
||||
Boolean PointerMoved(UInt32 pointerId,
|
||||
MouseButtonState buttonState,
|
||||
Boolean PointerMoved(MouseButtonState buttonState,
|
||||
UInt32 pointerUpdateKind,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers,
|
||||
Microsoft.Terminal.Core.Point pixelPosition);
|
||||
Boolean focused,
|
||||
Microsoft.Terminal.Core.Point pixelPosition,
|
||||
Boolean pointerPressedInBounds);
|
||||
|
||||
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint);
|
||||
void TouchMoved(Windows.Foundation.Point newTouchPoint,
|
||||
Boolean focused);
|
||||
|
||||
void PointerReleased(UInt32 pointerId,
|
||||
MouseButtonState buttonState,
|
||||
void PointerReleased(MouseButtonState buttonState,
|
||||
UInt32 pointerUpdateKind,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers,
|
||||
Microsoft.Terminal.Core.Point pixelPosition);
|
||||
|
||||
@@ -76,7 +76,6 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean RepositionCursorWithMouse { get; };
|
||||
|
||||
PathTranslationStyle PathTranslationStyle { get; };
|
||||
String DragDropDelimiter { get; };
|
||||
|
||||
// NOTE! When adding something here, make sure to update ControlProperties.h too!
|
||||
};
|
||||
|
||||
@@ -60,5 +60,7 @@ namespace Microsoft.Terminal.Control
|
||||
void SelectOutput(Boolean goUp);
|
||||
IVector<ScrollMark> ScrollMarks { get; };
|
||||
|
||||
String CurrentWorkingDirectory { get; };
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,10 +49,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider)
|
||||
{
|
||||
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
|
||||
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
|
||||
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
|
||||
_parentProvider = parentProvider ? parentProvider.as<IAutomationPeerProtected>().ProviderFromPeer(parentProvider) : nullptr;
|
||||
_parentProvider = parentProvider;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -184,11 +181,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const
|
||||
{
|
||||
if (!_parentProvider)
|
||||
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
|
||||
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
|
||||
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
|
||||
const auto parent{ _parentProvider.get() };
|
||||
if (!parent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, _parentProvider);
|
||||
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parent.as<IAutomationPeerProtected>().ProviderFromPeer(parent));
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
};
|
||||
|
||||
@@ -200,24 +201,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders)
|
||||
com_array<XamlAutomation::ITextRangeProvider> InteractivityAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
|
||||
{
|
||||
if (!_parentProvider)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// transfer ownership of UiaTextRanges to this new vector
|
||||
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
|
||||
const auto len = gsl::narrow<uint32_t>(providers.size());
|
||||
com_array<XamlAutomation::ITextRangeProvider> result{ len };
|
||||
auto count = gsl::narrow<int>(providers.size());
|
||||
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
std::vector<XamlAutomation::ITextRangeProvider> vec;
|
||||
vec.reserve(count);
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
if (auto xutr = _CreateXamlUiaTextRange(providers[i].detach()))
|
||||
{
|
||||
result[i] = std::move(xutr);
|
||||
vec.emplace_back(std::move(xutr));
|
||||
}
|
||||
}
|
||||
|
||||
com_array<XamlAutomation::ITextRangeProvider> result{ vec };
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
|
||||
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
|
||||
winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider{ nullptr };
|
||||
weak_ref<Windows::UI::Xaml::Automation::Peers::AutomationPeer> _parentProvider;
|
||||
|
||||
til::rect _controlBounds{};
|
||||
til::rect _controlPadding{};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
[default_interface] runtimeclass InteractivityAutomationPeer :
|
||||
Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer,
|
||||
Windows.UI.Xaml.Automation.Peers.AutomationPeer,
|
||||
Windows.UI.Xaml.Automation.Provider.ITextProvider
|
||||
{
|
||||
|
||||
|
||||
@@ -532,11 +532,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_core.Connection(newConnection);
|
||||
}
|
||||
|
||||
void TermControl::HardResetWithoutErase()
|
||||
{
|
||||
_core.HardResetWithoutErase();
|
||||
}
|
||||
|
||||
void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
|
||||
{
|
||||
if (!_initializedTerminal)
|
||||
@@ -1795,16 +1790,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return true;
|
||||
}
|
||||
|
||||
// While TSF has an active composition, key events must not be forwarded
|
||||
// to the PTY directly. The IME re-injects navigation/confirmation keys
|
||||
// after composition ends, at which point HasActiveComposition() == false
|
||||
// and they are forwarded normally. This mirrors conhost v1 behavior in
|
||||
// src/interactivity/win32/windowio.cpp.
|
||||
if (GetTSFHandle().HasActiveComposition())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown))
|
||||
{
|
||||
return true;
|
||||
@@ -1970,13 +1955,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto point = args.GetCurrentPoint(*this);
|
||||
const auto type = ptr.PointerDeviceType();
|
||||
|
||||
// GH#19908: _focused can be true even when the search box has
|
||||
// keyboard focus, because GotFocus bubbles from the search box
|
||||
// child and _GotFocusHandler sets _focused=true. If the user
|
||||
// click-drags in the terminal while the search box is focused,
|
||||
// we need to explicitly call Focus() to move keyboard focus back
|
||||
// to the terminal so that Ctrl+C copies the selection.
|
||||
if (!_focused || (_searchBox && _searchBox->ContainsFocus()))
|
||||
if (!_focused)
|
||||
{
|
||||
Focus(FocusState::Pointer);
|
||||
}
|
||||
@@ -1990,14 +1969,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// NB: I don't think this is correct because the touch should be in the center of the rect.
|
||||
// I suspect the point.Position() would be correct.
|
||||
const auto contactRect = point.Properties().ContactRect();
|
||||
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
|
||||
_interactivity.TouchPressed(newTouchPoint.to_core_point());
|
||||
_interactivity.TouchPressed({ contactRect.X, contactRect.Y });
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cursorPosition = point.Position();
|
||||
_interactivity.PointerPressed(point.PointerId(),
|
||||
TermControl::GetPressedMouseButtons(point),
|
||||
_interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point),
|
||||
TermControl::GetPointerUpdateKind(point),
|
||||
point.Timestamp(),
|
||||
ControlKeyStates{ args.KeyModifiers() },
|
||||
@@ -2036,11 +2013,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
|
||||
type == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
{
|
||||
auto suppressFurtherHandling = _interactivity.PointerMoved(point.PointerId(),
|
||||
TermControl::GetPressedMouseButtons(point),
|
||||
auto suppressFurtherHandling = _interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point),
|
||||
TermControl::GetPointerUpdateKind(point),
|
||||
ControlKeyStates(args.KeyModifiers()),
|
||||
pixelPosition);
|
||||
_focused,
|
||||
pixelPosition,
|
||||
_pointerPressedInBounds);
|
||||
|
||||
// GH#9109 - Only start an auto-scroll when the drag actually
|
||||
// started within our bounds. Otherwise, someone could start a drag
|
||||
@@ -2079,9 +2057,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
|
||||
{
|
||||
const auto contactRect = point.Properties().ContactRect();
|
||||
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
|
||||
|
||||
_interactivity.TouchMoved(newTouchPoint.to_core_point());
|
||||
_interactivity.TouchMoved({ contactRect.X, contactRect.Y }, _focused);
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
@@ -2114,8 +2090,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
|
||||
type == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
{
|
||||
_interactivity.PointerReleased(point.PointerId(),
|
||||
TermControl::GetPressedMouseButtons(point),
|
||||
_interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point),
|
||||
TermControl::GetPointerUpdateKind(point),
|
||||
ControlKeyStates(args.KeyModifiers()),
|
||||
pixelPosition);
|
||||
@@ -2526,9 +2501,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
_updateScrollBar->Run(update);
|
||||
|
||||
// If we have a selection with markers (exposed via selection mode),
|
||||
// update the position of the markers
|
||||
if (_core.HasSelection() && _core.SelectionMode() >= SelectionInteractionMode::Keyboard)
|
||||
// if a selection marker is already visible,
|
||||
// update the position of those markers
|
||||
if (SelectionStartMarker().Visibility() == Visibility::Visible || SelectionEndMarker().Visibility() == Visibility::Visible)
|
||||
{
|
||||
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
|
||||
}
|
||||
@@ -3079,7 +3054,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// the full path of the file to our terminal connection. Like conhost, if
|
||||
// the path contains a space, we'll wrap the path in quotes.
|
||||
// - Unlike conhost, if multiple files are dropped onto the terminal, we'll
|
||||
// write all the paths to the terminal, separated by the configured delimiter.
|
||||
// write all the paths to the terminal, separated by spaces.
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the Drop event
|
||||
// Return Value:
|
||||
@@ -3092,11 +3067,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (const auto hwnd = reinterpret_cast<HWND>(OwningHwnd()))
|
||||
{
|
||||
SetForegroundWindow(hwnd);
|
||||
}
|
||||
|
||||
const auto weak = get_weak();
|
||||
|
||||
if (e.DataView().Contains(StandardDataFormats::ApplicationLink()))
|
||||
@@ -3200,13 +3170,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
std::wstring allPathsString;
|
||||
const auto delimiter{ _core.Settings().DragDropDelimiter() };
|
||||
for (auto& fullPath : fullPaths)
|
||||
{
|
||||
// Join the paths with the delimiter
|
||||
// Join the paths with spaces
|
||||
if (!allPathsString.empty())
|
||||
{
|
||||
allPathsString += delimiter;
|
||||
allPathsString += L" ";
|
||||
}
|
||||
|
||||
const auto translationStyle{ _core.Settings().PathTranslationStyle() };
|
||||
@@ -3738,6 +3707,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
return _core.CommandHistory();
|
||||
}
|
||||
winrt::hstring TermControl::CurrentWorkingDirectory() const
|
||||
{
|
||||
return _core.CurrentWorkingDirectory();
|
||||
}
|
||||
|
||||
void TermControl::UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector<hstring> suggestions)
|
||||
{
|
||||
|
||||
@@ -117,6 +117,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
|
||||
void SelectCommand(const bool goUp);
|
||||
void SelectOutput(const bool goUp);
|
||||
|
||||
winrt::hstring CurrentWorkingDirectory() const;
|
||||
#pragma endregion
|
||||
|
||||
void ScrollViewport(int viewTop);
|
||||
@@ -190,7 +192,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
TerminalConnection::ITerminalConnection Connection();
|
||||
void Connection(const TerminalConnection::ITerminalConnection& connection);
|
||||
void HardResetWithoutErase();
|
||||
|
||||
Control::CursorDisplayState CursorVisibility() const noexcept;
|
||||
void CursorVisibility(Control::CursorDisplayState cursorVisibility);
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace Microsoft.Terminal.Control
|
||||
void SetOverrideColorScheme(Microsoft.Terminal.Core.ICoreScheme scheme);
|
||||
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection;
|
||||
void HardResetWithoutErase();
|
||||
|
||||
UInt64 ContentId{ get; };
|
||||
|
||||
|
||||
@@ -121,9 +121,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// GH#13978: If the TermControl has already been removed from the UI tree, XAML might run into weird bugs.
|
||||
// This will prevent the `dispatcher.RunAsync` calls below from raising UIA events on the main thread.
|
||||
_termControl = {};
|
||||
|
||||
// Solve the circular reference between us and the content automation peer.
|
||||
_contentAutomationPeer.ParentProvider(nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -55,18 +55,6 @@ void Terminal::Create(til::size viewportSize, til::CoordType scrollbackLines, Re
|
||||
_stateMachine = std::make_unique<StateMachine>(std::move(engine));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Resets all VT state to defaults without clearing the buffer content.
|
||||
// Called when a connection is restarted so that any dirty modes left
|
||||
// behind by a crashed application don't affect the new connection.
|
||||
void Terminal::HardResetWithoutErase()
|
||||
{
|
||||
_assertLocked();
|
||||
_stateMachine->ResetState();
|
||||
auto& engine = reinterpret_cast<OutputStateMachineEngine&>(_stateMachine->Engine());
|
||||
engine.Dispatch().HardReset(false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Initializes the Terminal from the given set of settings.
|
||||
// Arguments:
|
||||
@@ -1534,10 +1522,6 @@ void Terminal::SerializeMainBuffer(HANDLE handle) const
|
||||
_mainBuffer->SerializeTo(handle);
|
||||
}
|
||||
|
||||
void Terminal::UnknownSequence() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode)
|
||||
{
|
||||
const auto colorSelection = [this](const til::point coordStartInclusive, const til::point coordEndExclusive, const TextAttribute& attr) {
|
||||
|
||||
@@ -85,7 +85,6 @@ public:
|
||||
void Create(til::size viewportSize,
|
||||
til::CoordType scrollbackLines,
|
||||
Microsoft::Console::Render::Renderer& renderer);
|
||||
void HardResetWithoutErase();
|
||||
|
||||
void CreateFromSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings,
|
||||
Microsoft::Console::Render::Renderer& renderer);
|
||||
@@ -132,9 +131,7 @@ public:
|
||||
|
||||
#pragma region ITerminalApi
|
||||
// These methods are defined in TerminalApi.cpp
|
||||
void UnknownSequence() noexcept override;
|
||||
void ReturnResponse(const std::wstring_view response) override;
|
||||
bool IsConPTY() const noexcept override;
|
||||
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override;
|
||||
BufferState GetBufferAndViewport() noexcept override;
|
||||
void SetViewportPosition(const til::point position) noexcept override;
|
||||
|
||||
@@ -28,11 +28,6 @@ void Terminal::ReturnResponse(const std::wstring_view response)
|
||||
}
|
||||
}
|
||||
|
||||
bool Terminal::IsConPTY() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::Console::VirtualTerminal::StateMachine& Terminal::GetStateMachine() noexcept
|
||||
{
|
||||
return *_stateMachine;
|
||||
|
||||
@@ -313,7 +313,7 @@ std::pair<til::point, til::point> Terminal::_ExpandSelectionAnchors(std::pair<ti
|
||||
break;
|
||||
case SelectionExpansion::Word:
|
||||
{
|
||||
start = buffer.GetWordStart(start, _wordDelimiters, false);
|
||||
start = buffer.GetWordStart2(start, _wordDelimiters, false);
|
||||
|
||||
// GH#5099: We round to the nearest cell boundary,
|
||||
// so we would normally prematurely expand to the next word
|
||||
@@ -325,7 +325,7 @@ std::pair<til::point, til::point> Terminal::_ExpandSelectionAnchors(std::pair<ti
|
||||
{
|
||||
bufferSize.DecrementInExclusiveBounds(end);
|
||||
}
|
||||
end = buffer.GetWordEnd(end, _wordDelimiters, false);
|
||||
end = buffer.GetWordEnd2(end, _wordDelimiters, false);
|
||||
break;
|
||||
}
|
||||
case SelectionExpansion::Char:
|
||||
@@ -435,9 +435,9 @@ void Terminal::ExpandSelectionToWord()
|
||||
const auto& buffer = _activeBuffer();
|
||||
auto selection{ _selection.write() };
|
||||
wil::hide_name _selection;
|
||||
selection->start = buffer.GetWordStart(selection->start, _wordDelimiters, false);
|
||||
selection->start = buffer.GetWordStart2(selection->start, _wordDelimiters, false);
|
||||
selection->pivot = selection->start;
|
||||
selection->end = buffer.GetWordEnd(selection->end, _wordDelimiters, false);
|
||||
selection->end = buffer.GetWordEnd2(selection->end, _wordDelimiters, false);
|
||||
|
||||
// if we're targeting both endpoints, instead just target "end"
|
||||
if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) && WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
|
||||
@@ -826,13 +826,13 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Left:
|
||||
{
|
||||
auto nextPos = pos;
|
||||
nextPos = buffer.GetWordStart(nextPos, _wordDelimiters, true);
|
||||
nextPos = buffer.GetWordStart2(nextPos, _wordDelimiters, true);
|
||||
if (nextPos == pos)
|
||||
{
|
||||
// didn't move because we're already at the beginning of a word,
|
||||
// so move to the beginning of the previous word
|
||||
buffer.GetSize().DecrementInExclusiveBounds(nextPos);
|
||||
nextPos = buffer.GetWordStart(nextPos, _wordDelimiters, true);
|
||||
nextPos = buffer.GetWordStart2(nextPos, _wordDelimiters, true);
|
||||
}
|
||||
pos = nextPos;
|
||||
break;
|
||||
@@ -841,24 +841,24 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
{
|
||||
const auto mutableViewportEndExclusive = _GetMutableViewport().BottomInclusiveRightExclusive();
|
||||
auto nextPos = pos;
|
||||
nextPos = buffer.GetWordEnd(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
nextPos = buffer.GetWordEnd2(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
if (nextPos == pos)
|
||||
{
|
||||
// didn't move because we're already at the end of a word,
|
||||
// so move to the end of the next word
|
||||
buffer.GetSize().IncrementInExclusiveBounds(nextPos);
|
||||
nextPos = buffer.GetWordEnd(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
nextPos = buffer.GetWordEnd2(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
}
|
||||
pos = nextPos;
|
||||
break;
|
||||
}
|
||||
case SelectionDirection::Up:
|
||||
_MoveByChar(direction, pos);
|
||||
pos = buffer.GetWordStart(pos, _wordDelimiters, true);
|
||||
pos = buffer.GetWordStart2(pos, _wordDelimiters, true);
|
||||
break;
|
||||
case SelectionDirection::Down:
|
||||
_MoveByChar(direction, pos);
|
||||
pos = buffer.GetWordEnd(pos, _wordDelimiters, true);
|
||||
pos = buffer.GetWordEnd2(pos, _wordDelimiters, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,7 +353,6 @@ namespace winrt::Microsoft::Terminal::Settings
|
||||
_AllowVtChecksumReport = profile.AllowVtChecksumReport();
|
||||
_AllowVtClipboardWrite = profile.AllowVtClipboardWrite();
|
||||
_PathTranslationStyle = profile.PathTranslationStyle();
|
||||
_DragDropDelimiter = profile.DragDropDelimiter();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -43,8 +43,7 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Ambiguous Width -->
|
||||
<local:SettingContainer x:Name="AmbiguousWidth"
|
||||
x:Uid="Globals_AmbiguousWidth">
|
||||
<local:SettingContainer x:Uid="Globals_AmbiguousWidth">
|
||||
<ComboBox AutomationProperties.AccessibilityView="Content"
|
||||
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.AmbiguousWidthList}"
|
||||
|
||||
@@ -135,11 +135,14 @@
|
||||
<TextBlock x:Uid="Globals_WarningsHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
|
||||
<!-- Close All Tabs Warning -->
|
||||
<local:SettingContainer x:Name="ConfirmCloseAllTabs"
|
||||
x:Uid="Globals_ConfirmCloseAllTabs">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.ConfirmCloseAllTabs, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
<!-- Confirm Close On -->
|
||||
<local:SettingContainer x:Name="ConfirmCloseOn"
|
||||
x:Uid="Globals_ConfirmCloseOn">
|
||||
<ComboBox AutomationProperties.AccessibilityView="Content"
|
||||
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.ConfirmCloseOnList}"
|
||||
SelectedItem="{x:Bind ViewModel.CurrentConfirmCloseOn, Mode=TwoWay}"
|
||||
Style="{StaticResource ComboBoxSettingStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Input Service Warning -->
|
||||
|
||||
@@ -17,5 +17,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(TabSwitcherMode, TabSwitcherMode, TabSwitcherMode, L"Globals_TabSwitcherMode", L"Content");
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(CopyFormat, CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, L"Globals_CopyFormat", L"Content");
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(ConfirmCloseOn, ConfirmCloseOn, Model::ConfirmCloseOn, L"Globals_ConfirmCloseOn", L"Content");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
GETSET_BINDABLE_ENUM_SETTING(TabSwitcherMode, Model::TabSwitcherMode, _GlobalSettings.TabSwitcherMode);
|
||||
GETSET_BINDABLE_ENUM_SETTING(CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, _GlobalSettings.CopyFormatting);
|
||||
GETSET_BINDABLE_ENUM_SETTING(ConfirmCloseOn, Model::ConfirmCloseOn, _GlobalSettings.ConfirmCloseOn);
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, CopyOnSelect);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, TrimBlockSelection);
|
||||
@@ -30,7 +31,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, DetectURLs);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, SearchWebDefaultQueryUrl);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WordDelimiters);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ConfirmCloseAllTabs);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, InputServiceWarning);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutLargePaste);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutMultiLinePaste);
|
||||
|
||||
@@ -12,10 +12,13 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
InteractionViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings);
|
||||
|
||||
IInspectable CurrentTabSwitcherMode;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabSwitcherModeList { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> TabSwitcherModeList { get; };
|
||||
|
||||
IInspectable CurrentCopyFormat;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> CopyFormatList { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> CopyFormatList { get; };
|
||||
|
||||
IInspectable CurrentConfirmCloseOn;
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> ConfirmCloseOnList { get; };
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, CopyOnSelect);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, TrimBlockSelection);
|
||||
@@ -27,7 +30,6 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DetectURLs);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, SearchWebDefaultQueryUrl);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, WordDelimiters);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ConfirmCloseAllTabs);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, InputServiceWarning);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, WarnAboutLargePaste);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Microsoft.Terminal.Control.WarnAboutMultiLinePaste, WarnAboutMultiLinePaste);
|
||||
|
||||
@@ -44,28 +44,6 @@ using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// GH#19927 - Walk the visual tree of the given element to find any
|
||||
// Popups and set the theme on their Child element. In XAML Islands,
|
||||
// popup children render in the PopupRoot (separate from the content
|
||||
// tree), so they don't inherit our page's RequestedTheme
|
||||
static void _setThemeOnPopups(const winrt::Windows::UI::Xaml::DependencyObject& element,
|
||||
const winrt::Windows::UI::Xaml::ElementTheme theme)
|
||||
{
|
||||
const auto childCount = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);
|
||||
for (int32_t i = 0; i < childCount; i++)
|
||||
{
|
||||
const auto child = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i);
|
||||
if (const auto popup = child.try_as<winrt::Windows::UI::Xaml::Controls::Primitives::Popup>())
|
||||
{
|
||||
if (const auto popupChild = popup.Child().try_as<winrt::Windows::UI::Xaml::FrameworkElement>())
|
||||
{
|
||||
popupChild.RequestedTheme(theme);
|
||||
}
|
||||
}
|
||||
_setThemeOnPopups(child, theme);
|
||||
}
|
||||
}
|
||||
|
||||
static WUX::Controls::FontIcon _fontIconForNavTag(const std::wstring_view navTag)
|
||||
{
|
||||
WUX::Controls::FontIcon icon{};
|
||||
@@ -372,13 +350,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_Navigate(tag, BreadcrumbSubPage::None);
|
||||
}
|
||||
}
|
||||
|
||||
// GH#19927 - Theme the search box's internal popup now that the
|
||||
// visual tree is ready and control templates are applied
|
||||
const auto theme = _settingsSource.GlobalSettings().CurrentTheme();
|
||||
const auto hasThemeForSettings{ theme.Settings() != nullptr };
|
||||
const auto requestedTheme = hasThemeForSettings ? theme.Settings().RequestedTheme() : theme.RequestedTheme();
|
||||
_setThemeOnPopups(SettingsSearchBox(), requestedTheme);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -1127,17 +1098,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
const auto theme = _settingsSource.GlobalSettings().CurrentTheme();
|
||||
const auto hasThemeForSettings{ theme.Settings() != nullptr };
|
||||
const auto appTheme = theme.RequestedTheme();
|
||||
const auto requestedTheme = (hasThemeForSettings) ? theme.Settings().RequestedTheme() : appTheme;
|
||||
const auto& theme = _settingsSource.GlobalSettings().CurrentTheme();
|
||||
const bool hasThemeForSettings{ theme.Settings() != nullptr };
|
||||
const auto& appTheme = theme.RequestedTheme();
|
||||
const auto& requestedTheme = (hasThemeForSettings) ? theme.Settings().RequestedTheme() : appTheme;
|
||||
|
||||
RequestedTheme(requestedTheme);
|
||||
|
||||
// GH#19927 - Theme the search box's internal popup so its
|
||||
// dropdown matches the app theme instead of the system theme
|
||||
_setThemeOnPopups(SettingsSearchBox(), requestedTheme);
|
||||
|
||||
// Mica gets it's appearance from the app's theme, not necessarily the
|
||||
// Page's theme. In the case of dark app, light settings, mica will be a
|
||||
// dark color, and the text will also be dark, making the UI _very_ hard
|
||||
|
||||
@@ -329,8 +329,7 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Icon -->
|
||||
<local:SettingContainer x:Name="CurrentFolderIcon"
|
||||
x:Uid="NewTabMenu_CurrentFolderIcon"
|
||||
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderIcon"
|
||||
CurrentValueAccessibleName="{x:Bind ViewModel.CurrentFolderLocalizedIcon, Mode=OneWay}"
|
||||
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
|
||||
<local:SettingContainer.CurrentValue>
|
||||
@@ -498,8 +497,7 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Add Remaining Profiles -->
|
||||
<local:SettingContainer x:Name="AddRemainingProfiles"
|
||||
x:Uid="NewTabMenu_AddRemainingProfiles"
|
||||
<local:SettingContainer x:Uid="NewTabMenu_AddRemainingProfiles"
|
||||
FontIconGlyph=""
|
||||
Style="{StaticResource SettingContainerWithIcon}">
|
||||
<Button x:Name="AddRemainingProfilesButton"
|
||||
|
||||
@@ -145,7 +145,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, AnswerbackMessage);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, RainbowSuggestions);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, PathTranslationStyle);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, DragDropDelimiter);
|
||||
|
||||
WINRT_PROPERTY(bool, IsBaseLayer, false);
|
||||
WINRT_PROPERTY(bool, FocusDeleteButton, false);
|
||||
|
||||
@@ -139,7 +139,6 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, AnswerbackMessage);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RainbowSuggestions);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, DragDropDelimiter);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,16 +272,6 @@
|
||||
Style="{StaticResource ComboBoxSettingStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Drag Drop Delimiter -->
|
||||
<local:SettingContainer x:Name="DragDropDelimiter"
|
||||
x:Uid="Profile_DragDropDelimiter"
|
||||
ClearSettingValue="{x:Bind Profile.ClearDragDropDelimiter}"
|
||||
HasSettingValue="{x:Bind Profile.HasDragDropDelimiter, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind Profile.DragDropDelimiterOverrideSource, Mode=OneWay}">
|
||||
<TextBox Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind Profile.DragDropDelimiter, Mode=TwoWay}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -2197,6 +2197,26 @@
|
||||
<value>Warn when closing more than one tab</value>
|
||||
<comment>Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open.</comment>
|
||||
</data>
|
||||
<data name="Globals_ConfirmCloseOn.Header" xml:space="preserve">
|
||||
<value>Warn when closing</value>
|
||||
<comment>Header for a dropdown controlling when to show a confirmation dialog before closing.</comment>
|
||||
</data>
|
||||
<data name="Globals_ConfirmCloseOn.HelpText" xml:space="preserve">
|
||||
<value>Controls when a confirmation dialog appears before closing tabs or windows. "Always" presents the dialog when closing any pane.</value>
|
||||
<comment>Help text associated with Globals_ConfirmCloseOn. "Always" refers to Globals_ConfirmCloseOnAlways.Content.</comment>
|
||||
</data>
|
||||
<data name="Globals_ConfirmCloseOnNever.Content" xml:space="preserve">
|
||||
<value>Never</value>
|
||||
<comment>Option associated with Globals_ConfirmCloseOn. "Never" means that the system will never display a warning when closing.</comment>
|
||||
</data>
|
||||
<data name="Globals_ConfirmCloseOnAlways.Content" xml:space="preserve">
|
||||
<value>Always</value>
|
||||
<comment>Option associated with Globals_ConfirmCloseOn. "Always" means that the system will always display a warning when closing.</comment>
|
||||
</data>
|
||||
<data name="Globals_ConfirmCloseOnAutomatic.Content" xml:space="preserve">
|
||||
<value>Multiple tabs or panes</value>
|
||||
<comment>Option associated with Globals_ConfirmCloseOn. The system will display a warning when multiple tabs or panes are present.</comment>
|
||||
</data>
|
||||
<data name="Globals_InputServiceWarning.Header" xml:space="preserve">
|
||||
<value>Warn when "Touch Keyboard and Handwriting Panel Service" is disabled</value>
|
||||
</data>
|
||||
@@ -2748,12 +2768,4 @@
|
||||
<value>Type to filter icons</value>
|
||||
<comment>Placeholder text for a text box to filter and select an icon.</comment>
|
||||
</data>
|
||||
<data name="Profile_DragDropDelimiter.Header" xml:space="preserve">
|
||||
<value>Drag and drop delimiter</value>
|
||||
<comment>Header for a control to set the delimiter used when dragging multiple files into the terminal.</comment>
|
||||
</data>
|
||||
<data name="Profile_DragDropDelimiter.HelpText" xml:space="preserve">
|
||||
<value>This text will be inserted between the paths of multiple files dropped into the terminal.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
@@ -647,12 +647,9 @@ void CascadiaSettings::_validateMediaResources()
|
||||
|
||||
_globals->ResolveMediaResources(mediaResourceResolver);
|
||||
|
||||
if (Feature_WarnOnInvalidSettingsMediaResources::IsEnabled())
|
||||
if (_foundInvalidUserResources)
|
||||
{
|
||||
if (_foundInvalidUserResources)
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::InvalidMediaResource);
|
||||
}
|
||||
_warnings.Append(SettingsLoadWarnings::InvalidMediaResource);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::TextMeasurement, TextMeasurement);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::AmbiguousWidth, AmbiguousWidth);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::WarnAboutMultiLinePaste, WarnAboutMultiLinePaste);
|
||||
DEFINE_ENUM_MAP(Model::ConfirmCloseOn, ConfirmCloseOn);
|
||||
|
||||
// Profile Settings
|
||||
DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode);
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::TextMeasurement> TextMeasurement();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::AmbiguousWidth> AmbiguousWidth();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste> WarnAboutMultiLinePaste();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, ConfirmCloseOn> ConfirmCloseOn();
|
||||
|
||||
// Profile Settings
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, CloseOnExitMode> CloseOnExitMode();
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.TextMeasurement> TextMeasurement { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.AmbiguousWidth> AmbiguousWidth { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.WarnAboutMultiLinePaste> WarnAboutMultiLinePaste { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.ConfirmCloseOn> ConfirmCloseOn { get; };
|
||||
|
||||
// Profile Settings
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.CloseOnExitMode> CloseOnExitMode { get; };
|
||||
|
||||
@@ -160,7 +160,16 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyInputServiceWarningKey, _InputServiceWarning) || _fixupsAppliedDuringLoad;
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutLargePasteKey, _WarnAboutLargePaste) || _fixupsAppliedDuringLoad;
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste) || _fixupsAppliedDuringLoad;
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, _ConfirmCloseAllTabs) || _fixupsAppliedDuringLoad;
|
||||
// GH#6549 - Migrate legacy "confirmCloseAllTabs" boolean to the new
|
||||
// "confirmCloseOn" enum. true -> Automatic, false -> Never.
|
||||
{
|
||||
std::optional<bool> legacyConfirmClose;
|
||||
if (JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, legacyConfirmClose))
|
||||
{
|
||||
_ConfirmCloseOn = legacyConfirmClose.value() ? ConfirmCloseOn::Automatic : ConfirmCloseOn::Never;
|
||||
_fixupsAppliedDuringLoad = true;
|
||||
}
|
||||
}
|
||||
|
||||
#define GLOBAL_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::GetValueForKey(json, jsonKey, _##name); \
|
||||
|
||||
@@ -50,6 +50,13 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
AfterCurrentTab,
|
||||
};
|
||||
|
||||
enum ConfirmCloseOn
|
||||
{
|
||||
Never = 0,
|
||||
Automatic = 1,
|
||||
Always = 2,
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass GlobalAppSettings {
|
||||
Guid DefaultProfile;
|
||||
|
||||
@@ -61,7 +68,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
INHERITABLE_SETTING(Boolean, ShowTabsFullscreen);
|
||||
INHERITABLE_SETTING(NewTabPosition, NewTabPosition);
|
||||
INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar);
|
||||
INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs);
|
||||
INHERITABLE_SETTING(ConfirmCloseOn, ConfirmCloseOn);
|
||||
INHERITABLE_SETTING(String, Language);
|
||||
INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode);
|
||||
INHERITABLE_SETTING(Boolean, UseAcrylicInTabRow);
|
||||
|
||||
@@ -38,7 +38,7 @@ Author(s):
|
||||
X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \
|
||||
X(Model::NewTabPosition, NewTabPosition, "newTabPosition", Model::NewTabPosition::AfterLastTab) \
|
||||
X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \
|
||||
X(bool, ConfirmCloseAllTabs, "warning.confirmCloseAllTabs", true) \
|
||||
X(Model::ConfirmCloseOn, ConfirmCloseOn, "warning.confirmCloseOn", Model::ConfirmCloseOn::Automatic) \
|
||||
X(Model::ThemePair, Theme, "theme") \
|
||||
X(hstring, Language, "language") \
|
||||
X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \
|
||||
@@ -108,7 +108,6 @@ Author(s):
|
||||
X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \
|
||||
X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \
|
||||
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \
|
||||
X(hstring, DragDropDelimiter, "dragDropDelimiter", L" ") \
|
||||
X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None)
|
||||
|
||||
// Intentionally omitted Profile settings:
|
||||
|
||||
@@ -94,6 +94,5 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
|
||||
|
||||
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
|
||||
INHERITABLE_PROFILE_SETTING(String, DragDropDelimiter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,25 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Core::MatchMode)
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::ConfirmCloseOn)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "never", ValueType::Never },
|
||||
pair_type{ "always", ValueType::Always },
|
||||
pair_type{ "automatic", ValueType::Automatic },
|
||||
};
|
||||
|
||||
auto FromJson(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::FromJson(json);
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json);
|
||||
}
|
||||
};
|
||||
|
||||
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle)
|
||||
{
|
||||
static constexpr std::array<pair_type, 6> mappings = {
|
||||
|
||||
@@ -8,16 +8,6 @@
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
static bool _IsPwshAvailable()
|
||||
{
|
||||
// Try to detect if `pwsh.exe` is available in the PATH, if so we want to use that
|
||||
// Allow some extra space in case user put it somewhere odd
|
||||
// We do need to allocate space for the full path even if we don't want to paste the whole thing in
|
||||
wchar_t pwshPath[MAX_PATH] = { 0 };
|
||||
const auto pwshExeName = L"pwsh.exe";
|
||||
return SearchPathW(nullptr, pwshExeName, nullptr, MAX_PATH, pwshPath, nullptr);
|
||||
}
|
||||
|
||||
void VsDevShellGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
||||
{
|
||||
try
|
||||
@@ -31,10 +21,9 @@ void VsDevShellGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupIn
|
||||
const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, std::as_bytes(std::span{ seed })) };
|
||||
auto profile = winrt::make_self<implementation::Profile>(profileGuid);
|
||||
profile->Name(winrt::hstring{ GetProfileName(instance) });
|
||||
auto isPwsh = _IsPwshAvailable();
|
||||
profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance, isPwsh) });
|
||||
profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance) });
|
||||
profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() });
|
||||
profile->Icon(winrt::hstring{ GetProfileIconPath(isPwsh) });
|
||||
profile->Icon(winrt::hstring{ GetProfileIconPath() });
|
||||
profile->Hidden(hidden);
|
||||
profiles.emplace_back(std::move(profile));
|
||||
}
|
||||
@@ -48,15 +37,20 @@ std::wstring VsDevShellGenerator::GetProfileName(const VsSetupConfiguration::VsS
|
||||
return name;
|
||||
}
|
||||
|
||||
std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance, bool isPwsh) const
|
||||
std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const
|
||||
{
|
||||
// Build this in stages, so reserve space now
|
||||
std::wstring commandLine;
|
||||
commandLine.reserve(256);
|
||||
|
||||
if (isPwsh)
|
||||
// Try to detect if `pwsh.exe` is available in the PATH, if so we want to use that
|
||||
// Allow some extra space in case user put it somewhere odd
|
||||
// We do need to allocate space for the full path even if we don't want to paste the whole thing in
|
||||
wchar_t pwshPath[MAX_PATH] = { 0 };
|
||||
const auto pwshExeName = L"pwsh.exe";
|
||||
if (SearchPathW(nullptr, pwshExeName, nullptr, MAX_PATH, pwshPath, nullptr))
|
||||
{
|
||||
commandLine.append(L"pwsh.exe");
|
||||
commandLine.append(pwshExeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -37,14 +37,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
return L"VsDevShell" + instance.GetInstanceId();
|
||||
}
|
||||
|
||||
std::wstring GetProfileIconPath(bool isPwsh) const
|
||||
std::wstring GetProfileIconPath() const
|
||||
{
|
||||
return isPwsh ? L"ms-appx:///ProfileIcons/vs-pwsh.png" :
|
||||
L"ms-appx:///ProfileIcons/vs-powershell.png";
|
||||
return L"ms-appx:///ProfileIcons/vs-powershell.png";
|
||||
}
|
||||
|
||||
std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const;
|
||||
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance, bool isPwsh) const;
|
||||
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const;
|
||||
std::wstring GetDevShellModulePath(const VsSetupConfiguration::VsSetupInstance& instance) const;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"showAdminShield": true,
|
||||
|
||||
// Miscellaneous
|
||||
"confirmCloseAllTabs": true,
|
||||
"warning.confirmCloseOn": "automatic",
|
||||
"theme": "dark",
|
||||
"snapToGridOnResize": true,
|
||||
"disableAnimations": false,
|
||||
|
||||
@@ -313,13 +313,10 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 0, 0 };
|
||||
const auto cursorPosition0 = terminalPosition0 * fontSize;
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -332,28 +329,29 @@ namespace ControlUnitTests
|
||||
// move not quite a whole cell, but enough to start a selection
|
||||
const til::point terminalPosition1{ 0, 0 };
|
||||
const til::point cursorPosition1{ 6, 0 };
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
Log::Comment(L"Drag the mouse down a whole row");
|
||||
const til::point terminalPosition2{ 1, 1 };
|
||||
const auto cursorPosition2 = terminalPosition2 * fontSize;
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition2.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition2.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's now two selections (one on each row)");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
Log::Comment(L"Release the mouse");
|
||||
interactivity->PointerReleased(0,
|
||||
noMouseDown,
|
||||
interactivity->PointerReleased(noMouseDown,
|
||||
WM_LBUTTONUP, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition2.to_core_point());
|
||||
@@ -363,8 +361,7 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"click outside the current selection");
|
||||
const til::point terminalPosition3{ 2, 2 };
|
||||
const auto cursorPosition3 = terminalPosition3 * fontSize;
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -375,11 +372,12 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Drag the mouse");
|
||||
const til::point terminalPosition4{ 3, 2 };
|
||||
const auto cursorPosition4 = terminalPosition4 * fontSize;
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition4.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition4.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's now one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
}
|
||||
@@ -410,17 +408,10 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
// This test is sensitive to the number of rows scrolled per scroll wheel,
|
||||
// which is reloaded from the system parameters when focus is received.
|
||||
// Reset it.
|
||||
interactivity->_rowsToScroll = 1;
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -435,11 +426,12 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Drag the mouse just a little");
|
||||
// move not quite a whole cell, but enough to start a selection
|
||||
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -567,12 +559,9 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point cursorPosition0{ 6, 0 };
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -586,11 +575,12 @@ namespace ControlUnitTests
|
||||
|
||||
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
|
||||
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -612,13 +602,10 @@ namespace ControlUnitTests
|
||||
const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
|
||||
const Control::MouseButtonState noMouseDown{};
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point cursorPosition0{ 6, 0 };
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -632,11 +619,12 @@ namespace ControlUnitTests
|
||||
|
||||
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
|
||||
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -646,8 +634,7 @@ namespace ControlUnitTests
|
||||
til::point expectedEnd{ 3, 0 }; // add 1 to x-coordinate because end is exclusive
|
||||
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
|
||||
|
||||
interactivity->PointerReleased(0,
|
||||
noMouseDown,
|
||||
interactivity->PointerReleased(noMouseDown,
|
||||
WM_LBUTTONUP,
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
@@ -657,11 +644,12 @@ namespace ControlUnitTests
|
||||
|
||||
Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
|
||||
const til::point cursorPosition2{ fontSize.width * 10, 0 };
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition2.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition2.to_core_point(),
|
||||
false);
|
||||
|
||||
Log::Comment(L"The selection should be unchanged.");
|
||||
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
|
||||
@@ -688,12 +676,6 @@ namespace ControlUnitTests
|
||||
auto expectedViewHeight = 20;
|
||||
auto expectedBufferHeight = 20;
|
||||
|
||||
interactivity->GotFocus();
|
||||
// This test is sensitive to the number of rows scrolled per scroll wheel,
|
||||
// which is reloaded from the system parameters when focus is received.
|
||||
// Reset it.
|
||||
interactivity->_rowsToScroll = 1;
|
||||
|
||||
auto scrollChangedHandler = [&](auto&&, const Control::ScrollPositionChangedArgs& args) mutable {
|
||||
VERIFY_ARE_EQUAL(expectedTop, args.ViewTop());
|
||||
VERIFY_ARE_EQUAL(expectedViewHeight, args.ViewHeight());
|
||||
@@ -740,8 +722,7 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 4, 4 };
|
||||
const auto cursorPosition0 = terminalPosition0 * fontSize;
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -754,11 +735,12 @@ namespace ControlUnitTests
|
||||
// move the mouse as if to make a selection
|
||||
const til::point terminalPosition1{ 10, 4 };
|
||||
const auto cursorPosition1 = terminalPosition1 * fontSize;
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's still no selection");
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
}
|
||||
@@ -788,13 +770,10 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -809,11 +788,12 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Drag the mouse just a little");
|
||||
// move not quite a whole cell, but enough to start a selection
|
||||
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -870,11 +850,12 @@ namespace ControlUnitTests
|
||||
// character in the buffer (if, albeit in a new location).
|
||||
//
|
||||
// This helps test GH #14462, a regression from #10749.
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition0.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition0.to_core_point(),
|
||||
true);
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto anchor{ core->_terminal->GetSelectionAnchor() };
|
||||
@@ -895,11 +876,12 @@ namespace ControlUnitTests
|
||||
expectedAnchor.y -= 1;
|
||||
expectedEnd.y -= 1;
|
||||
VERIFY_ARE_EQUAL(scrollbackLength - 3, core->_terminal->GetScrollOffset());
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto anchor{ core->_terminal->GetSelectionAnchor() };
|
||||
@@ -947,8 +929,7 @@ namespace ControlUnitTests
|
||||
const til::size fontSize{ 9, 21 };
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -997,8 +978,7 @@ namespace ControlUnitTests
|
||||
const til::size fontSize{ 9, 21 };
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -1014,8 +994,7 @@ namespace ControlUnitTests
|
||||
// The viewport is only 30 wide, so clamping 35 to the buffer size gets
|
||||
// us 29, which converted is (32 + 29 + 1) = 62 = '>'
|
||||
expectedOutput.push_back(L"\x1b[M >&");
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -1030,8 +1009,7 @@ namespace ControlUnitTests
|
||||
// will be clamped to the top line.
|
||||
|
||||
expectedOutput.push_back(L"\x1b[M &!"); // 5, 1
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -1047,8 +1025,7 @@ namespace ControlUnitTests
|
||||
VERIFY_ARE_EQUAL(0, core->ScrollOffset());
|
||||
Log::Comment(L" --- Click on a spot that's still outside the buffer ---");
|
||||
expectedOutput.push_back(L"\x1b[M >&");
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -1062,8 +1039,7 @@ namespace ControlUnitTests
|
||||
Log::Comment(L" --- Click on a spot that's NOW INSIDE the buffer ---");
|
||||
// (32 + 35 + 1) = 68 = 'D'
|
||||
expectedOutput.push_back(L"\x1b[M D&");
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
|
||||
@@ -176,9 +176,9 @@ namespace SettingsModelUnitTests
|
||||
auto hashFromKey = [&](auto& kc) {
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().template as<NewTabArgs>();
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs.ContentArgs());
|
||||
const auto terminalArgs{ realArgs.ContentArgs().template try_as<NewTerminalArgs>() };
|
||||
const auto terminalArgs{ realArgs.ContentArgs().try_as<NewTerminalArgs>() };
|
||||
return terminalArgs.Hash();
|
||||
};
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace SettingsModelUnitTests
|
||||
|
||||
"trimPaste": true,
|
||||
|
||||
"warning.confirmCloseAllTabs" : true,
|
||||
"warning.confirmCloseOn": "automatic",
|
||||
"warning.inputService" : true,
|
||||
"warning.largePaste" : true,
|
||||
"warning.multiLinePaste" : "automatic",
|
||||
|
||||
@@ -315,29 +315,16 @@ AppHost* WindowEmperor::_mostRecentWindow() const noexcept
|
||||
|
||||
void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
|
||||
{
|
||||
// When running without package identity, set an explicit AppUserModelID so
|
||||
// that toast notifications (and other shell features like taskbar grouping)
|
||||
// work correctly. We include...
|
||||
// - a hash of the executable path
|
||||
// - a hash of the user SID
|
||||
// This prevents crosstalk between different portable/unpackaged installations.
|
||||
// The same isolation strategy is used for the single-instance mutex below.
|
||||
std::wstring unpackagedAumid;
|
||||
|
||||
std::wstring windowClassName;
|
||||
windowClassName.reserve(64); // "Windows Terminal Preview Admin 0123456789012345 0123456789012345"
|
||||
#if defined(WT_BRANDING_RELEASE)
|
||||
windowClassName.append(L"Windows Terminal");
|
||||
unpackagedAumid = L"Microsoft.WindowsTerminal";
|
||||
#elif defined(WT_BRANDING_PREVIEW)
|
||||
windowClassName.append(L"Windows Terminal Preview");
|
||||
unpackagedAumid = L"Microsoft.WindowsTerminalPreview";
|
||||
#elif defined(WT_BRANDING_CANARY)
|
||||
windowClassName.append(L"Windows Terminal Canary");
|
||||
unpackagedAumid = L"Microsoft.WindowsTerminalCanary";
|
||||
#else
|
||||
windowClassName.append(L"Windows Terminal Dev");
|
||||
unpackagedAumid = L"WindowsTerminalDev";
|
||||
#endif
|
||||
if (Utils::IsRunningElevated())
|
||||
{
|
||||
@@ -349,10 +336,8 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
|
||||
const auto hash = til::hash(path);
|
||||
#ifdef _WIN64
|
||||
fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:016x}"), hash);
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:016x}"), hash);
|
||||
#else
|
||||
fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:08x}"), hash);
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:08x}"), hash);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -366,15 +351,6 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
|
||||
#else
|
||||
fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:08x}"), hash);
|
||||
#endif
|
||||
if (!IsPackaged())
|
||||
{
|
||||
#ifdef _WIN64
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:016x}"), hash);
|
||||
#else
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:08x}"), hash);
|
||||
#endif
|
||||
LOG_IF_FAILED(SetCurrentProcessExplicitAppUserModelID(unpackagedAumid.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Windows Terminal is a single-instance application. Either acquire ownership
|
||||
|
||||
@@ -87,5 +87,4 @@
|
||||
X(bool, ShowMarks, false) \
|
||||
X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \
|
||||
X(bool, RightClickContextMenu, false) \
|
||||
X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None) \
|
||||
X(winrt::hstring, DragDropDelimiter, L" ")
|
||||
X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None)
|
||||
|
||||
@@ -324,11 +324,9 @@ std::vector<wil::com_ptr<T>> SafeArrayToOwningVector(SAFEARRAY* safeArray)
|
||||
std::vector<wil::com_ptr<T>> result{ gsl::narrow<std::size_t>(count) };
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
result[i] = pVals[i];
|
||||
result[i].attach(pVals[i]);
|
||||
}
|
||||
|
||||
THROW_IF_FAILED(SafeArrayUnaccessData(safeArray));
|
||||
THROW_IF_FAILED(SafeArrayDestroy(safeArray));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion Condition="'$(WindowsTargetPlatformMinVersion)' == ''">10.0.18362.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -95,8 +95,7 @@
|
||||
|
||||
<!-- For ALL build types-->
|
||||
<PropertyGroup Label="Configuration">
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' >= '18.0'">v145</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(PlatformToolset)' == ''">v143</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
@@ -125,8 +124,6 @@
|
||||
Conhost code converts DWORDs to HANDLEs for instance.
|
||||
C4467: usage of ATL attributes is deprecated
|
||||
Conhost code still uses ATL.
|
||||
C4875: a non-string literal argument to [[gsl::suppress]] is deprecated ...
|
||||
This diagnostic will be fixed in a newer version of GSL.
|
||||
C26445: Do not assign std::span or std::string_view to a reference. They are cheap to construct and are not owners of the underlying data. (gsl.view).
|
||||
Even for MSVC v19.32 this is actually far from true. Copying (as opposed to referencing) larger
|
||||
than register-sized structures is fairly expensive. Example: https://godbolt.org/z/oPco88PaP
|
||||
@@ -139,7 +136,7 @@
|
||||
C26494: Variable 'index' is uninitialized. Always initialize an object (type. 5).
|
||||
This diagnostic is broken in VS 17.7 which our CI currently uses. It's fixed in 17.8.
|
||||
-->
|
||||
<DisableSpecificWarnings>4201;4312;4467;4875;5105;26434;26445;26456;26478;26494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4201;4312;4467;5105;26434;26445;26456;26478;26494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<PreprocessorDefinitions>_WINDOWS;EXTERNAL_BUILD;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
||||
@@ -201,6 +198,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SetChecksum>false</SetChecksum>
|
||||
<GenerateDebugInformation>DebugFastLink</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
||||
@@ -187,11 +187,4 @@
|
||||
<alwaysDisabledReleaseTokens/>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_WarnOnInvalidSettingsMediaResources</name>
|
||||
<description>Controls whether Terminal should display a warning dialog when icon, backgroundImage, shader, etc. could not be found.</description>
|
||||
<stage>AlwaysEnabled</stage>
|
||||
<alwaysDisabledReleaseTokens/>
|
||||
</feature>
|
||||
|
||||
</featureStaging>
|
||||
|
||||
@@ -400,16 +400,20 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
|
||||
// See FillConsoleOutputCharacterWImpl and its identical code.
|
||||
if (enablePowershellShim)
|
||||
{
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = attribute == (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
|
||||
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
if (const auto writer = gci.GetVtWriterForBuffer(&OutContext))
|
||||
{
|
||||
// PowerShell has previously called FillConsoleOutputCharacterW() which triggered a call to WriteClearScreen().
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = attribute == (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
|
||||
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
{
|
||||
// PowerShell has previously called FillConsoleOutputCharacterW() which triggered a call to WriteClearScreen().
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,23 +457,21 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
|
||||
// their entire buffer will be cleared as well.
|
||||
if (enablePowershellShim)
|
||||
{
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = character == UNICODE_SPACE;
|
||||
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
if (auto writer = gci.GetVtWriterForBuffer(&OutContext))
|
||||
{
|
||||
WriteClearScreen(OutContext);
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = character == UNICODE_SPACE;
|
||||
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
if (auto writer = gci.GetVtWriterForBuffer(&OutContext))
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
{
|
||||
WriteClearScreen(OutContext);
|
||||
writer.Submit();
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -670,7 +670,7 @@ void FileTests::TestReadFileBasicEmpty()
|
||||
const auto hIn = GetStdInputHandle();
|
||||
VERIFY_IS_NOT_NULL(hIn, L"Verify we have the standard input handle.");
|
||||
|
||||
DWORD dwMode = ENABLE_PROCESSED_INPUT; // ^Z is only handled when processed input is enabled.
|
||||
DWORD dwMode = 0;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hIn, dwMode), L"Set input mode for test.");
|
||||
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(hIn), L"Flush input buffer in preparation for test.");
|
||||
|
||||
@@ -40,12 +40,6 @@
|
||||
"Execution": {
|
||||
"AdditionalParameter": "/select:\"@IsPerfTest=true\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "NoAppPlatform",
|
||||
"Execution": {
|
||||
"AdditionalParameter": "/select:\"not(@Name='PolicyTests::*')\""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -982,20 +982,23 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
|
||||
// A null character will get translated to whitespace.
|
||||
fillCharacter = Microsoft::Console::VirtualTerminal::VtIo::SanitizeUCS2(fillCharacter);
|
||||
|
||||
// GH#3126 - This is a shim for cmd's `cls` function. In the
|
||||
// legacy console, `cls` is supposed to clear the entire buffer.
|
||||
// We always use a VT sequence, even if ConPTY isn't used, because those are faster nowadays.
|
||||
if (enableCmdShim &&
|
||||
source.left <= 0 && source.top <= 0 &&
|
||||
source.right >= bufferSize.RightInclusive() && source.bottom >= bufferSize.BottomInclusive() &&
|
||||
target.x == 0 && target.y <= -bufferSize.BottomExclusive() &&
|
||||
!clip &&
|
||||
fillCharacter == UNICODE_SPACE && fillAttribute == buffer.GetAttributes().GetLegacyAttributes())
|
||||
{
|
||||
WriteClearScreen(context);
|
||||
}
|
||||
else if (writer)
|
||||
if (writer)
|
||||
{
|
||||
// GH#3126 - This is a shim for cmd's `cls` function. In the
|
||||
// legacy console, `cls` is supposed to clear the entire buffer.
|
||||
// We always use a VT sequence, even if ConPTY isn't used, because those are faster nowadays.
|
||||
if (enableCmdShim &&
|
||||
source.left <= 0 && source.top <= 0 &&
|
||||
source.right >= bufferSize.RightInclusive() && source.bottom >= bufferSize.BottomInclusive() &&
|
||||
target.x == 0 && target.y <= -bufferSize.BottomExclusive() &&
|
||||
!clip &&
|
||||
fillCharacter == UNICODE_SPACE && fillAttribute == buffer.GetAttributes().GetLegacyAttributes())
|
||||
{
|
||||
WriteClearScreen(context);
|
||||
writer.Submit();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
const auto clipViewport = clip ? Viewport::FromInclusive(*clip).Clamp(bufferSize) : bufferSize;
|
||||
const auto sourceViewport = Viewport::FromInclusive(source);
|
||||
const auto fillViewport = sourceViewport.Clamp(clipViewport);
|
||||
@@ -1081,6 +1084,8 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
|
||||
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, fill, w, fillViewport, writtenViewport));
|
||||
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, backup, w, Viewport::FromDimensions(target, readViewport.Dimensions()).Clamp(clipViewport), writtenViewport));
|
||||
}
|
||||
|
||||
writer.Submit();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1088,11 +1093,6 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
|
||||
ScrollRegion(buffer, source, clip, target, fillCharacter, useThisAttr);
|
||||
}
|
||||
|
||||
if (writer)
|
||||
{
|
||||
writer.Submit();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
@@ -24,22 +24,6 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) :
|
||||
{
|
||||
}
|
||||
|
||||
void ConhostInternalGetSet::UnknownSequence() noexcept
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
|
||||
// VT sequences unknown to us may cause the cursor position to change in a way that
|
||||
// we don't know about. In this case, we need to mark the cursor position as "dirty".
|
||||
//
|
||||
// The worst offender is likely PowerShell. It uses VT sequences but also calls
|
||||
// GetConsoleScreenBufferInfoEx for *every single line of output* (!!!). This prevents
|
||||
// us from using a more conservative solution (e.g. always fetching the cursor position).
|
||||
if (gci.IsInVtIoMode())
|
||||
{
|
||||
gci.GetActiveOutputBuffer().SetConptyCursorPositionMayBeWrong();
|
||||
}
|
||||
}
|
||||
|
||||
// - Sends a string response to the input stream of the console.
|
||||
// - Used by various commands where the program attached would like a reply to one of the commands issued.
|
||||
// - This will generate two "key presses" (one down, one up) for every character in the string and place them into the head of the console's input stream.
|
||||
@@ -64,12 +48,6 @@ void ConhostInternalGetSet::ReturnResponse(const std::wstring_view response)
|
||||
_io.GetActiveInputBuffer()->WriteString(response);
|
||||
}
|
||||
|
||||
bool ConhostInternalGetSet::IsConPTY() const noexcept
|
||||
{
|
||||
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
return gci.IsInVtIoMode();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves the state machine for the active output buffer.
|
||||
// Arguments:
|
||||
|
||||
@@ -29,10 +29,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
|
||||
public:
|
||||
ConhostInternalGetSet(_In_ Microsoft::Console::IIoProvider& io);
|
||||
|
||||
void UnknownSequence() noexcept override;
|
||||
void ReturnResponse(const std::wstring_view response) override;
|
||||
|
||||
bool IsConPTY() const noexcept override;
|
||||
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override;
|
||||
BufferState GetBufferAndViewport() override;
|
||||
void SetViewportPosition(const til::point position) override;
|
||||
|
||||
@@ -1313,15 +1313,6 @@ COOKED_READ_DATA::LayoutResult COOKED_READ_DATA::_layoutLine(std::wstring& outpu
|
||||
til::CoordType cols = 0;
|
||||
const auto len = textBuffer.FitTextIntoColumns(text, columnLimit - column, cols);
|
||||
|
||||
// GH#19922: We need to account for terminals that are just 1 column wide, as we may deadlock otherwise.
|
||||
// `columnLimit - column == 1` will then prevent `FitTextIntoColumns` from fitting any wide glyphs.
|
||||
// We can detect this by checking for `len == 0`, skip the offending glyph and break out of the deadlock.
|
||||
if (len == 0) [[unlikely]]
|
||||
{
|
||||
it += textBuffer.GraphemeNext(text, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
output.append(text, 0, len);
|
||||
column += cols;
|
||||
it += len;
|
||||
|
||||
@@ -859,15 +859,14 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand,
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (ConsoleConnectionDeservesVisibleWindow(p))
|
||||
// Allow the renderer to paint once the rest of the console is hooked up.
|
||||
if (g.pRender)
|
||||
{
|
||||
// Allow the renderer to paint once the rest of the console is hooked up,
|
||||
// but only if there's actually something to paint on.
|
||||
if (g.pRender)
|
||||
{
|
||||
g.pRender->EnablePainting();
|
||||
}
|
||||
g.pRender->EnablePainting();
|
||||
}
|
||||
|
||||
if (SUCCEEDED_NTSTATUS(Status) && ConsoleConnectionDeservesVisibleWindow(p))
|
||||
{
|
||||
HANDLE Thread = nullptr;
|
||||
|
||||
IConsoleInputThread* pNewThread = nullptr;
|
||||
|
||||
@@ -18,17 +18,10 @@ class SearchTests
|
||||
{
|
||||
TEST_CLASS(SearchTests);
|
||||
|
||||
CommonState* m_state{ nullptr };
|
||||
CommonState* m_state;
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
wil::unique_hmodule icu{ LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) };
|
||||
if (!icu)
|
||||
{
|
||||
Log::Result(TestResults::Skipped, L"ICU is not present");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_state = new CommonState();
|
||||
m_state->PrepareGlobalScreenBuffer();
|
||||
return true;
|
||||
@@ -36,11 +29,8 @@ class SearchTests
|
||||
|
||||
TEST_CLASS_CLEANUP(ClassCleanup)
|
||||
{
|
||||
if (m_state)
|
||||
{
|
||||
m_state->CleanupGlobalScreenBuffer();
|
||||
delete m_state;
|
||||
}
|
||||
m_state->CleanupGlobalScreenBuffer();
|
||||
delete m_state;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ class TextBufferTests
|
||||
|
||||
void WriteLinesToBuffer(const std::vector<std::wstring>& text, TextBuffer& buffer);
|
||||
TEST_METHOD(GetWordBoundaries);
|
||||
|
||||
TEST_METHOD(MoveByWord);
|
||||
TEST_METHOD(GetGlyphBoundaries);
|
||||
|
||||
TEST_METHOD(GetTextRects);
|
||||
@@ -2039,12 +2039,12 @@ void TextBufferTests::GetWordBoundaries()
|
||||
|
||||
// Test Data:
|
||||
// - til::point - starting position
|
||||
// - til::point - expected result (includeWhitespace = false)
|
||||
// - til::point - expected result (includeWhitespace = true)
|
||||
// - til::point - expected result (accessibilityMode = false)
|
||||
// - til::point - expected result (accessibilityMode = true)
|
||||
struct ExpectedResult
|
||||
{
|
||||
til::point selectionMode;
|
||||
til::point accessibilityMode;
|
||||
til::point accessibilityModeDisabled;
|
||||
til::point accessibilityModeEnabled;
|
||||
};
|
||||
|
||||
struct Test
|
||||
@@ -2056,8 +2056,7 @@ void TextBufferTests::GetWordBoundaries()
|
||||
// Set testData for GetWordStart tests
|
||||
// clang-format off
|
||||
std::vector<Test> testData = {
|
||||
// tests for first line of text ("word other" + spaces)
|
||||
// selectionMode accessibilityMode
|
||||
// tests for first line of text
|
||||
{ { 0, 0 }, {{ 0, 0 }, { 0, 0 }} },
|
||||
{ { 1, 0 }, {{ 0, 0 }, { 0, 0 }} },
|
||||
{ { 3, 0 }, {{ 0, 0 }, { 0, 0 }} },
|
||||
@@ -2067,7 +2066,7 @@ void TextBufferTests::GetWordBoundaries()
|
||||
{ { 20, 0 }, {{ 10, 0 }, { 5, 0 }} },
|
||||
{ { 79, 0 }, {{ 10, 0 }, { 5, 0 }} },
|
||||
|
||||
// tests for second line of text (" more words" + spaces)
|
||||
// tests for second line of text
|
||||
{ { 0, 1 }, {{ 0, 1 }, { 5, 0 }} },
|
||||
{ { 1, 1 }, {{ 0, 1 }, { 5, 0 }} },
|
||||
{ { 2, 1 }, {{ 2, 1 }, { 2, 1 }} },
|
||||
@@ -2083,56 +2082,54 @@ void TextBufferTests::GetWordBoundaries()
|
||||
// clang-format on
|
||||
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:includeWhitespace", L"{false, true}")
|
||||
TEST_METHOD_PROPERTY(L"Data:accessibilityMode", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES();
|
||||
|
||||
bool includeWhitespace;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"includeWhitespace", includeWhitespace), L"Get includeWhitespace variant");
|
||||
bool accessibilityMode;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"accessibilityMode", accessibilityMode), L"Get accessibility mode variant");
|
||||
|
||||
const std::wstring_view delimiters = L" ";
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
|
||||
// Update testData for GetWordEnd tests
|
||||
// Note: GetWordEnd returns exclusive end positions
|
||||
// clang-format off
|
||||
testData = {
|
||||
// tests for first line of text ("word other" + spaces)
|
||||
// selectionMode accessibilityMode
|
||||
{ { 0, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 3, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 5, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 10, 0 }, { 2, 1 } } },
|
||||
{ { 6, 0 }, { { 10, 0 }, { 2, 1 } } },
|
||||
{ { 20, 0 }, { { 80, 0 }, { 2, 1 } } },
|
||||
{ { 79, 0 }, { { 80, 0 }, { 2, 1 } } },
|
||||
// tests for first line of text
|
||||
{ { 0, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 3, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 9, 0 }, { 2, 1 } } },
|
||||
{ { 6, 0 }, { { 9, 0 }, { 2, 1 } } },
|
||||
{ { 20, 0 }, { { 79, 0 }, { 2, 1 } } },
|
||||
{ { 79, 0 }, { { 79, 0 }, { 2, 1 } } },
|
||||
|
||||
// tests for second line of text (" more words" + spaces)
|
||||
{ { 0, 1 }, { { 2, 1 }, { 2, 1 } } },
|
||||
{ { 1, 1 }, { { 2, 1 }, { 2, 1 } } },
|
||||
{ { 2, 1 }, { { 6, 1 }, { 9, 1 } } },
|
||||
{ { 3, 1 }, { { 6, 1 }, { 9, 1 } } },
|
||||
{ { 5, 1 }, { { 6, 1 }, { 9, 1 } } },
|
||||
{ { 6, 1 }, { { 9, 1 }, { 9, 1 } } },
|
||||
{ { 7, 1 }, { { 9, 1 }, { 9, 1 } } },
|
||||
{ { 9, 1 }, { { 14, 1 }, { 80, 9000 } } },
|
||||
{ { 10, 1 }, { { 14, 1 }, { 80, 9000 } } },
|
||||
{ { 20, 1 }, { { 80, 1 }, { 80, 9000 } } },
|
||||
{ { 79, 1 }, { { 80, 1 }, { 80, 9000 } } },
|
||||
// tests for second line of text
|
||||
{ { 0, 1 }, { { 1, 1 }, { 2, 1 } } },
|
||||
{ { 1, 1 }, { { 1, 1 }, { 2, 1 } } },
|
||||
{ { 2, 1 }, { { 5, 1 }, { 9, 1 } } },
|
||||
{ { 3, 1 }, { { 5, 1 }, { 9, 1 } } },
|
||||
{ { 5, 1 }, { { 5, 1 }, { 9, 1 } } },
|
||||
{ { 6, 1 }, { { 8, 1 }, { 9, 1 } } },
|
||||
{ { 7, 1 }, { { 8, 1 }, { 9, 1 } } },
|
||||
{ { 9, 1 }, { { 13, 1 }, { 0, 9001 } } },
|
||||
{ { 10, 1 }, { { 13, 1 }, { 0, 9001 } } },
|
||||
{ { 20, 1 }, { { 79, 1 }, { 0, 9001 } } },
|
||||
{ { 79, 1 }, { { 79, 1 }, { 0, 9001 } } },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
|
||||
@@ -2164,7 +2161,6 @@ void TextBufferTests::GetWordBoundaries()
|
||||
|
||||
// clang-format off
|
||||
testData = {
|
||||
// selectionMode accessibilityMode
|
||||
{ { 0, 0 }, { { 0, 0 }, { 0, 0 } } },
|
||||
{ { 1, 0 }, { { 0, 0 }, { 0, 0 } } },
|
||||
{ { 4, 0 }, { { 4, 0 }, { 0, 0 } } },
|
||||
@@ -2177,11 +2173,11 @@ void TextBufferTests::GetWordBoundaries()
|
||||
|
||||
{ { 0, 2 }, { { 0, 2 }, { 0, 2 } } },
|
||||
{ { 9, 2 }, { { 0, 2 }, { 0, 2 } } },
|
||||
|
||||
// v accessibility does not consider wrapping
|
||||
{ { 0, 3 }, { { 0, 3 }, { 0, 2 } } },
|
||||
{ { 7, 3 }, { { 6, 3 }, { 0, 2 } } },
|
||||
// v selection mode now also crosses wrapped rows for ControlChar
|
||||
{ { 1, 4 }, { { 6, 3 }, { 0, 2 } } },
|
||||
// v accessibility does not consider wrapping
|
||||
{ { 1, 4 }, { { 0, 4 }, { 0, 2 } } },
|
||||
{ { 4, 4 }, { { 4, 4 }, { 4, 4 } } },
|
||||
{ { 8, 4 }, { { 4, 4 }, { 4, 4 } } },
|
||||
|
||||
@@ -2192,8 +2188,8 @@ void TextBufferTests::GetWordBoundaries()
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Testing til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
|
||||
@@ -2209,43 +2205,123 @@ void TextBufferTests::GetWordBoundaries()
|
||||
// clang-format off
|
||||
testData = {
|
||||
// tests for first line of text
|
||||
// selectionMode accessibilityMode
|
||||
{ { 0, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 5, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 8, 1 }, { 0, 2 } } },
|
||||
{ { 7, 0 }, { { 8, 1 }, { 0, 2 } } },
|
||||
{ { 0, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 7, 1 }, { 0, 2 } } },
|
||||
{ { 7, 0 }, { { 7, 1 }, { 0, 2 } } },
|
||||
|
||||
{ { 4, 1 }, { { 8, 1 }, { 0, 2 } } },
|
||||
{ { 7, 1 }, { { 8, 1 }, { 0, 2 } } },
|
||||
{ { 9, 1 }, { { 10, 1 }, { 0, 2 } } },
|
||||
{ { 4, 1 }, { { 7, 1 }, { 0, 2 } } },
|
||||
{ { 7, 1 }, { { 7, 1 }, { 0, 2 } } },
|
||||
{ { 9, 1 }, { { 9, 1 }, { 0, 2 } } },
|
||||
|
||||
{ { 0, 2 }, { { 10, 2 }, { 4, 4 } } },
|
||||
{ { 9, 2 }, { { 10, 2 }, { 4, 4 } } },
|
||||
{ { 0, 2 }, { { 9, 2 }, { 4, 4 } } },
|
||||
{ { 9, 2 }, { { 9, 2 }, { 4, 4 } } },
|
||||
|
||||
{ { 0, 3 }, { { 6, 3 }, { 4, 4 } } },
|
||||
{ { 7, 3 }, { { 4, 4 }, { 4, 4 } } },
|
||||
{ { 0, 3 }, { { 5, 3 }, { 4, 4 } } },
|
||||
{ { 7, 3 }, { { 9, 3 }, { 4, 4 } } },
|
||||
|
||||
{ { 1, 4 }, { { 4, 4 }, { 4, 4 } } },
|
||||
{ { 4, 4 }, { { 1, 5 }, { 2, 5 } } },
|
||||
{ { 8, 4 }, { { 1, 5 }, { 2, 5 } } },
|
||||
{ { 1, 4 }, { { 3, 4 }, { 4, 4 } } },
|
||||
{ { 4, 4 }, { { 0, 5 }, { 2, 5 } } },
|
||||
{ { 8, 4 }, { { 0, 5 }, { 2, 5 } } },
|
||||
|
||||
{ { 0, 5 }, { { 1, 5 }, { 2, 5 } } },
|
||||
{ { 1, 5 }, { { 2, 5 }, { 2, 5 } } },
|
||||
{ { 4, 5 }, { { 10, 5 }, { 10, 5 } } },
|
||||
{ { 9, 5 }, { { 10, 5 }, { 10, 5 } } },
|
||||
{ { 0, 5 }, { { 0, 5 }, { 2, 5 } } },
|
||||
{ { 1, 5 }, { { 1, 5 }, { 2, 5 } } },
|
||||
{ { 4, 5 }, { { 9, 5 }, { 0, 6 } } },
|
||||
{ { 9, 5 }, { { 9, 5 }, { 0, 6 } } },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"TestEnd til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
void TextBufferTests::MoveByWord()
|
||||
{
|
||||
til::size bufferSize{ 80, 9001 };
|
||||
UINT cursorSize = 12;
|
||||
TextAttribute attr{ 0x7f };
|
||||
auto _buffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, false, &_renderer);
|
||||
|
||||
// Setup: Write lines of text to the buffer
|
||||
const std::vector<std::wstring> text = { L"word other",
|
||||
L" more words" };
|
||||
WriteLinesToBuffer(text, *_buffer);
|
||||
|
||||
// Test Data:
|
||||
// - til::point - starting position
|
||||
// - til::point - expected result (moving forwards)
|
||||
// - til::point - expected result (moving backwards)
|
||||
struct ExpectedResult
|
||||
{
|
||||
til::point moveForwards;
|
||||
til::point moveBackwards;
|
||||
};
|
||||
|
||||
struct Test
|
||||
{
|
||||
til::point startPos;
|
||||
ExpectedResult expected;
|
||||
};
|
||||
|
||||
// Set testData for GetWordStart tests
|
||||
// clang-format off
|
||||
std::vector<Test> testData = {
|
||||
// tests for first line of text
|
||||
{ { 0, 0 }, {{ 5, 0 }, { 0, 0 }} },
|
||||
{ { 1, 0 }, {{ 5, 0 }, { 1, 0 }} },
|
||||
{ { 3, 0 }, {{ 5, 0 }, { 3, 0 }} },
|
||||
{ { 4, 0 }, {{ 5, 0 }, { 4, 0 }} },
|
||||
{ { 5, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 6, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 20, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 79, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
|
||||
// tests for second line of text
|
||||
{ { 0, 1 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 1, 1 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 2, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 3, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 5, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 6, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 7, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 9, 1 }, {{ 9, 1 }, { 2, 1 }} },
|
||||
{ { 10, 1 }, {{10, 1 }, { 2, 1 }} },
|
||||
{ { 20, 1 }, {{20, 1 }, { 2, 1 }} },
|
||||
{ { 79, 1 }, {{79, 1 }, { 2, 1 }} },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:movingForwards", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES();
|
||||
|
||||
bool movingForwards;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"movingForwards", movingForwards), L"Get movingForwards variant");
|
||||
|
||||
const std::wstring_view delimiters = L" ";
|
||||
const auto lastCharPos = _buffer->GetLastNonSpaceCharacter();
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"COORD (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
auto pos{ test.startPos };
|
||||
const auto result = movingForwards ?
|
||||
_buffer->MoveToNextWord(pos, delimiters, lastCharPos) :
|
||||
_buffer->MoveToPreviousWord(pos, delimiters);
|
||||
const auto expected = movingForwards ? test.expected.moveForwards : test.expected.moveBackwards;
|
||||
VERIFY_ARE_EQUAL(expected, pos);
|
||||
|
||||
// if we moved, result is true and pos != startPos.
|
||||
// otherwise, result is false and pos == startPos.
|
||||
VERIFY_ARE_EQUAL(result, pos != test.startPos);
|
||||
}
|
||||
}
|
||||
|
||||
void TextBufferTests::GetGlyphBoundaries()
|
||||
{
|
||||
struct ExpectedResult
|
||||
|
||||
@@ -63,8 +63,9 @@
|
||||
#include <wil/nt_result_macros.h>
|
||||
|
||||
// GSL
|
||||
#include <gsl/narrow>
|
||||
#include <gsl/util>
|
||||
// Block GSL Multi Span include because it both has C++17 deprecated iterators
|
||||
// and uses the C-namespaced "max" which conflicts with Windows definitions.
|
||||
#include <gsl/gsl_util>
|
||||
#include <gsl/pointers>
|
||||
|
||||
// CppCoreCheck
|
||||
|
||||
@@ -42,7 +42,7 @@ CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COOR
|
||||
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC, BOOL keepCursorRow);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, BOOL show);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, bool show);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newParent);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyReleasePseudoConsole(HPCON hPC);
|
||||
|
||||
|
||||
@@ -444,15 +444,7 @@ VOID ConIoSrvComm::HandleFocusEvent(const CIS_EVENT* const Event)
|
||||
// TODO: MSFT: 11833883 - Determine action when wait on paint operation via
|
||||
// DirectX on OneCoreUAP times out while switching console
|
||||
// applications.
|
||||
|
||||
UnlockConsole();
|
||||
// Teardown may need to wait for an entire frame to finish, but it will not be
|
||||
// able to do so while we are holding the console lock. Relinquish the lock
|
||||
// for as long as it takes to quiesce the render thread, and then take it back
|
||||
// afterwards. This is globally safe because ConIoSrv will not process any other
|
||||
// requests while the focus event is outstanding.
|
||||
Renderer->TriggerTeardown();
|
||||
LockConsole();
|
||||
|
||||
// Relinquish control of the graphics device (only one
|
||||
// DirectX application may control the device at any one
|
||||
|
||||
@@ -951,11 +951,6 @@ LRESULT Window::_HandleGetDpiScaledSize(UINT dpiNew, _Inout_ SIZE* pSizeNew) con
|
||||
// - <none>
|
||||
void Window::_HandleDrop(const WPARAM wParam) const
|
||||
{
|
||||
if (const auto hwnd = GetWindowHandle())
|
||||
{
|
||||
SetForegroundWindow(hwnd);
|
||||
}
|
||||
|
||||
const auto drop = reinterpret_cast<HDROP>(wParam);
|
||||
Clipboard::Instance().PasteDrop(drop);
|
||||
DragFinish(drop);
|
||||
|
||||
@@ -12,11 +12,7 @@ namespace Microsoft::Console::Render::Atlas
|
||||
#ifdef NDEBUG
|
||||
#define ATLAS_DEBUG__IS_DEBUG 0
|
||||
#else
|
||||
#ifdef __INSIDE_WINDOWS
|
||||
#define ATLAS_DEBUG__IS_DEBUG 0
|
||||
#else
|
||||
#define ATLAS_DEBUG__IS_DEBUG 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If set to 1, this will cause the entire viewport to be invalidated at all times.
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
#include <VersionHelpers.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include <gsl/narrow>
|
||||
#include <gsl/util>
|
||||
#include <gsl/gsl_util>
|
||||
#include <gsl/pointers>
|
||||
#include <wil/com.h>
|
||||
#include <wil/filesystem.h>
|
||||
|
||||
@@ -789,8 +789,22 @@ bool Renderer::_CheckViewportAndScroll()
|
||||
void Renderer::_scheduleRenditionBlink()
|
||||
{
|
||||
const auto& buffer = _pData->GetTextBuffer();
|
||||
const auto blinkUsed = buffer.ContainsBlinkAttributeInRegion(_viewport);
|
||||
bool blinkUsed = false;
|
||||
|
||||
for (auto row = _viewport.Top(); row < _viewport.BottomExclusive(); ++row)
|
||||
{
|
||||
const auto& r = buffer.GetRowByOffset(row);
|
||||
for (const auto& attr : r.Attributes())
|
||||
{
|
||||
if (attr.IsBlinking())
|
||||
{
|
||||
blinkUsed = true;
|
||||
goto why_does_cpp_not_have_labeled_loops;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
why_does_cpp_not_have_labeled_loops:
|
||||
if (blinkUsed != IsTimerRunning(_renditionBlinker))
|
||||
{
|
||||
if (blinkUsed)
|
||||
@@ -1516,7 +1530,7 @@ void Renderer::_updateCursorInfo()
|
||||
_currentCursorOptions.lineRendition = lineRendition;
|
||||
_currentCursorOptions.ulCursorHeightPercent = cursorHeight;
|
||||
_currentCursorOptions.cursorPixelWidth = _pData->GetCursorPixelWidth();
|
||||
_currentCursorOptions.fIsDoubleWidth = buffer.IsGlyphDoubleWidthAt(cursorPosition);
|
||||
_currentCursorOptions.fIsDoubleWidth = buffer.GetRowByOffset(cursorPosition.y).DbcsAttrAt(cursorPosition.x) != DbcsAttribute::Single;
|
||||
_currentCursorOptions.cursorType = cursor.GetType();
|
||||
_currentCursorOptions.fUseColor = useColor;
|
||||
_currentCursorOptions.cursorColor = cursorColor;
|
||||
|
||||
@@ -270,16 +270,6 @@ constexpr T saturate(auto val)
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
if (a->ProcessControlZ)
|
||||
{
|
||||
// ProcessControlZ is only set for CONSOLE_IO_RAW_READ. To restore
|
||||
// the behavior from Windows 7 (see filehops.c:123) we need to honor
|
||||
// ^Z only if PROCESSED_INPUT is enabled.
|
||||
ULONG InputMode{ 0 };
|
||||
m->_pApiRoutines->GetConsoleInputModeImpl(*pInputBuffer, InputMode);
|
||||
a->ProcessControlZ = (InputMode & ENABLE_PROCESSED_INPUT) != 0;
|
||||
}
|
||||
|
||||
TraceConsoleAPICallWithOrigin(
|
||||
"ReadConsole",
|
||||
TraceLoggingBoolean(a->Unicode, "Unicode"),
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
#pragma warning(disable : 26432) // suppress rule of 5 violation on interface because tampering with this is fraught with peril
|
||||
virtual ~ITermDispatch() = 0;
|
||||
|
||||
virtual void UnknownSequence() noexcept = 0;
|
||||
virtual void Print(const wchar_t wchPrintable) = 0;
|
||||
virtual void PrintString(const std::wstring_view string) = 0;
|
||||
|
||||
@@ -82,7 +81,6 @@ public:
|
||||
virtual void BackIndex() = 0; // DECBI
|
||||
virtual void ForwardIndex() = 0; // DECFI
|
||||
virtual void SetWindowTitle(std::wstring_view title) = 0; // DECSWT, OscWindowTitle
|
||||
virtual void SetCurrentWorkingDirectory(const std::wstring_view uri) = 0; // OSC 7
|
||||
virtual void HorizontalTabSet() = 0; // HTS
|
||||
virtual void ForwardTab(const VTInt numTabs) = 0; // CHT, HT
|
||||
virtual void BackwardsTab(const VTInt numTabs) = 0; // CBT
|
||||
@@ -141,7 +139,7 @@ public:
|
||||
virtual void AnnounceCodeStructure(const VTInt ansiLevel) = 0; // ACS
|
||||
|
||||
virtual void SoftReset() = 0; // DECSTR
|
||||
virtual void HardReset(bool erase) = 0; // RIS
|
||||
virtual void HardReset() = 0; // RIS
|
||||
virtual void ScreenAlignmentPattern() = 0; // DECALN
|
||||
|
||||
virtual void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR
|
||||
@@ -157,9 +155,13 @@ public:
|
||||
virtual void EndHyperlink() = 0;
|
||||
|
||||
virtual void DoConEmuAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoITerm2Action(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoFinalTermAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoVsCodeAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoWTAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual StringHandler DefineSixelImage(const VTInt macroParameter,
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
ITerminalApi& operator=(const ITerminalApi&) = delete;
|
||||
ITerminalApi& operator=(ITerminalApi&&) = delete;
|
||||
|
||||
virtual void UnknownSequence() noexcept = 0;
|
||||
virtual void ReturnResponse(const std::wstring_view response) = 0;
|
||||
|
||||
struct BufferState
|
||||
@@ -47,7 +46,6 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
bool isMainBuffer;
|
||||
};
|
||||
|
||||
virtual bool IsConPTY() const noexcept = 0;
|
||||
virtual StateMachine& GetStateMachine() = 0;
|
||||
virtual BufferState GetBufferAndViewport() = 0;
|
||||
virtual void SetViewportPosition(const til::point position) = 0;
|
||||
|
||||
@@ -48,11 +48,6 @@ AdaptDispatch::AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettin
|
||||
{
|
||||
}
|
||||
|
||||
void AdaptDispatch::UnknownSequence() noexcept
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Translates and displays a single character
|
||||
// Arguments:
|
||||
@@ -2078,13 +2073,6 @@ void AdaptDispatch::SetAnsiMode(const bool ansiMode)
|
||||
// CSI = flags ; mode u - Sets kitty keyboard protocol flags
|
||||
void AdaptDispatch::SetKittyKeyboardProtocol(const VTParameter flags, const VTParameter mode) noexcept
|
||||
{
|
||||
// Avoid setting KKP flags in `_terminalInput` when we're ConPTY. Otherwise, we'd be translating
|
||||
// W32IM to KKP, even when KKP is not supported by the hosting terminal (possibly intentionally).
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto kittyFlags = gsl::narrow_cast<uint8_t>(flags.value_or(0));
|
||||
const auto KittyKeyboardProtocol = static_cast<TerminalInput::KittyKeyboardProtocolMode>(mode.value_or(1));
|
||||
_terminalInput.SetKittyKeyboardProtocol(kittyFlags, KittyKeyboardProtocol);
|
||||
@@ -2093,11 +2081,6 @@ void AdaptDispatch::SetKittyKeyboardProtocol(const VTParameter flags, const VTPa
|
||||
// CSI ? u - Queries current kitty keyboard protocol flags
|
||||
void AdaptDispatch::QueryKittyKeyboardProtocol()
|
||||
{
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto flags = static_cast<VTInt>(_terminalInput.GetKittyFlags());
|
||||
_ReturnCsiResponse(fmt::format(FMT_COMPILE(L"?{}u"), flags));
|
||||
}
|
||||
@@ -2105,11 +2088,6 @@ void AdaptDispatch::QueryKittyKeyboardProtocol()
|
||||
// CSI > flags u - Pushes current kitty keyboard flags onto the stack and sets new flags
|
||||
void AdaptDispatch::PushKittyKeyboardProtocol(const VTParameter flags)
|
||||
{
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto kittyFlags = gsl::narrow_cast<uint8_t>(flags.value_or(0));
|
||||
_terminalInput.PushKittyFlags(kittyFlags);
|
||||
}
|
||||
@@ -2117,11 +2095,6 @@ void AdaptDispatch::PushKittyKeyboardProtocol(const VTParameter flags)
|
||||
// CSI < count u - Pops one or more entries from the kitty keyboard stack
|
||||
void AdaptDispatch::PopKittyKeyboardProtocol(const VTParameter count)
|
||||
{
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto popCount = static_cast<size_t>(count.value_or(1));
|
||||
_terminalInput.PopKittyFlags(popCount);
|
||||
}
|
||||
@@ -2615,27 +2588,6 @@ void AdaptDispatch::SetWindowTitle(std::wstring_view title)
|
||||
_api.SetWindowTitle(title);
|
||||
}
|
||||
|
||||
// OSC 7 - Set Current Working Directory
|
||||
// While ConEmu's OSC 9;9 works well for native Windows paths,
|
||||
// OSC 7 uses file URIs, which may not always work.
|
||||
void AdaptDispatch::SetCurrentWorkingDirectory(std::wstring_view uri)
|
||||
{
|
||||
// Ensure that the URI has a null terminator.
|
||||
std::wstring path{ uri };
|
||||
|
||||
// PathCreateFromUrlW supports writing to the input pointer,
|
||||
// and the resulting path can never be longer than the URI.
|
||||
const auto ptr = path.data();
|
||||
auto len = gsl::narrow<DWORD>(path.size());
|
||||
THROW_IF_FAILED(PathCreateFromUrlW(ptr, ptr, &len, 0));
|
||||
path.resize(len);
|
||||
|
||||
if (til::is_legal_path(path))
|
||||
{
|
||||
_api.SetWorkingDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
//Routine Description:
|
||||
// HTS - sets a VT tab stop in the cursor's current column.
|
||||
//Arguments:
|
||||
@@ -3030,7 +2982,7 @@ void AdaptDispatch::SoftReset()
|
||||
// - Clears UDKs.
|
||||
// - Clears a down-line-loaded character set.
|
||||
// * The soft font is reset in the renderer and the font buffer is deleted.
|
||||
// - Clears the screen. (if erase=true)
|
||||
// - Clears the screen.
|
||||
// * This is like Erase in Display (3), also clearing scrollback, as well as ED(2)
|
||||
// - Returns the cursor to the upper-left corner of the screen.
|
||||
// * CUP(1;1)
|
||||
@@ -3040,8 +2992,8 @@ void AdaptDispatch::SoftReset()
|
||||
// - Sets all character sets to the default.
|
||||
// * G0(USASCII)
|
||||
//Arguments:
|
||||
// - erase: if true, erase the screen and scrollback
|
||||
void AdaptDispatch::HardReset(bool erase)
|
||||
// <none>
|
||||
void AdaptDispatch::HardReset()
|
||||
{
|
||||
// If in the alt buffer, switch back to main before doing anything else.
|
||||
if (_usingAltBuffer)
|
||||
@@ -3068,12 +3020,9 @@ void AdaptDispatch::HardReset(bool erase)
|
||||
// to ensure that it clears with the default background color.
|
||||
SoftReset();
|
||||
|
||||
if (erase)
|
||||
{
|
||||
// Clears the screen - Needs to be done in two operations.
|
||||
EraseInDisplay(DispatchTypes::EraseType::All);
|
||||
EraseInDisplay(DispatchTypes::EraseType::Scrollback);
|
||||
}
|
||||
// Clears the screen - Needs to be done in two operations.
|
||||
EraseInDisplay(DispatchTypes::EraseType::All);
|
||||
EraseInDisplay(DispatchTypes::EraseType::Scrollback);
|
||||
|
||||
// Set the color table and render modes back to their initial startup values.
|
||||
_renderSettings.RestoreDefaultSettings();
|
||||
@@ -3084,14 +3033,8 @@ void AdaptDispatch::HardReset(bool erase)
|
||||
_renderer->SynchronizedOutputChanged();
|
||||
}
|
||||
|
||||
if (erase)
|
||||
{
|
||||
// Cursor to 1,1 - the Soft Reset guarantees this is absolute.
|
||||
// Only done when clearing buffers, because when preserving content
|
||||
// the cursor should stay where the previous shell left it so the
|
||||
// new shell prompt appears in the right place.
|
||||
CursorPosition(1, 1);
|
||||
}
|
||||
// Cursor to 1,1 - the Soft Reset guarantees this is absolute
|
||||
CursorPosition(1, 1);
|
||||
|
||||
// We only reset the system line feed mode if the input mode is set. If it
|
||||
// isn't set, that either means they're both reset, and there's nothing for
|
||||
@@ -3642,10 +3585,6 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
|
||||
_pages.ActivePage().Buffer().StartCommand();
|
||||
_api.NotifyShellIntegrationMark();
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -3677,10 +3616,6 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string)
|
||||
_pages.ActivePage().Buffer().StartPrompt();
|
||||
_api.NotifyShellIntegrationMark();
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -3751,14 +3686,9 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
_api.UnknownSequence();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
|
||||
// When we add the rest of the FTCS sequences (GH#11000), we should add a
|
||||
// simple state machine here to track the most recently emitted mark from
|
||||
@@ -3833,10 +3763,6 @@ void AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
|
||||
|
||||
// If it's poorly formatted, just eat it
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
public:
|
||||
AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettings& renderSettings, TerminalInput& terminalInput) noexcept;
|
||||
|
||||
void UnknownSequence() noexcept override;
|
||||
void Print(const wchar_t wchPrintable) override;
|
||||
void PrintString(const std::wstring_view string) override;
|
||||
|
||||
@@ -114,7 +113,6 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
void BackIndex() override; // DECBI
|
||||
void ForwardIndex() override; // DECFI
|
||||
void SetWindowTitle(const std::wstring_view title) override; // DECSWT, OSCWindowTitle
|
||||
void SetCurrentWorkingDirectory(std::wstring_view uri) override; // OSC 7
|
||||
void HorizontalTabSet() override; // HTS
|
||||
void ForwardTab(const VTInt numTabs) override; // CHT, HT
|
||||
void BackwardsTab(const VTInt numTabs) override; // CBT
|
||||
@@ -130,7 +128,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
void SendC1Controls(const bool enabled) override; // S8C1T, S7C1T
|
||||
void AnnounceCodeStructure(const VTInt ansiLevel) override; // ACS
|
||||
void SoftReset() override; // DECSTR
|
||||
void HardReset(bool erase) override; // RIS
|
||||
void HardReset() override; // RIS
|
||||
void ScreenAlignmentPattern() override; // DECALN
|
||||
void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user