mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-21 22:37:19 +00:00
Compare commits
19 Commits
dev/duhowe
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dcbc5b493 | ||
|
|
83e8a76552 | ||
|
|
b49cd81d0a | ||
|
|
d6f8dc3484 | ||
|
|
bff2c6b949 | ||
|
|
e620fcd802 | ||
|
|
4c2518e5d0 | ||
|
|
17e6126597 | ||
|
|
7a83c0f167 | ||
|
|
b2666fb346 | ||
|
|
8f4fdf9751 | ||
|
|
33a80191c1 | ||
|
|
84ae7adec6 | ||
|
|
a6ebdd3d4a | ||
|
|
031998bfc3 | ||
|
|
41e08a68bd | ||
|
|
81170aff78 | ||
|
|
c90ace8326 | ||
|
|
fd30d00304 |
10
.github/workflows/spelling2.yml
vendored
10
.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@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
|
||||
checkout: true
|
||||
@@ -153,9 +153,8 @@ 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@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: microsoft/terminal@main
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
@@ -171,9 +170,8 @@ 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@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: microsoft/terminal@main
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
@@ -197,7 +195,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@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
checkout: true
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -285,4 +285,12 @@ profiles.json
|
||||
*.swp
|
||||
|
||||
# MSBuildCache
|
||||
/MSBuildCacheLogs/
|
||||
/MSBuildCacheLogs/
|
||||
|
||||
# ignore all squad files
|
||||
.squad/
|
||||
.github/workflows/squad*
|
||||
.copilot
|
||||
.github/agents/squad*
|
||||
.github/workflows/sync-squad-labels.yml
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -350,7 +350,7 @@ void ROW::_init() noexcept
|
||||
std::iota(_charOffsets.begin(), _charOffsets.end(), uint16_t{ 0 });
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
void ROW::CopyFrom(const ROW& source)
|
||||
@@ -1143,6 +1143,13 @@ til::CoordType ROW::GetTrailingColumnAtCharOffset(const ptrdiff_t offset) const
|
||||
return _createCharToColumnMapper(offset).GetTrailingColumnAt(offset);
|
||||
}
|
||||
|
||||
uint16_t ROW::GetCharOffset(til::CoordType col) const noexcept
|
||||
{
|
||||
const auto columns = GetReadableColumnCount();
|
||||
const auto colBeg = clamp(col, 0, columns);
|
||||
return _uncheckedCharOffset(gsl::narrow_cast<size_t>(colBeg));
|
||||
}
|
||||
|
||||
DelimiterClass ROW::DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept
|
||||
{
|
||||
const auto col = _clampedColumn(column);
|
||||
|
||||
@@ -172,6 +172,7 @@ public:
|
||||
std::wstring_view GetText(til::CoordType columnBegin, til::CoordType columnEnd) const noexcept;
|
||||
til::CoordType GetLeadingColumnAtCharOffset(ptrdiff_t offset) const noexcept;
|
||||
til::CoordType GetTrailingColumnAtCharOffset(ptrdiff_t offset) const noexcept;
|
||||
uint16_t GetCharOffset(til::CoordType col) const noexcept;
|
||||
DelimiterClass DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept;
|
||||
|
||||
auto AttrBegin() const noexcept { return _attr.begin(); }
|
||||
|
||||
@@ -107,14 +107,14 @@
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
<com:Extension Category="windows.comInterface">
|
||||
<!-- <com:Extension Category="windows.comInterface">
|
||||
<com:ComInterface>
|
||||
<com:ProxyStub Id="DEC4804D-56D1-4F73-9FBE-6828E7C85C56" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
|
||||
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
<com:Interface Id="6F23DA90-15C5-4203-9DB0-64E73F1B1B00" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/> <!-- ITerminalHandoff3 -->
|
||||
<com:Interface Id="6F23DA90-15C5-4203-9DB0-64E73F1B1B00" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension>
|
||||
</com:Extension> -->
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
|
||||
|
||||
@@ -308,6 +308,11 @@ namespace TerminalAppLocalTests
|
||||
// TerminalPage and not only create them successfully, but also create a
|
||||
// tab using those settings successfully.
|
||||
|
||||
// - - - IMPORTANT - - -
|
||||
// GH#14623: "closeOnExit": "never" is important for all test profiles. Without
|
||||
// it, the spawned process exits immediately in the UAP test environment,
|
||||
// and the default "automatic" close-on-exit behavior removes the
|
||||
// tab/pane asynchronously, racing against test assertions.
|
||||
static constexpr std::wstring_view settingsJson0{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
@@ -315,12 +320,14 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -347,10 +354,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TryDuplicateBadTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _DuplicateFocusedTab on tab 1
|
||||
@@ -365,12 +368,14 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -382,7 +387,8 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -438,10 +444,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TryDuplicateBadPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _SplitPane(Duplicate) on tab 1
|
||||
@@ -456,12 +458,14 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -473,7 +477,8 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -572,25 +577,29 @@ namespace TerminalAppLocalTests
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 0",
|
||||
"historySize": 1
|
||||
"historySize": 1,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 1",
|
||||
"historySize": 2
|
||||
"historySize": 2,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile2",
|
||||
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 2",
|
||||
"historySize": 3
|
||||
"historySize": 3,
|
||||
"closeOnExit": "never"
|
||||
},
|
||||
{
|
||||
"name" : "profile3",
|
||||
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}",
|
||||
"tabTitle" : "Profile 3",
|
||||
"historySize": 4
|
||||
"historySize": 4,
|
||||
"closeOnExit": "never"
|
||||
}
|
||||
],
|
||||
"schemes":
|
||||
@@ -693,7 +702,6 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -733,10 +741,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::MoveFocusFromZoomedPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second pane");
|
||||
@@ -782,10 +786,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::CloseZoomedPane()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second pane");
|
||||
@@ -841,10 +841,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::SwapPanes()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Setup 4 panes.");
|
||||
@@ -1051,31 +1047,31 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::NextMRUTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// This is a test for GH#8025 - we want to make sure that we can do both
|
||||
// in-order and MRU tab traversal, using the tab switcher and with the
|
||||
// tab switcher disabled.
|
||||
// This is a test for GH#8025 - we want to make sure that MRU tab
|
||||
// ordering works correctly and that in-order/disabled switching works.
|
||||
//
|
||||
// Note: We test MRU ordering directly rather than going through the
|
||||
// command palette tab switcher, because the palette's anchor key
|
||||
// handling auto-dismisses when no modifier keys are held (which we
|
||||
// can't simulate in the test environment).
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Create a second tab");
|
||||
Log::Comment(L"Create Tab[1]");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 1 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create a third tab");
|
||||
Log::Comment(L"Create Tab[2]");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 2 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create a fourth tab");
|
||||
Log::Comment(L"Create Tab[3]");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 3 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
@@ -1084,99 +1080,89 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Select the second tab");
|
||||
Log::Comment(L"Select Tab[1]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectTab(1);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify Tab[1] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to MRU switching");
|
||||
// MRU order should now be: Tab[1], Tab[3], Tab[2], Tab[0]
|
||||
// Verify the MRU list directly.
|
||||
Log::Comment(L"Verify MRU order: MRU[0]=Tab[1], MRU[1]=Tab[3]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
uint32_t mruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(0), mruIdx);
|
||||
VERIFY_ARE_EQUAL(1u, mruIdx, L"MRU[0] should be Tab[1] (most recent)");
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), mruIdx);
|
||||
VERIFY_ARE_EQUAL(3u, mruIdx, L"MRU[1] should be Tab[3] (last tab added)");
|
||||
});
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
|
||||
Log::Comment(L"Select MRU[1]=Tab[3] directly");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Hide the command palette, to confirm the selection");
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
// The next MRU tab after Tab[1] is Tab[3]
|
||||
uint32_t nextMruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), nextMruIdx);
|
||||
page->_SelectTab(nextMruIdx);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the second tab");
|
||||
Log::Comment(L"Select MRU[1]=Tab[1] directly");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
|
||||
Log::Comment(L"Sleep to let events propagate");
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Hide the command palette, to confirm the selection");
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
uint32_t nextMruIdx;
|
||||
page->_tabs.IndexOf(page->_mruTabs.GetAt(1), nextMruIdx);
|
||||
page->_SelectTab(nextMruIdx);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to in-order switching");
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::InOrder);
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify the third tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify Tab[1] is focused");
|
||||
});
|
||||
|
||||
// The Disabled tab switcher mode uses direct index-based switching
|
||||
// without the command palette, so it works in the test environment.
|
||||
Log::Comment(L"Change the tab switch order to not use the tab switcher (which is in-order always)");
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::Disabled);
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
|
||||
Log::Comment(L"Switch to the next in-order tab: Tab[2]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
|
||||
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify Tab[2] is focused");
|
||||
});
|
||||
|
||||
Log::Comment(L"Switch to the next in-order tab: Tab[3]");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
|
||||
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify Tab[3] is focused");
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::VerifyCommandPaletteTabSwitcherOrder()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// This is a test for GH#8188 - we want to make sure that the order of tabs
|
||||
// is preserved in the CommandPalette's TabSwitcher
|
||||
// This is a test for GH#8188 - we want to make sure that the MRU
|
||||
// ordering is correctly maintained as tabs are selected.
|
||||
//
|
||||
// Note: We verify MRU ordering directly rather than going through
|
||||
// the command palette tab switcher, because the palette's anchor key
|
||||
// handling auto-dismisses when no modifier keys are held (which we
|
||||
// can't simulate in the test environment).
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
@@ -1189,7 +1175,7 @@ namespace TerminalAppLocalTests
|
||||
});
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
|
||||
Log::Comment(L"give alphabetical names to all switch tab actions");
|
||||
Log::Comment(L"give alphabetical names to all tabs");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_GetTabImpl(page->_tabs.GetAt(0))->Title(L"a");
|
||||
});
|
||||
@@ -1211,18 +1197,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"c", page->_tabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_tabs.GetAt(3).Title());
|
||||
|
||||
// MRU order after creating Tab[0]-Tab[3]: MRU[0]=Tab[3], MRU[3]=Tab[0]
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to MRU switching");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
||||
});
|
||||
|
||||
Log::Comment(L"Select the tabs from 0 to 3");
|
||||
Log::Comment(L"Select Tab[0] through Tab[3] to establish MRU order");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(0));
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(1));
|
||||
@@ -1230,47 +1212,31 @@ namespace TerminalAppLocalTests
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(3));
|
||||
});
|
||||
|
||||
Log::Comment(L"Verify MRU order: MRU[0]='d', MRU[1]='c', MRU[2]='b', MRU[3]='a'");
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true, nullptr);
|
||||
// In the course of a single tick, the Command Palette will:
|
||||
// * open
|
||||
// * select the proper tab from the mru's list
|
||||
// * raise an event for _filteredActionsView().SelectionChanged to
|
||||
// immediately preview the new tab
|
||||
// * raise a _SwitchToTabRequestedHandlers event
|
||||
// * then dismiss itself, because we can't fake holing down an
|
||||
// anchor key in the tests
|
||||
Log::Comment(L"Select Tab[2]='c' (MRU[1] after 'd')");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_SelectTab(2);
|
||||
});
|
||||
|
||||
Log::Comment(L"Verify MRU order updated: MRU[0]='c', MRU[1]='d', MRU[2]='b', MRU[3]='a'");
|
||||
TestOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->LoadCommandPalette());
|
||||
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode");
|
||||
// At this point, the contents of the command palette's _mruTabs list is
|
||||
// still the _old_ ordering (d, c, b, a). The ordering is only updated
|
||||
// in TerminalPage::_SelectNextTab, but as we saw before, the palette
|
||||
// will also dismiss itself immediately when that's called. So we can't
|
||||
// really inspect the contents of the list in this test, unfortunately.
|
||||
}
|
||||
|
||||
void TabTests::TestWindowRenameSuccessful()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1303,7 +1269,6 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1336,10 +1301,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewCommitScheme()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1402,10 +1363,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewDismissScheme()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then dismissed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
@@ -1454,10 +1411,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestPreviewSchemeWhilePreviewing()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Preview a color scheme, then preview another scheme. ");
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
@@ -1525,10 +1478,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TestClampSwitchToTab()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True") // GH#19610 tracks re-enabling this test
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
Log::Comment(L"Test that switching to a tab index higher than the number of tabs just clamps to the last tab.");
|
||||
|
||||
auto page = _commonSetup();
|
||||
|
||||
@@ -938,6 +938,27 @@ namespace winrt::TerminalApp::implementation
|
||||
co_return;
|
||||
}
|
||||
|
||||
// Launch `wt -w <name>` so the monarch can either summon an existing
|
||||
// window with that name or restore a persisted workspace.
|
||||
safe_void_coroutine TerminalPage::_OpenWorkspaceWindow(const winrt::hstring name)
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
|
||||
const auto exePath{ GetWtExePath() };
|
||||
const auto cmdline = fmt::format(FMT_COMPILE(L"-w {}"), std::wstring_view{ name });
|
||||
|
||||
SHELLEXECUTEINFOW seInfo{ 0 };
|
||||
seInfo.cbSize = sizeof(seInfo);
|
||||
seInfo.fMask = SEE_MASK_NOASYNC;
|
||||
seInfo.lpVerb = L"open";
|
||||
seInfo.lpFile = exePath.c_str();
|
||||
seInfo.lpParameters = cmdline.c_str();
|
||||
seInfo.nShow = SW_SHOWNORMAL;
|
||||
LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo));
|
||||
|
||||
co_return;
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleNewWindow(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& actionArgs)
|
||||
{
|
||||
@@ -1633,4 +1654,25 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleOpenWorkspace(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
// Open (or summon) a named window. We launch a new `wt -w <name>`
|
||||
// process which the monarch will route to the correct live window or
|
||||
// restore from a persisted workspace.
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<OpenWorkspaceArgs>())
|
||||
{
|
||||
const auto name = realArgs.Name();
|
||||
if (!name.empty())
|
||||
{
|
||||
_OpenWorkspaceWindow(name);
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ namespace winrt::TerminalApp::implementation
|
||||
WINRT_PROPERTY(TerminalApp::CommandlineArgs, Command, nullptr);
|
||||
WINRT_PROPERTY(winrt::hstring, Content);
|
||||
WINRT_PROPERTY(Windows::Foundation::IReference<Windows::Foundation::Rect>, InitialBounds);
|
||||
WINRT_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::WindowLayout, PersistedLayout, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -51,5 +51,6 @@ namespace TerminalApp
|
||||
CommandlineArgs Command { get; };
|
||||
String Content { get; };
|
||||
Windows.Foundation.IReference<Windows.Foundation.Rect> InitialBounds { get; };
|
||||
Microsoft.Terminal.Settings.Model.WindowLayout PersistedLayout;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -672,9 +672,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Dieser Linktyp wird derzeit nicht unterstützt:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Abbrechen</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Dieser Link kann zu einem unsicheren Speicherort führen. Links können ihren Computer und Ihre Daten beschädigen. Klicken Sie zum Schutz Des Computers nur auf Links aus vertrauenswürdigen Quellen.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Trotzdem öffnen</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Einstellungen</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>This link type is currently not supported:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>This link may lead to an unsafe location. Hyperlinks can be harmful to your computer and data. To protect your computer, only click links from trusted sources.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Open anyway</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Este tipo de vínculo no se admite actualmente:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancelar</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Este vínculo puede dar lugar a una ubicación no segura. Los hipervínculos pueden ser perjudiciales para el equipo y los datos. Para proteger el equipo, haga clic solo en vínculos de orígenes de confianza.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Abrir de todas formas</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Configuración</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ce type de lien n’est actuellement pas pris en charge :</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Annuler</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ce lien peut entraîner un emplacement non sécurisé. Les liens hypertexte peuvent endommager votre ordinateur et vos données. Pour protéger votre ordinateur, cliquez uniquement sur des liens provenant de sources fiables.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Ouvrir quand même</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Paramètres</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Questo tipo di collegamento non è al momento supportato:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Annulla</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Questo collegamento potrebbe causare un percorso non sicuro. I collegamenti ipertestuali possono essere dannosi per il computer e i dati. Per proteggere il computer, fare clic solo su collegamenti da origini attendibili.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Apri comunque</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Impostazioni</value>
|
||||
</data>
|
||||
|
||||
@@ -670,9 +670,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>このリンクの種類は現在サポートされていません:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>キャンセル</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>このリンクは安全でない可能性があります。ハイパーリンクは、コンピューターやデータに問題を起こす可能性があります。コンピューターを保護するには、信頼できるソースからのリンクのみをクリックしてください。</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>開く</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>이 링크 형식은 현재 지원되지 않습니다.</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>취소</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>이 링크로 인해 안전하지 않은 위치가 발생할 수 있습니다. 하이퍼링크는 컴퓨터와 데이터를 손상시킬 수 있습니다. 컴퓨터를 보호하려면 신뢰할 수 있는 원본의 링크만 클릭하세요.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>열기</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>설정</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Não há suporte para este tipo de link no momento:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Cancelar</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Este link pode levar a um local não seguro. Hiperlinks podem ser prejudiciais ao computador e aos dados. Para proteger o computador, clique somente em links de fontes confiáveis.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Abrir mesmo assim</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Configurações</value>
|
||||
</data>
|
||||
|
||||
@@ -669,8 +669,14 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Сąñс℮ł !</value>
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Çдπсёľ !</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ŧђīś ℓîŋќ мαў ľêãδ τб áń úʼnšàƒé ℓоćάŧίоñ. Ĥўрзŗℓĭŋķѕ çâⁿ ъέ ђąřмƒúļ τό ўôця ċómφύŧèґ аňδ ðáťǻ. Ţб ρгøťėçŧ ўòύг ςömφùţĕŕ, ŏŋľỳ čℓΐςķ łίŋκѕ ƒřöм ťŗμѕŧєđ śόυяčêś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Őρέй ǻпŷŵãγ !!!</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Śëţťĩпğś !!</value>
|
||||
|
||||
@@ -669,8 +669,14 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Сąñс℮ł !</value>
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Çдπсёľ !</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ŧђīś ℓîŋќ мαў ľêãδ τб áń úʼnšàƒé ℓоćάŧίоñ. Ĥўрзŗℓĭŋķѕ çâⁿ ъέ ђąřмƒúļ τό ўôця ċómφύŧèґ аňδ ðáťǻ. Ţб ρгøťėçŧ ўòύг ςömφùţĕŕ, ŏŋľỳ čℓΐςķ łίŋκѕ ƒřöм ťŗμѕŧєđ śόυяčêś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Őρέй ǻпŷŵãγ !!!</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Śëţťĩпğś !!</value>
|
||||
|
||||
@@ -669,8 +669,14 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Ťђïś łϊηќ ŧурē ιş çũґѓзⁿτľÿ ñστ şΰρρоŕŧĕđ: !!! !!! !!! !!! </value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Сąñс℮ł !</value>
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Çдπсёľ !</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Ŧђīś ℓîŋќ мαў ľêãδ τб áń úʼnšàƒé ℓоćάŧίоñ. Ĥўрзŗℓĭŋķѕ çâⁿ ъέ ђąřмƒúļ τό ўôця ċómφύŧèґ аňδ ðáťǻ. Ţб ρгøťėçŧ ўòύг ςömφùţĕŕ, ŏŋľỳ čℓΐςķ łίŋκѕ ƒřöм ťŗμѕŧєđ śόυяčêś. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Őρέй ǻпŷŵãγ !!!</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Śëţťĩпğś !!</value>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>Этот тип связи в настоящее время не поддерживается:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Отмена</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>Эта ссылка может привести к небезопасному расположению. Гиперссылки могут нанести вред компьютеру и данным. Чтобы защитить компьютер, щелкните ссылки только из надежных источников.</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>Все равно открыть</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>Параметры</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>当前不支持此链接类型:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>此链接可能会导致不安全的位置。超链接可能对你的计算机和数据有害。若要保护你的计算机,请仅单击来自受信任源的链接。</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>仍然打开</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
|
||||
@@ -669,9 +669,15 @@
|
||||
<data name="UnsupportedSchemeText" xml:space="preserve">
|
||||
<value>目前不支援此連結類型:</value>
|
||||
</data>
|
||||
<data name="CouldNotOpenUriDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<data name="UriErrorDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmText" xml:space="preserve">
|
||||
<value>此連結可能通往不安全地點。超連結可能對你的電腦和資料造成傷害。為了保護你的電腦,只點擊來自可信來源的連結。</value>
|
||||
</data>
|
||||
<data name="UnsafeUrlConfirmAllowAction" xml:space="preserve">
|
||||
<value>一律開啟</value>
|
||||
</data>
|
||||
<data name="SettingsTab" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
|
||||
@@ -438,6 +438,21 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
const auto focusedTabIndex{ _GetFocusedTabIndex() };
|
||||
|
||||
// If this is the last tab in a named window, persist the workspace
|
||||
// layout now—before the tab is shut down—so GetWindowLayout() can
|
||||
// still see its tab data.
|
||||
if (_tabs.Size() == 1)
|
||||
{
|
||||
const auto& windowName = _WindowProperties.WindowName();
|
||||
if (!windowName.empty())
|
||||
{
|
||||
if (const auto layout = GetWindowLayout())
|
||||
{
|
||||
ApplicationState::SharedInstance().SaveWorkspace(windowName, layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removing the tab from the collection should destroy its control and disconnect its connection,
|
||||
// but it doesn't always do so. The UI tree may still be holding the control and preventing its destruction.
|
||||
tab.Shutdown();
|
||||
|
||||
@@ -25,6 +25,21 @@ namespace winrt::TerminalApp::implementation
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
void TabRowControl::WorkspaceName(const winrt::hstring& value)
|
||||
{
|
||||
if (_WorkspaceName != value)
|
||||
{
|
||||
_WorkspaceName = value;
|
||||
PropertyChanged.raise(*this, WUX::Data::PropertyChangedEventArgs{ L"WorkspaceName" });
|
||||
|
||||
// Collapse the name text when empty so the button shows only the icon.
|
||||
if (const auto textBlock = WorkspaceNameText())
|
||||
{
|
||||
textBlock.Visibility(value.empty() ? WUX::Visibility::Collapsed : WUX::Visibility::Visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Bound in the Xaml editor to the [+] button.
|
||||
// Arguments:
|
||||
|
||||
@@ -19,6 +19,14 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
til::property_changed_event PropertyChanged;
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, ShowElevationShield, PropertyChanged.raise, false);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, ShowWindowsButton, PropertyChanged.raise, true);
|
||||
|
||||
public:
|
||||
winrt::hstring WorkspaceName() const noexcept { return _WorkspaceName; }
|
||||
void WorkspaceName(const winrt::hstring& value);
|
||||
|
||||
private:
|
||||
winrt::hstring _WorkspaceName{};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,5 +9,7 @@ namespace TerminalApp
|
||||
TabRowControl();
|
||||
Microsoft.UI.Xaml.Controls.TabView TabView { get; };
|
||||
Boolean ShowElevationShield;
|
||||
Boolean ShowWindowsButton;
|
||||
String WorkspaceName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,14 +35,43 @@
|
||||
TabWidthMode="Equal">
|
||||
|
||||
<mux:TabView.TabStripHeader>
|
||||
<!-- EA18 is the "Shield" glyph -->
|
||||
<FontIcon x:Uid="ElevationShield"
|
||||
Margin="9,4,0,4"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind ShowElevationShield, Mode=OneWay}" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!-- EA18 is the "Shield" glyph -->
|
||||
<FontIcon x:Uid="ElevationShield"
|
||||
Margin="9,4,0,4"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind ShowElevationShield, Mode=OneWay}" />
|
||||
|
||||
<!-- Workspace/windows button -->
|
||||
<Button x:Name="WorkspaceDropdown"
|
||||
Margin="4,4,0,4"
|
||||
Padding="8,2,4,2"
|
||||
VerticalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Visibility="{x:Bind ShowWindowsButton, Mode=OneWay}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<!-- EE40 is the "TaskViewSettings" glyph -->
|
||||
<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock x:Name="WorkspaceNameText"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Visibility="Collapsed"
|
||||
Text="{x:Bind WorkspaceName, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout x:Name="WorkspaceFlyout" />
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</mux:TabView.TabStripHeader>
|
||||
|
||||
<mux:TabView.TabStripFooter>
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "TerminalSettingsCache.h"
|
||||
|
||||
#include "LaunchPositionRequest.g.cpp"
|
||||
#include "WindowListEntry.g.cpp"
|
||||
#include "WindowListRequest.g.cpp"
|
||||
#include "RenameWindowRequestedArgs.g.cpp"
|
||||
#include "RequestMoveContentArgs.g.cpp"
|
||||
#include "TerminalPage.g.cpp"
|
||||
@@ -334,6 +336,20 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
auto tabRowImpl = winrt::get_self<implementation::TabRowControl>(_tabRow);
|
||||
_newTabButton = tabRowImpl->NewTabButton();
|
||||
_workspaceFlyout = tabRowImpl->WorkspaceFlyout();
|
||||
|
||||
// Set the initial workspace name from the window name.
|
||||
// Use raw WindowName() so unnamed windows show no text.
|
||||
_tabRow.WorkspaceName(_WindowProperties.WindowName());
|
||||
|
||||
// Rebuild the workspace flyout each time it opens so it always
|
||||
// reflects the latest set of persisted workspaces.
|
||||
_workspaceFlyout.Opening([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->_PopulateWorkspaceFlyout();
|
||||
}
|
||||
});
|
||||
|
||||
if (_settings.GlobalSettings().ShowTabsInTitlebar())
|
||||
{
|
||||
@@ -443,6 +459,12 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
_tabRow.ShowElevationShield(IsRunningElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
|
||||
// Apply the ShowWindowsButton theme setting.
|
||||
if (const auto theme = _settings.GlobalSettings().CurrentTheme())
|
||||
{
|
||||
_tabRow.ShowWindowsButton(theme.Window() ? theme.Window().ShowWindowsButton() : true);
|
||||
}
|
||||
|
||||
_adjustProcessPriorityThrottled = std::make_shared<ThrottledFunc<>>(
|
||||
DispatcherQueue::GetForCurrentThread(),
|
||||
til::throttled_func_options{
|
||||
@@ -2232,14 +2254,14 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::PersistState()
|
||||
WindowLayout TerminalPage::GetWindowLayout()
|
||||
{
|
||||
// This method may be called for a window even if it hasn't had a tab yet or lost all of them.
|
||||
// We shouldn't persist such windows.
|
||||
const auto tabCount = _tabs.Size();
|
||||
if (_startupState != StartupState::Initialized || tabCount == 0)
|
||||
{
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<ActionAndArgs> actions;
|
||||
@@ -2254,7 +2276,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Avoid persisting a window with zero tabs, because `BuildStartupActions` happened to return an empty vector.
|
||||
if (actions.empty())
|
||||
{
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// if the focused tab was not the last tab, restore that
|
||||
@@ -2303,7 +2325,49 @@ namespace winrt::TerminalApp::implementation
|
||||
RequestLaunchPosition.raise(*this, launchPosRequest);
|
||||
layout.InitialPosition(launchPosRequest.Position());
|
||||
|
||||
ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
void TerminalPage::PersistState()
|
||||
{
|
||||
// There are two persistence mechanisms in play here:
|
||||
// * PersistedWindowLayouts (vector) — consumed on next startup to
|
||||
// re-open a matching set of windows. Cleared after restore.
|
||||
// * PersistedWorkspaces (name-keyed map) — the full tab/buffer
|
||||
// state of a named window, claimed by name on demand via
|
||||
// ApplicationState::TakeWorkspace.
|
||||
//
|
||||
// For named windows we save the full layout into the workspace map
|
||||
// and drop a lightweight `openWorkspace` stub into the generic vector,
|
||||
// so the generic restore path re-opens the named window which in
|
||||
// turn claims its own workspace. Unnamed windows don't have a stable
|
||||
// key, so their full layout is stored directly in the vector.
|
||||
if (const auto layout = GetWindowLayout())
|
||||
{
|
||||
const auto& windowName = _WindowProperties.WindowName();
|
||||
if (!windowName.empty())
|
||||
{
|
||||
// Persist the full layout into the workspace collection.
|
||||
ApplicationState::SharedInstance().SaveWorkspace(windowName, layout);
|
||||
|
||||
// Build a minimal layout with just an openWorkspace action
|
||||
// so the generic restore path re-opens this workspace by name.
|
||||
std::vector<ActionAndArgs> actions;
|
||||
ActionAndArgs action;
|
||||
action.Action(ShortcutAction::OpenWorkspace);
|
||||
OpenWorkspaceArgs args{ windowName };
|
||||
action.Args(args);
|
||||
actions.emplace_back(std::move(action));
|
||||
|
||||
WindowLayout stub;
|
||||
stub.TabLayout(winrt::single_threaded_vector<ActionAndArgs>(std::move(actions)));
|
||||
ApplicationState::SharedInstance().AppendPersistedWindowLayout(stub);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -3085,18 +3149,42 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void TerminalPage::_OpenHyperlinkHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs)
|
||||
safe_void_coroutine TerminalPage::_OpenHyperlinkHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto parsed = winrt::Windows::Foundation::Uri(eventArgs.Uri());
|
||||
auto uriString{ eventArgs.Uri() };
|
||||
auto parsed = winrt::Windows::Foundation::Uri(uriString);
|
||||
if (_IsUriSupported(parsed))
|
||||
{
|
||||
ShellExecute(nullptr, L"open", eventArgs.Uri().c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
bool shouldLaunch{ _IsUriConsideredSomewhatSafe(parsed) };
|
||||
|
||||
if (!shouldLaunch)
|
||||
{
|
||||
if (auto presenter{ _dialogPresenter.get() })
|
||||
{
|
||||
// FindName needs to be called first to actually load the xaml object
|
||||
auto unopenedUriDialog = FindName(L"UriErrorDialog").try_as<WUX::Controls::ContentDialog>();
|
||||
|
||||
// Insert the reason and the URI
|
||||
unopenedUriDialog.SecondaryButtonText(RS_(L"UnsafeUrlConfirmAllowAction"));
|
||||
CouldNotOpenUriReason().Text(RS_(L"UnsafeUrlConfirmText"));
|
||||
UnopenedUri().Text(uriString);
|
||||
|
||||
// Show the dialog
|
||||
auto result = co_await presenter.ShowDialog(unopenedUriDialog);
|
||||
shouldLaunch = result == ContentDialogResult::Secondary;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldLaunch)
|
||||
{
|
||||
ShellExecuteW(nullptr, L"open", uriString.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_ShowCouldNotOpenDialog(RS_(L"UnsupportedSchemeText"), eventArgs.Uri());
|
||||
_ShowCouldNotOpenDialog(RS_(L"UnsupportedSchemeText"), uriString);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -3116,9 +3204,10 @@ namespace winrt::TerminalApp::implementation
|
||||
if (auto presenter{ _dialogPresenter.get() })
|
||||
{
|
||||
// FindName needs to be called first to actually load the xaml object
|
||||
auto unopenedUriDialog = FindName(L"CouldNotOpenUriDialog").try_as<WUX::Controls::ContentDialog>();
|
||||
auto unopenedUriDialog = FindName(L"UriErrorDialog").try_as<WUX::Controls::ContentDialog>();
|
||||
|
||||
// Insert the reason and the URI
|
||||
unopenedUriDialog.SecondaryButtonText({});
|
||||
CouldNotOpenUriReason().Text(reason);
|
||||
UnopenedUri().Text(uri);
|
||||
|
||||
@@ -3171,6 +3260,30 @@ namespace winrt::TerminalApp::implementation
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerminalPage::_IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri)
|
||||
{
|
||||
if (parsedUri.SchemeName() == L"http" || parsedUri.SchemeName() == L"https")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (parsedUri.SchemeName() == L"file")
|
||||
{
|
||||
static const auto pathext{ wil::TryGetEnvironmentVariableW<std::wstring>(L"PATHEXT") };
|
||||
const auto filename = parsedUri.Path();
|
||||
for (const auto& e : til::split_iterator{ std::wstring_view{ pathext }, L';' })
|
||||
{
|
||||
if (til::ends_with_insensitive_ascii(filename, e))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Important! Don't take this eventArgs by reference, we need to extend the
|
||||
// lifetime of it to the other side of the co_await!
|
||||
safe_void_coroutine TerminalPage::_ControlNoticeRaisedHandler(const IInspectable /*sender*/,
|
||||
@@ -3885,6 +3998,12 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
_tabRow.ShowElevationShield(IsRunningElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
|
||||
// Apply the ShowWindowsButton theme setting.
|
||||
if (const auto theme = _settings.GlobalSettings().CurrentTheme())
|
||||
{
|
||||
_tabRow.ShowWindowsButton(theme.Window() ? theme.Window().ShowWindowsButton() : true);
|
||||
}
|
||||
|
||||
Media::SolidColorBrush transparent{ Windows::UI::Colors::Transparent() };
|
||||
_tabView.Background(transparent);
|
||||
|
||||
@@ -5525,6 +5644,156 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the workspace flyout contents. Called every time the flyout opens
|
||||
// so it reflects the current set of persisted workspaces.
|
||||
void TerminalPage::_PopulateWorkspaceFlyout()
|
||||
{
|
||||
if (!_workspaceFlyout)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_workspaceFlyout.Items().Clear();
|
||||
|
||||
// --- "Name / Rename this window" ---
|
||||
{
|
||||
MenuFlyoutItem item{};
|
||||
item.Text(winrt::hstring{ _WindowProperties.WindowName().empty() ? L"Name this window\u2026" : L"Rename this window\u2026" });
|
||||
|
||||
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE8AC"); // Rename glyph
|
||||
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
|
||||
item.Icon(iconElement);
|
||||
|
||||
item.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
// Re-use the existing openWindowRenamer action.
|
||||
page->_actionDispatch->DoAction(ActionAndArgs{ ShortcutAction::OpenWindowRenamer, nullptr });
|
||||
}
|
||||
});
|
||||
_workspaceFlyout.Items().Append(item);
|
||||
}
|
||||
|
||||
// --- Gather open window info first so we can filter workspaces ---
|
||||
// Ask the host (via AppHost → WindowEmperor) for all live windows.
|
||||
const auto windowListReq{ winrt::make<WindowListRequest>() };
|
||||
RequestWindowList.raise(*this, windowListReq);
|
||||
const auto windowEntries = windowListReq.Entries();
|
||||
|
||||
// Collect the names of all currently-open windows so we can hide
|
||||
// workspaces that are already live from the saved-workspaces list.
|
||||
std::set<winrt::hstring> openWindowNames;
|
||||
if (windowEntries)
|
||||
{
|
||||
for (const auto& entry : windowEntries)
|
||||
{
|
||||
const auto& name = entry.Name();
|
||||
if (!name.empty())
|
||||
{
|
||||
openWindowNames.emplace(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Saved workspaces section (only those not currently open) ---
|
||||
const auto workspaces = ApplicationState::SharedInstance().AllPersistedWorkspaces();
|
||||
if (workspaces && workspaces.Size() > 0)
|
||||
{
|
||||
bool addedSeparator = false;
|
||||
|
||||
for (const auto& pair : workspaces)
|
||||
{
|
||||
const auto name = pair.Key();
|
||||
|
||||
// Skip workspaces that correspond to a currently-open window.
|
||||
if (openWindowNames.count(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!addedSeparator)
|
||||
{
|
||||
_workspaceFlyout.Items().Append(MenuFlyoutSeparator{});
|
||||
addedSeparator = true;
|
||||
}
|
||||
|
||||
MenuFlyoutItem item{};
|
||||
item.Text(name);
|
||||
|
||||
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE8F1"); // SwitchApps glyph
|
||||
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
|
||||
item.Icon(iconElement);
|
||||
|
||||
item.Click([weakThis{ get_weak() }, name](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->_OpenWorkspaceWindow(name);
|
||||
}
|
||||
});
|
||||
|
||||
_workspaceFlyout.Items().Append(item);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Open windows section ---
|
||||
|
||||
if (windowEntries && windowEntries.Size() > 0)
|
||||
{
|
||||
_workspaceFlyout.Items().Append(MenuFlyoutSeparator{});
|
||||
|
||||
const auto thisWindowId = _WindowProperties.WindowId();
|
||||
|
||||
for (const auto& entry : windowEntries)
|
||||
{
|
||||
const auto id = entry.Id();
|
||||
const auto& name = entry.Name();
|
||||
|
||||
// Build display text like "#1: MyWindow" or "#2: <unnamed>"
|
||||
winrt::hstring displayText;
|
||||
if (name.empty())
|
||||
{
|
||||
displayText = winrt::hstring{ fmt::format(FMT_COMPILE(L"#{} (unnamed)"), id) };
|
||||
}
|
||||
else
|
||||
{
|
||||
displayText = winrt::hstring{ fmt::format(FMT_COMPILE(L"#{}: {}"), id, name) };
|
||||
}
|
||||
|
||||
MenuFlyoutItem item{};
|
||||
item.Text(displayText);
|
||||
|
||||
// Use a different glyph for the current window
|
||||
if (id == thisWindowId)
|
||||
{
|
||||
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE73E"); // CheckMark glyph
|
||||
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
|
||||
item.Icon(iconElement);
|
||||
item.IsEnabled(false); // Can't summon yourself
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE737"); // ChromeRestore glyph
|
||||
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
|
||||
item.Icon(iconElement);
|
||||
|
||||
// Click handler: summon the target window by ID.
|
||||
// We raise SummonWindowByIdRequested which is handled by
|
||||
// AppHost to directly summon the window without creating
|
||||
// a new tab (unlike _OpenWorkspaceWindow which launches
|
||||
// `wt -w <name>` and creates a tab).
|
||||
item.Click([weakThis{ get_weak() }, id](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->SummonWindowByIdRequested.raise(*page, winrt::make<SummonWindowByIdRequestedArgs>(id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_workspaceFlyout.Items().Append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handler for our WindowProperties's PropertyChanged event. We'll use this
|
||||
// to pop the "Identify Window" toast when the user renames our window.
|
||||
void TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/, const WUX::Data::PropertyChangedEventArgs& args)
|
||||
@@ -5534,6 +5803,10 @@ namespace winrt::TerminalApp::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the workspace dropdown label in sync with the window name.
|
||||
// Use raw WindowName() so clearing the name hides the text.
|
||||
_tabRow.WorkspaceName(_WindowProperties.WindowName());
|
||||
|
||||
// DON'T display the confirmation if this is the name we were
|
||||
// given on startup!
|
||||
if (_startupState == StartupState::Initialized)
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
#include "AppKeyBindings.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
#include "RenameWindowRequestedArgs.g.h"
|
||||
#include "SummonWindowByIdRequestedArgs.g.h"
|
||||
#include "RequestMoveContentArgs.g.h"
|
||||
#include "LaunchPositionRequest.g.h"
|
||||
#include "WindowListEntry.g.h"
|
||||
#include "WindowListRequest.g.h"
|
||||
#include "Toast.h"
|
||||
|
||||
#include "WindowsPackageManagerFactory.h"
|
||||
@@ -63,6 +66,15 @@ namespace winrt::TerminalApp::implementation
|
||||
_ProposedName{ name } {};
|
||||
};
|
||||
|
||||
struct SummonWindowByIdRequestedArgs : SummonWindowByIdRequestedArgsT<SummonWindowByIdRequestedArgs>
|
||||
{
|
||||
WINRT_PROPERTY(uint64_t, WindowId);
|
||||
|
||||
public:
|
||||
SummonWindowByIdRequestedArgs(uint64_t id) :
|
||||
_WindowId{ id } {};
|
||||
};
|
||||
|
||||
struct RequestMoveContentArgs : RequestMoveContentArgsT<RequestMoveContentArgs>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::hstring, Window);
|
||||
@@ -84,6 +96,25 @@ namespace winrt::TerminalApp::implementation
|
||||
til::property<winrt::Microsoft::Terminal::Settings::Model::LaunchPosition> Position;
|
||||
};
|
||||
|
||||
struct WindowListEntry : WindowListEntryT<WindowListEntry>
|
||||
{
|
||||
WindowListEntry() = default;
|
||||
|
||||
til::property<uint64_t> Id;
|
||||
til::property<winrt::hstring> Name;
|
||||
};
|
||||
|
||||
struct WindowListRequest : WindowListRequestT<WindowListRequest>
|
||||
{
|
||||
WindowListRequest() :
|
||||
_Entries{ winrt::single_threaded_vector<winrt::TerminalApp::WindowListEntry>() } {}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IVector<winrt::TerminalApp::WindowListEntry> Entries() const { return _Entries; }
|
||||
|
||||
private:
|
||||
winrt::Windows::Foundation::Collections::IVector<winrt::TerminalApp::WindowListEntry> _Entries;
|
||||
};
|
||||
|
||||
struct WinGetSearchParams
|
||||
{
|
||||
winrt::Microsoft::Management::Deployment::PackageMatchField Field;
|
||||
@@ -122,6 +153,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
safe_void_coroutine RequestQuit();
|
||||
safe_void_coroutine CloseWindow();
|
||||
winrt::Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
|
||||
void PersistState();
|
||||
std::vector<IPaneContent> Panes() const;
|
||||
|
||||
@@ -192,6 +224,7 @@ namespace winrt::TerminalApp::implementation
|
||||
til::typed_event<IInspectable, IInspectable> IdentifyWindowsRequested;
|
||||
til::typed_event<IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs> RenameWindowRequested;
|
||||
til::typed_event<IInspectable, IInspectable> SummonWindowRequested;
|
||||
til::typed_event<IInspectable, winrt::TerminalApp::SummonWindowByIdRequestedArgs> SummonWindowByIdRequested;
|
||||
til::typed_event<IInspectable, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs> WindowSizeChanged;
|
||||
|
||||
til::typed_event<IInspectable, IInspectable> OpenSystemMenu;
|
||||
@@ -203,6 +236,7 @@ namespace winrt::TerminalApp::implementation
|
||||
til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs> RequestReceiveContent;
|
||||
|
||||
til::typed_event<IInspectable, winrt::TerminalApp::LaunchPositionRequest> RequestLaunchPosition;
|
||||
til::typed_event<IInspectable, winrt::TerminalApp::WindowListRequest> RequestWindowList;
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, PropertyChanged.raise, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, FrameBrush, PropertyChanged.raise, nullptr);
|
||||
@@ -225,6 +259,7 @@ namespace winrt::TerminalApp::implementation
|
||||
TerminalApp::TabRowControl _tabRow{ nullptr };
|
||||
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
|
||||
Microsoft::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
|
||||
Windows::UI::Xaml::Controls::MenuFlyout _workspaceFlyout{ nullptr };
|
||||
winrt::TerminalApp::ColorPickupFlyout _tabColorPicker{ nullptr };
|
||||
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
@@ -324,6 +359,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _restartPaneConnection(const TerminalApp::TerminalPaneContent&, const winrt::Windows::Foundation::IInspectable&);
|
||||
|
||||
safe_void_coroutine _OpenNewWindow(const Microsoft::Terminal::Settings::Model::INewContentArgs newContentArgs);
|
||||
safe_void_coroutine _OpenWorkspaceWindow(const winrt::hstring name);
|
||||
|
||||
void _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
@@ -422,8 +458,9 @@ namespace winrt::TerminalApp::implementation
|
||||
safe_void_coroutine _PasteFromClipboardHandler(const IInspectable sender,
|
||||
const Microsoft::Terminal::Control::PasteFromClipboardEventArgs eventArgs);
|
||||
|
||||
void _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs);
|
||||
bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri);
|
||||
safe_void_coroutine _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs);
|
||||
static bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri);
|
||||
static bool _IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri);
|
||||
|
||||
void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri);
|
||||
bool _CopyText(bool dismissSelection, bool singleLine, bool withControlSequences, Microsoft::Terminal::Control::CopyFormat formats);
|
||||
@@ -562,6 +599,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _PopulateContextMenu(const Microsoft::Terminal::Control::TermControl& control, const Microsoft::UI::Xaml::Controls::CommandBarFlyout& sender, const bool withSelection);
|
||||
void _PopulateQuickFixMenu(const Microsoft::Terminal::Control::TermControl& control, const Windows::UI::Xaml::Controls::MenuFlyout& sender);
|
||||
void _PopulateWorkspaceFlyout();
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender);
|
||||
@@ -586,4 +624,5 @@ namespace winrt::TerminalApp::implementation
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(TerminalPage);
|
||||
BASIC_FACTORY(WindowListEntry);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ namespace TerminalApp
|
||||
{
|
||||
String ProposedName { get; };
|
||||
};
|
||||
[default_interface] runtimeclass SummonWindowByIdRequestedArgs
|
||||
{
|
||||
UInt64 WindowId { get; };
|
||||
};
|
||||
[default_interface] runtimeclass RequestMoveContentArgs
|
||||
{
|
||||
String Window { get; };
|
||||
@@ -50,6 +54,20 @@ namespace TerminalApp
|
||||
Microsoft.Terminal.Settings.Model.LaunchPosition Position;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass WindowListEntry
|
||||
{
|
||||
WindowListEntry();
|
||||
UInt64 Id;
|
||||
String Name;
|
||||
}
|
||||
|
||||
// Raised by TerminalPage when it needs the list of open windows.
|
||||
// The handler (AppHost) fills Entries synchronously.
|
||||
[default_interface] runtimeclass WindowListRequest
|
||||
{
|
||||
Windows.Foundation.Collections.IVector<WindowListEntry> Entries { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, Microsoft.Terminal.UI.IDirectKeyListener
|
||||
{
|
||||
TerminalPage(WindowProperties properties, ContentManager manager);
|
||||
@@ -93,6 +111,7 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowByIdRequestedArgs> SummonWindowByIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.WindowSizeChangedEventArgs> WindowSizeChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
|
||||
@@ -103,5 +122,6 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, LaunchPositionRequest> RequestLaunchPosition;
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowListRequest> RequestWindowList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,17 +144,17 @@
|
||||
</TextBlock>
|
||||
</ContentDialog>
|
||||
|
||||
<ContentDialog x:Name="CouldNotOpenUriDialog"
|
||||
x:Uid="CouldNotOpenUriDialog"
|
||||
<ContentDialog x:Name="UriErrorDialog"
|
||||
x:Uid="UriErrorDialog"
|
||||
Grid.Row="2"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary">
|
||||
DefaultButton="Close">
|
||||
<TextBlock IsTextSelectionEnabled="True"
|
||||
TextWrapping="WrapWholeWords">
|
||||
<TextBlock.ContextFlyout>
|
||||
<mtu:TextMenuFlyout />
|
||||
</TextBlock.ContextFlyout>
|
||||
<Run x:Name="CouldNotOpenUriReason" /> <LineBreak />
|
||||
<Run x:Name="CouldNotOpenUriReason" /> <LineBreak /> <LineBreak />
|
||||
<Run x:Name="UnopenedUri"
|
||||
FontFamily="Cascadia Mono" />
|
||||
</TextBlock>
|
||||
|
||||
@@ -252,6 +252,15 @@ namespace winrt::TerminalApp::implementation
|
||||
AppLogic::Current()->NotifyRootInitialized();
|
||||
}
|
||||
|
||||
WindowLayout TerminalWindow::GetWindowLayout()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
return _root->GetWindowLayout();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TerminalWindow::PersistState()
|
||||
{
|
||||
if (_root)
|
||||
@@ -1097,6 +1106,11 @@ namespace winrt::TerminalApp::implementation
|
||||
_initialContentArgs = wil::to_vector(args);
|
||||
}
|
||||
|
||||
void TerminalWindow::SetPersistedLayout(const winrt::Microsoft::Terminal::Settings::Model::WindowLayout& layout)
|
||||
{
|
||||
_cachedLayout = layout;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Parse the provided commandline arguments into actions, and try to
|
||||
// perform them immediately.
|
||||
@@ -1208,7 +1222,14 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalWindow::WindowName(const winrt::hstring& name)
|
||||
{
|
||||
const auto oldIsQuakeMode = _WindowProperties->IsQuakeWindow();
|
||||
const auto oldName = _WindowProperties->WindowName();
|
||||
_WindowProperties->WindowName(name);
|
||||
// If this window had a persisted workspace under the old name, rename
|
||||
// that entry too so we don't leave a stale copy behind.
|
||||
if (!oldName.empty() && !name.empty() && oldName != name)
|
||||
{
|
||||
ApplicationState::SharedInstance().RenameWorkspace(oldName, name);
|
||||
}
|
||||
if (!_root)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void Create();
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
|
||||
void PersistState();
|
||||
|
||||
void UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args);
|
||||
@@ -79,6 +80,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
int32_t SetStartupCommandline(TerminalApp::CommandlineArgs args);
|
||||
void SetStartupContent(const winrt::hstring& content, const Windows::Foundation::IReference<Windows::Foundation::Rect>& contentBounds);
|
||||
void SetPersistedLayout(const winrt::Microsoft::Terminal::Settings::Model::WindowLayout& layout);
|
||||
int32_t ExecuteCommandline(TerminalApp::CommandlineArgs args);
|
||||
void SetSettingsStartupArgs(const std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
|
||||
|
||||
@@ -221,6 +223,7 @@ namespace winrt::TerminalApp::implementation
|
||||
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
|
||||
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
|
||||
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
|
||||
FORWARDED_TYPED_EVENT(SummonWindowByIdRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::SummonWindowByIdRequestedArgs, _root, SummonWindowByIdRequested);
|
||||
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
|
||||
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
|
||||
FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged);
|
||||
@@ -229,6 +232,7 @@ namespace winrt::TerminalApp::implementation
|
||||
FORWARDED_TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs, _root, RequestReceiveContent);
|
||||
|
||||
FORWARDED_TYPED_EVENT(RequestLaunchPosition, Windows::Foundation::IInspectable, winrt::TerminalApp::LaunchPositionRequest, _root, RequestLaunchPosition);
|
||||
FORWARDED_TYPED_EVENT(RequestWindowList, Windows::Foundation::IInspectable, winrt::TerminalApp::WindowListRequest, _root, RequestWindowList);
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TerminalAppLocalTests::CommandlineTest;
|
||||
|
||||
@@ -55,11 +55,13 @@ namespace TerminalApp
|
||||
|
||||
Int32 SetStartupCommandline(CommandlineArgs args);
|
||||
void SetStartupContent(String json, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
|
||||
void SetPersistedLayout(Microsoft.Terminal.Settings.Model.WindowLayout layout);
|
||||
Int32 ExecuteCommandline(CommandlineArgs args);
|
||||
|
||||
Boolean ShouldImmediatelyHandoffToElevated();
|
||||
void HandoffToElevated();
|
||||
|
||||
Microsoft.Terminal.Settings.Model.WindowLayout GetWindowLayout();
|
||||
void PersistState();
|
||||
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
@@ -126,6 +128,7 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowByIdRequestedArgs> SummonWindowByIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
|
||||
@@ -137,6 +140,7 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LaunchPositionRequest> RequestLaunchPosition;
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowListRequest> RequestWindowList;
|
||||
|
||||
void AttachContent(String content, UInt32 tabIndex);
|
||||
void SendContentToOther(RequestReceiveContentArgs args);
|
||||
|
||||
@@ -1935,8 +1935,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - args: event data
|
||||
void TermControl::_TappedHandler(const IInspectable& /*sender*/, const TappedRoutedEventArgs& e)
|
||||
{
|
||||
Focus(FocusState::Pointer);
|
||||
|
||||
if (e.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
|
||||
{
|
||||
// Normally TSF would be responsible for showing the touch keyboard, but it's buggy for us:
|
||||
@@ -2908,8 +2906,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do we ever get here (= uninitialized terminal)? If so: How?
|
||||
assert(false);
|
||||
return { 10, 10 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>Tippen, um Symbole zu filtern</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>Trennzeichen per Drag &amp; Drop</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>Dieser Text wird zwischen den Pfaden mehrerer in das Terminal gezogener Dateien eingefügt.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2610,19 +2610,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>Escriba para filtrar iconos</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>Delimitador de arrastrar y colocar</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>Este texto se insertará entre las rutas de acceso de varios archivos colocados en el terminal.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C :\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C :\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>Taper pour filtrer les icônes</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>Glisser-déplacer le délimiteur</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>Ce texte sera inséré entre les chemins d’accès de plusieurs fichiers déposés dans le terminal.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>Digita per filtrare icone</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>Trascina e rilascia il delimitatore</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>Questo testo verrà inserito tra i percorsi di più file trascinati nel terminale.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>入力してアイコンをフィルター処理します</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>ドラッグ アンド ドロップ区切り記号</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>このテキストは、ターミナルにドロップされた複数のファイルのパスの間に挿入されます。</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL(C:\ -> /mnt/c)</value>
|
||||
<value>WSL(C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin(C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin(C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2(C:\ -> /c)</value>
|
||||
<value>MSYS2(C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW(C:\ -> C:/)</value>
|
||||
<value>MinGW(C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>입력하여 아이콘 필터링</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>끌어서 놓기 구분 기호</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>이 텍스트는 터미널에 놓인 여러 파일의 경로 사이에 삽입됩니다.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>Digite para filtrar ícones</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>Delimitador de Arrastar e soltar</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>Este texto será inserido entre os caminhos de vários arquivos descartados no terminal.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2610,19 +2610,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c) !!! !!!</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c) !!! !!!</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c) !!! !!! !!</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c) !!! !!! !!</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c) !!! !!</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c) !!! !!</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/) !!! !!</value>
|
||||
<value>MinGW (C:\ 🡒 C:/) !!! !!</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2748,4 +2748,12 @@
|
||||
<value>Тÿρě ţθ ƒíŀŧēŗ īçōйš !!! !!!</value>
|
||||
<comment>Placeholder text for a text box to filter and select an icon.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="Profile_DragDropDelimiter.Header" xml:space="preserve">
|
||||
<value>Đґâġ ąńð δŗορ ďèŀιмïţ℮я !!! !!! </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>Тĥїś ťэхť ẃĭŀł вё îⁿŝέŗŧеď вēťщ℮ěπ τĥę ρªτħѕ óƒ мџĺţīрℓé ƒĭļèś đяǿρрεδ ιйţθ ţħê ţèřмĭлªŀ. !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -2610,19 +2610,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c) !!! !!!</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c) !!! !!!</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c) !!! !!! !!</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c) !!! !!! !!</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c) !!! !!</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c) !!! !!</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/) !!! !!</value>
|
||||
<value>MinGW (C:\ 🡒 C:/) !!! !!</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2748,4 +2748,12 @@
|
||||
<value>Тÿρě ţθ ƒíŀŧēŗ īçōйš !!! !!!</value>
|
||||
<comment>Placeholder text for a text box to filter and select an icon.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="Profile_DragDropDelimiter.Header" xml:space="preserve">
|
||||
<value>Đґâġ ąńð δŗορ ďèŀιмïţ℮я !!! !!! </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>Тĥїś ťэхť ẃĭŀł вё îⁿŝέŗŧеď вēťщ℮ěπ τĥę ρªτħѕ óƒ мџĺţīрℓé ƒĭļèś đяǿρрεδ ιйţθ ţħê ţèřмĭлªŀ. !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -2610,19 +2610,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c) !!! !!!</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c) !!! !!!</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c) !!! !!! !!</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c) !!! !!! !!</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c) !!! !!</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c) !!! !!</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/) !!! !!</value>
|
||||
<value>MinGW (C:\ 🡒 C:/) !!! !!</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2748,4 +2748,12 @@
|
||||
<value>Тÿρě ţθ ƒíŀŧēŗ īçōйš !!! !!!</value>
|
||||
<comment>Placeholder text for a text box to filter and select an icon.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="Profile_DragDropDelimiter.Header" xml:space="preserve">
|
||||
<value>Đґâġ ąńð δŗορ ďèŀιмïţ℮я !!! !!! </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>Тĥїś ťэхť ẃĭŀł вё îⁿŝέŗŧеď вēťщ℮ěπ τĥę ρªτħѕ óƒ мџĺţīрℓé ƒĭļèś đяǿρрεδ ιйţθ ţħê ţèřмĭлªŀ. !!! !!! !!! !!! !!! !!! !!! !!! !!!</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>Введите текст для фильтрации значков</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>Перетащите разделитель</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>Этот текст будет вставлен между путями нескольких файлов, перетащенных в терминал.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>键入以筛选图标</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>拖放分隔符</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>此文本将在放置到终端的多个文件的路径之间插入。</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2606,19 +2606,19 @@
|
||||
<comment>An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
|
||||
<value>WSL (C:\ -> /mnt/c)</value>
|
||||
<value>WSL (C:\ 🡒 /mnt/c)</value>
|
||||
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
|
||||
<value>Cygwin (C:\ -> /cygdrive/c)</value>
|
||||
<value>Cygwin (C:\ 🡒 /cygdrive/c)</value>
|
||||
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
|
||||
<value>MSYS2 (C:\ -> /c)</value>
|
||||
<value>MSYS2 (C:\ 🡒 /c)</value>
|
||||
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
|
||||
<value>MinGW (C:\ -> C:/)</value>
|
||||
<value>MinGW (C:\ 🡒 C:/)</value>
|
||||
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
|
||||
</data>
|
||||
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
|
||||
@@ -2744,4 +2744,12 @@
|
||||
<value>輸入以篩選圖示</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>拖放分隔符號</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>這些文字會插入多個丟入終端機的檔案路徑之間。</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -73,6 +73,8 @@ static constexpr std::string_view NewWindowKey{ "newWindow" };
|
||||
static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" };
|
||||
static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" };
|
||||
static constexpr std::string_view RenameWindowKey{ "renameWindow" };
|
||||
static constexpr std::string_view OpenWorkspaceKey{ "openWorkspace" };
|
||||
|
||||
static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
|
||||
static constexpr std::string_view DisplayWorkingDirectoryKey{ "debugTerminalCwd" };
|
||||
static constexpr std::string_view SearchForTextKey{ "searchWeb" };
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "PrevTabArgs.g.cpp"
|
||||
#include "NextTabArgs.g.cpp"
|
||||
#include "RenameWindowArgs.g.cpp"
|
||||
#include "OpenWorkspaceArgs.g.cpp"
|
||||
#include "SearchForTextArgs.g.cpp"
|
||||
#include "GlobalSummonArgs.g.cpp"
|
||||
#include "FocusPaneArgs.g.cpp"
|
||||
@@ -795,6 +796,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return RS_switchable_(L"ResetWindowNameCommandKey");
|
||||
}
|
||||
|
||||
winrt::hstring OpenWorkspaceArgs::GenerateName(const winrt::WARC::ResourceContext& context) const
|
||||
{
|
||||
if (!Name().empty())
|
||||
{
|
||||
return winrt::hstring{ RS_switchable_fmt(L"OpenWorkspaceCommandKey", Name()) };
|
||||
}
|
||||
return RS_switchable_(L"OpenWorkspaceDefaultCommandKey");
|
||||
}
|
||||
|
||||
winrt::hstring SearchForTextArgs::GenerateName(const winrt::WARC::ResourceContext& context) const
|
||||
{
|
||||
if (QueryUrl().empty())
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "PrevTabArgs.g.h"
|
||||
#include "NextTabArgs.g.h"
|
||||
#include "RenameWindowArgs.g.h"
|
||||
#include "OpenWorkspaceArgs.g.h"
|
||||
#include "SearchForTextArgs.g.h"
|
||||
#include "GlobalSummonArgs.g.h"
|
||||
#include "FocusPaneArgs.g.h"
|
||||
@@ -246,6 +247,10 @@ protected: \
|
||||
#define RENAME_WINDOW_ARGS(X) \
|
||||
X(winrt::hstring, Name, "name", false, ArgTypeHint::None, L"")
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define OPEN_WORKSPACE_ARGS(X) \
|
||||
X(winrt::hstring, Name, "name", false, ArgTypeHint::None, L"")
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define SEARCH_FOR_TEXT_ARGS(X) \
|
||||
X(winrt::hstring, QueryUrl, "queryUrl", false, ArgTypeHint::None, L"")
|
||||
@@ -940,6 +945,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
ACTION_ARGS_STRUCT(RenameWindowArgs, RENAME_WINDOW_ARGS);
|
||||
|
||||
ACTION_ARGS_STRUCT(OpenWorkspaceArgs, OPEN_WORKSPACE_ARGS);
|
||||
|
||||
ACTION_ARGS_STRUCT(SearchForTextArgs, SEARCH_FOR_TEXT_ARGS);
|
||||
|
||||
struct GlobalSummonArgs : public GlobalSummonArgsT<GlobalSummonArgs>
|
||||
@@ -1059,6 +1066,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
BASIC_FACTORY(SetMaximizedArgs);
|
||||
BASIC_FACTORY(SetColorSchemeArgs);
|
||||
BASIC_FACTORY(RenameWindowArgs);
|
||||
BASIC_FACTORY(OpenWorkspaceArgs);
|
||||
BASIC_FACTORY(ExecuteCommandlineArgs);
|
||||
BASIC_FACTORY(CloseOtherTabsArgs);
|
||||
BASIC_FACTORY(CloseTabsAfterArgs);
|
||||
|
||||
@@ -420,6 +420,12 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
String Name { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass OpenWorkspaceArgs : IActionArgs, IActionArgsDescriptorAccess
|
||||
{
|
||||
OpenWorkspaceArgs(String name);
|
||||
String Name { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SearchForTextArgs : IActionArgs, IActionArgsDescriptorAccess
|
||||
{
|
||||
String QueryUrl { get; };
|
||||
|
||||
@@ -113,7 +113,8 @@
|
||||
ON_ALL_ACTIONS(OpenScratchpad) \
|
||||
ON_ALL_ACTIONS(OpenAbout) \
|
||||
ON_ALL_ACTIONS(QuickFix) \
|
||||
ON_ALL_ACTIONS(OpenCWD)
|
||||
ON_ALL_ACTIONS(OpenCWD) \
|
||||
ON_ALL_ACTIONS(OpenWorkspace)
|
||||
|
||||
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
|
||||
@@ -158,7 +159,8 @@
|
||||
ON_ALL_ACTIONS_WITH_ARGS(Suggestions) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(SelectCommand) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(SelectOutput) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(ColorSelection)
|
||||
ON_ALL_ACTIONS_WITH_ARGS(ColorSelection) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(OpenWorkspace)
|
||||
|
||||
// These two macros here are for actions that we only use as internal currency.
|
||||
// They don't need to be parsed by the settings model, or saved as actions to
|
||||
|
||||
@@ -20,6 +20,7 @@ static constexpr std::string_view TabLayoutKey{ "tabLayout" };
|
||||
static constexpr std::string_view InitialPositionKey{ "initialPosition" };
|
||||
static constexpr std::string_view InitialSizeKey{ "initialSize" };
|
||||
static constexpr std::string_view LaunchModeKey{ "launchMode" };
|
||||
static constexpr std::string_view PersistedWorkspacesKey{ "persistedWorkspaces" };
|
||||
|
||||
namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
{
|
||||
@@ -276,6 +277,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
|
||||
// Manually handled because IMap<K,V> has a comma that breaks the X-macro.
|
||||
if (WI_IsFlagSet(parseSource, FileSource::Local))
|
||||
state->PersistedWorkspaces = JsonUtils::GetValueForKey<std::optional<Windows::Foundation::Collections::IMap<hstring, Model::WindowLayout>>>(root, PersistedWorkspacesKey);
|
||||
}
|
||||
|
||||
Json::Value ApplicationState::ToJson(FileSource parseSource) const noexcept
|
||||
@@ -298,6 +303,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
|
||||
// Manually handled because IMap<K,V> has a comma that breaks the X-macro.
|
||||
if (WI_IsFlagSet(parseSource, FileSource::Local))
|
||||
JsonUtils::SetValueForKey(root, PersistedWorkspacesKey, state->PersistedWorkspaces);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -341,6 +350,114 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApplicationState::SaveWorkspace(const hstring& name, const Model::WindowLayout& layout)
|
||||
{
|
||||
{
|
||||
const auto state = _state.lock();
|
||||
if (!state->PersistedWorkspaces || !*state->PersistedWorkspaces)
|
||||
{
|
||||
state->PersistedWorkspaces = winrt::single_threaded_map<hstring, Model::WindowLayout>();
|
||||
}
|
||||
(*state->PersistedWorkspaces).Insert(name, layout);
|
||||
}
|
||||
_throttler();
|
||||
}
|
||||
|
||||
bool ApplicationState::RemoveWorkspace(const hstring& name)
|
||||
{
|
||||
bool removed{ false };
|
||||
{
|
||||
const auto state = _state.lock();
|
||||
if (state->PersistedWorkspaces && *state->PersistedWorkspaces)
|
||||
{
|
||||
auto map = *state->PersistedWorkspaces;
|
||||
if (map.HasKey(name))
|
||||
{
|
||||
map.Remove(name);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
{
|
||||
_throttler();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Rename a persisted workspace entry from oldName to newName. If there
|
||||
// was no entry for oldName, this is a no-op. If an entry for newName
|
||||
// already exists, it will be overwritten with the layout from oldName.
|
||||
// Return Value:
|
||||
// - true if an entry was renamed, false otherwise.
|
||||
bool ApplicationState::RenameWorkspace(const hstring& oldName, const hstring& newName)
|
||||
{
|
||||
if (oldName == newName || oldName.empty() || newName.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool renamed{ false };
|
||||
{
|
||||
const auto state = _state.lock();
|
||||
if (state->PersistedWorkspaces && *state->PersistedWorkspaces)
|
||||
{
|
||||
auto map = *state->PersistedWorkspaces;
|
||||
if (map.HasKey(oldName))
|
||||
{
|
||||
const auto layout = map.Lookup(oldName);
|
||||
map.Insert(newName, layout);
|
||||
map.Remove(oldName);
|
||||
renamed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (renamed)
|
||||
{
|
||||
_throttler();
|
||||
}
|
||||
return renamed;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Atomically remove and return a persisted workspace entry. This is the
|
||||
// intended API for the startup path that restores a named workspace,
|
||||
// because it guarantees only one caller can claim a given workspace.
|
||||
// Return Value:
|
||||
// - The layout that was stored under `name`, or nullptr if there was none.
|
||||
Model::WindowLayout ApplicationState::TakeWorkspace(const hstring& name)
|
||||
{
|
||||
Model::WindowLayout result{ nullptr };
|
||||
{
|
||||
const auto state = _state.lock();
|
||||
if (state->PersistedWorkspaces && *state->PersistedWorkspaces)
|
||||
{
|
||||
auto map = *state->PersistedWorkspaces;
|
||||
if (map.HasKey(name))
|
||||
{
|
||||
result = map.Lookup(name);
|
||||
map.Remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
{
|
||||
_throttler();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::WindowLayout> ApplicationState::AllPersistedWorkspaces()
|
||||
{
|
||||
const auto state = _state.lock_shared();
|
||||
if (state->PersistedWorkspaces && *state->PersistedWorkspaces)
|
||||
{
|
||||
return (*state->PersistedWorkspaces).GetView();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Generate all getter/setters
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
type ApplicationState::name() const noexcept \
|
||||
|
||||
@@ -75,6 +75,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
bool DismissBadge(const hstring& badgeId);
|
||||
bool BadgeDismissed(const hstring& badgeId) const;
|
||||
|
||||
void SaveWorkspace(const hstring& name, const Model::WindowLayout& layout);
|
||||
bool RemoveWorkspace(const hstring& name);
|
||||
bool RenameWorkspace(const hstring& oldName, const hstring& newName);
|
||||
Model::WindowLayout TakeWorkspace(const hstring& name);
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::WindowLayout> AllPersistedWorkspaces();
|
||||
|
||||
// State getters/setters
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
type name() const noexcept; \
|
||||
@@ -88,6 +94,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) std::optional<type> name{ __VA_ARGS__ };
|
||||
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
|
||||
#undef MTSM_APPLICATION_STATE_GEN
|
||||
// Manually declared because IMap<K,V> has a comma that breaks the macro.
|
||||
std::optional<Windows::Foundation::Collections::IMap<hstring, Model::WindowLayout>> PersistedWorkspaces;
|
||||
};
|
||||
til::shared_mutex<state_t> _state;
|
||||
std::filesystem::path _sharedPath;
|
||||
|
||||
@@ -36,6 +36,12 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Boolean DismissBadge(String badgeId);
|
||||
Boolean BadgeDismissed(String badgeId);
|
||||
|
||||
void SaveWorkspace(String name, WindowLayout layout);
|
||||
Boolean RemoveWorkspace(String name);
|
||||
Boolean RenameWorkspace(String oldName, String newName);
|
||||
WindowLayout TakeWorkspace(String name);
|
||||
Windows.Foundation.Collections.IMapView<String, WindowLayout> AllPersistedWorkspaces();
|
||||
|
||||
String SettingsHash;
|
||||
Windows.Foundation.Collections.IVector<WindowLayout> PersistedWindowLayouts;
|
||||
Windows.Foundation.Collections.IVector<String> RecentCommands;
|
||||
|
||||
@@ -160,7 +160,8 @@ Author(s):
|
||||
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Frame, "frame", nullptr) \
|
||||
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedFrame, "unfocusedFrame", nullptr) \
|
||||
X(bool, RainbowFrame, "experimental.rainbowFrame", false) \
|
||||
X(bool, UseMica, "useMica", false)
|
||||
X(bool, UseMica, "useMica", false) \
|
||||
X(bool, ShowWindowsButton, "showWindowsButton", true)
|
||||
|
||||
#define MTSM_THEME_SETTINGS_SETTINGS(X) \
|
||||
X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "theme", winrt::Windows::UI::Xaml::ElementTheme::Default)
|
||||
|
||||
@@ -518,6 +518,13 @@
|
||||
<data name="ResetWindowNameCommandKey" xml:space="preserve">
|
||||
<value>Reset window name</value>
|
||||
</data>
|
||||
<data name="OpenWorkspaceCommandKey" xml:space="preserve">
|
||||
<value>Open workspace "{0}"</value>
|
||||
<comment>{0} will be replaced with the workspace name</comment>
|
||||
</data>
|
||||
<data name="OpenWorkspaceDefaultCommandKey" xml:space="preserve">
|
||||
<value>Open workspace</value>
|
||||
</data>
|
||||
<data name="OpenWindowRenamerCommandKey" xml:space="preserve">
|
||||
<value>Rename window...</value>
|
||||
</data>
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
|
||||
Boolean UseMica { get; };
|
||||
Boolean RainbowFrame { get; };
|
||||
Boolean ShowWindowsButton { get; };
|
||||
ThemeColor Frame { get; };
|
||||
ThemeColor UnfocusedFrame { get; };
|
||||
}
|
||||
|
||||
127
src/cascadia/UnitTests_SettingsModel/ApplicationStateTests.cpp
Normal file
127
src/cascadia/UnitTests_SettingsModel/ApplicationStateTests.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/ApplicationState.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace SettingsModelUnitTests
|
||||
{
|
||||
// Covers the workspace-persistence APIs added to ApplicationState:
|
||||
// SaveWorkspace / RemoveWorkspace / RenameWorkspace / TakeWorkspace /
|
||||
// AllPersistedWorkspaces.
|
||||
// All tests operate on a throw-away ApplicationState instance pointed at
|
||||
// a temp directory, so they don't touch the real user state.
|
||||
class ApplicationStateTests
|
||||
{
|
||||
TEST_CLASS(ApplicationStateTests);
|
||||
|
||||
TEST_METHOD(SaveAndLookupWorkspace);
|
||||
TEST_METHOD(RemoveWorkspaceReturnsFalseWhenMissing);
|
||||
TEST_METHOD(RenameWorkspaceMigratesEntry);
|
||||
TEST_METHOD(RenameWorkspaceNoOpForEmptyOrEqualNames);
|
||||
TEST_METHOD(RenameWorkspaceNoOpForMissingEntry);
|
||||
TEST_METHOD(TakeWorkspaceRemovesAndReturns);
|
||||
TEST_METHOD(TakeWorkspaceReturnsNullWhenMissing);
|
||||
|
||||
private:
|
||||
static std::filesystem::path _tempRoot()
|
||||
{
|
||||
auto root = std::filesystem::temp_directory_path() / L"WT_ApplicationStateTests";
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(root, ec);
|
||||
// Best-effort clean of any leftover state.json from a prior run so
|
||||
// tests see an empty starting point.
|
||||
std::filesystem::remove(root / L"state.json", ec);
|
||||
std::filesystem::remove(root / L"elevated-state.json", ec);
|
||||
return root;
|
||||
}
|
||||
|
||||
static winrt::com_ptr<implementation::ApplicationState> _make()
|
||||
{
|
||||
return winrt::make_self<implementation::ApplicationState>(_tempRoot());
|
||||
}
|
||||
|
||||
static WindowLayout _makeLayout()
|
||||
{
|
||||
WindowLayout layout;
|
||||
layout.TabLayout(winrt::single_threaded_vector<ActionAndArgs>());
|
||||
return layout;
|
||||
}
|
||||
};
|
||||
|
||||
void ApplicationStateTests::SaveAndLookupWorkspace()
|
||||
{
|
||||
auto state = _make();
|
||||
const auto layout = _makeLayout();
|
||||
state->SaveWorkspace(L"win1", layout);
|
||||
|
||||
const auto all = state->AllPersistedWorkspaces();
|
||||
VERIFY_IS_NOT_NULL(all);
|
||||
VERIFY_IS_TRUE(all.HasKey(L"win1"));
|
||||
}
|
||||
|
||||
void ApplicationStateTests::RemoveWorkspaceReturnsFalseWhenMissing()
|
||||
{
|
||||
auto state = _make();
|
||||
VERIFY_IS_FALSE(state->RemoveWorkspace(L"does-not-exist"));
|
||||
|
||||
state->SaveWorkspace(L"win1", _makeLayout());
|
||||
VERIFY_IS_TRUE(state->RemoveWorkspace(L"win1"));
|
||||
VERIFY_IS_FALSE(state->RemoveWorkspace(L"win1"));
|
||||
}
|
||||
|
||||
void ApplicationStateTests::RenameWorkspaceMigratesEntry()
|
||||
{
|
||||
auto state = _make();
|
||||
state->SaveWorkspace(L"oldName", _makeLayout());
|
||||
|
||||
VERIFY_IS_TRUE(state->RenameWorkspace(L"oldName", L"newName"));
|
||||
|
||||
const auto all = state->AllPersistedWorkspaces();
|
||||
VERIFY_IS_NOT_NULL(all);
|
||||
VERIFY_IS_FALSE(all.HasKey(L"oldName"));
|
||||
VERIFY_IS_TRUE(all.HasKey(L"newName"));
|
||||
}
|
||||
|
||||
void ApplicationStateTests::RenameWorkspaceNoOpForEmptyOrEqualNames()
|
||||
{
|
||||
auto state = _make();
|
||||
state->SaveWorkspace(L"win1", _makeLayout());
|
||||
|
||||
VERIFY_IS_FALSE(state->RenameWorkspace(L"win1", L"win1"));
|
||||
VERIFY_IS_FALSE(state->RenameWorkspace(L"", L"win2"));
|
||||
VERIFY_IS_FALSE(state->RenameWorkspace(L"win1", L""));
|
||||
}
|
||||
|
||||
void ApplicationStateTests::RenameWorkspaceNoOpForMissingEntry()
|
||||
{
|
||||
auto state = _make();
|
||||
VERIFY_IS_FALSE(state->RenameWorkspace(L"missing", L"newName"));
|
||||
}
|
||||
|
||||
void ApplicationStateTests::TakeWorkspaceRemovesAndReturns()
|
||||
{
|
||||
auto state = _make();
|
||||
state->SaveWorkspace(L"win1", _makeLayout());
|
||||
|
||||
const auto taken = state->TakeWorkspace(L"win1");
|
||||
VERIFY_IS_NOT_NULL(taken);
|
||||
|
||||
// Subsequent Take for the same name must return null — this is the
|
||||
// atomicity guarantee the startup path relies on.
|
||||
VERIFY_IS_NULL(state->TakeWorkspace(L"win1"));
|
||||
}
|
||||
|
||||
void ApplicationStateTests::TakeWorkspaceReturnsNullWhenMissing()
|
||||
{
|
||||
auto state = _make();
|
||||
VERIFY_IS_NULL(state->TakeWorkspace(L"missing"));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -105,9 +105,10 @@ namespace SettingsModelUnitTests
|
||||
TEST_METHOD(RealResolverUrlCases);
|
||||
TEST_METHOD(RealResolverUNCCases);
|
||||
|
||||
static constexpr std::wstring_view pingCommandline{ LR"(C:\Windows\System32\PING.EXE)" }; // Normalized by Profile (this is the casing that Windows stores on disk)
|
||||
static constexpr std::wstring_view overrideCommandline{ LR"(C:\Windows\System32\cscript.exe)" };
|
||||
static constexpr std::wstring_view cmdCommandline{ LR"(C:\Windows\System32\cmd.exe)" }; // The default commandline for a profile
|
||||
// These are normalized by NormalizeCommandLine, which resolves to the on-disk casing.
|
||||
// They are used in test cases where media paths fall back to profile command lines.
|
||||
static inline std::wstring overrideCommandline;
|
||||
static inline std::wstring cmdCommandline;
|
||||
static constexpr std::wstring_view fragmentBasePath1{ LR"(C:\Windows\Media)" };
|
||||
|
||||
private:
|
||||
@@ -218,6 +219,9 @@ namespace SettingsModelUnitTests
|
||||
// Some of our tests use paths under system32. Just don't redirect them.
|
||||
Wow64DisableWow64FsRedirection(&redirectionFlag);
|
||||
#endif
|
||||
// Normalize these AFTER the call above so that we get the correctly redirected paths.
|
||||
overrideCommandline = implementation::Profile::NormalizeCommandLine(LR"(C:\Windows\System32\cscript.exe)");
|
||||
cmdCommandline = implementation::Profile::NormalizeCommandLine(LR"(C:\Windows\System32\cmd.exe)");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
<ClCompile Include="TerminalSettingsTests.cpp" />
|
||||
<ClCompile Include="ThemeTests.cpp" />
|
||||
<ClCompile Include="MediaResourceTests.cpp" />
|
||||
<ClCompile Include="WindowSettingsTests.cpp" />
|
||||
<ClCompile Include="ApplicationStateTests.cpp" />
|
||||
<ClCompile Include="../TerminalSettingsAppAdapterLib/TerminalSettings.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
|
||||
@@ -132,7 +132,12 @@ void AppHost::_HandleCommandlineArgs(const winrt::TerminalApp::WindowRequestedAr
|
||||
// We don't have XAML yet, but we do have other stuff.
|
||||
_windowLogic = _appLogic.CreateNewWindow();
|
||||
|
||||
if (const auto content = windowArgs.Content(); !content.empty())
|
||||
if (const auto layout = windowArgs.PersistedLayout())
|
||||
{
|
||||
_windowLogic.SetPersistedLayout(layout);
|
||||
_launchShowWindowCommand = SW_NORMAL;
|
||||
}
|
||||
else if (const auto content = windowArgs.Content(); !content.empty())
|
||||
{
|
||||
_windowLogic.SetStartupContent(content, windowArgs.InitialBounds());
|
||||
_launchShowWindowCommand = SW_NORMAL;
|
||||
@@ -265,11 +270,13 @@ void AppHost::Initialize()
|
||||
|
||||
_revokers.IsQuakeWindowChanged = _windowLogic.IsQuakeWindowChanged(winrt::auto_revoke, { this, &AppHost::_IsQuakeWindowChanged });
|
||||
_revokers.SummonWindowRequested = _windowLogic.SummonWindowRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowRequested });
|
||||
_revokers.SummonWindowByIdRequested = _windowLogic.SummonWindowByIdRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowByIdRequested });
|
||||
_revokers.OpenSystemMenu = _windowLogic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu });
|
||||
_revokers.QuitRequested = _windowLogic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll });
|
||||
_revokers.ShowWindowChanged = _windowLogic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged });
|
||||
_revokers.RequestMoveContent = _windowLogic.RequestMoveContent(winrt::auto_revoke, { this, &AppHost::_handleMoveContent });
|
||||
_revokers.RequestReceiveContent = _windowLogic.RequestReceiveContent(winrt::auto_revoke, { this, &AppHost::_handleReceiveContent });
|
||||
_revokers.RequestWindowList = _windowLogic.RequestWindowList(winrt::auto_revoke, { this, &AppHost::_HandleRequestWindowList });
|
||||
|
||||
// BODGY
|
||||
// On certain builds of Windows, when Terminal is set as the default
|
||||
@@ -409,6 +416,28 @@ void AppHost::_HandleRequestLaunchPosition(const winrt::Windows::Foundation::IIn
|
||||
args.Position(_GetWindowLaunchPosition());
|
||||
}
|
||||
|
||||
void AppHost::_HandleRequestWindowList(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
winrt::TerminalApp::WindowListRequest args)
|
||||
{
|
||||
// Ask the Emperor (on the main thread) for the current window list.
|
||||
// SendMessage blocks until the message is processed, so this is
|
||||
// synchronous and the results vector is filled in-place.
|
||||
std::vector<WindowEmperor::WindowListEntry> entries;
|
||||
SendMessage(_windowManager->GetMainWindow(),
|
||||
WindowEmperor::WM_GET_WINDOW_LIST,
|
||||
0,
|
||||
reinterpret_cast<LPARAM>(&entries));
|
||||
|
||||
auto windowEntries = args.Entries();
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
winrt::TerminalApp::WindowListEntry wle;
|
||||
wle.Id(entry.Id);
|
||||
wle.Name(winrt::hstring{ entry.Name });
|
||||
windowEntries.Append(wle);
|
||||
}
|
||||
}
|
||||
|
||||
LaunchPosition AppHost::_GetWindowLaunchPosition()
|
||||
{
|
||||
LaunchPosition pos{};
|
||||
@@ -1064,6 +1093,23 @@ void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspecta
|
||||
HandleSummon(std::move(summonArgs));
|
||||
}
|
||||
|
||||
void AppHost::_SummonWindowByIdRequested(const winrt::Windows::Foundation::IInspectable&,
|
||||
const winrt::TerminalApp::SummonWindowByIdRequestedArgs& args)
|
||||
{
|
||||
// Summon the window by its ID without creating a new tab.
|
||||
// We look up the target window in WindowEmperor and call HandleSummon directly.
|
||||
const auto targetId = args.WindowId();
|
||||
if (auto* targetWindow = _windowManager->GetWindowById(targetId))
|
||||
{
|
||||
winrt::TerminalApp::SummonWindowBehavior summonBehavior;
|
||||
summonBehavior.MoveToCurrentDesktop(false);
|
||||
summonBehavior.DropdownDuration(0);
|
||||
summonBehavior.ToMonitor(winrt::TerminalApp::MonitorBehavior::InPlace);
|
||||
summonBehavior.ToggleVisibility(false); // Do not toggle, just make visible.
|
||||
targetWindow->HandleSummon(std::move(summonBehavior));
|
||||
}
|
||||
}
|
||||
|
||||
void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&,
|
||||
const winrt::Windows::Foundation::IInspectable&)
|
||||
{
|
||||
|
||||
@@ -91,6 +91,9 @@ private:
|
||||
void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
void _SummonWindowByIdRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::TerminalApp::SummonWindowByIdRequestedArgs& args);
|
||||
|
||||
void _OpenSystemMenu(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
@@ -130,6 +133,8 @@ private:
|
||||
void _AppTitleChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&);
|
||||
void _HandleRequestLaunchPosition(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
winrt::TerminalApp::LaunchPositionRequest args);
|
||||
void _HandleRequestWindowList(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
winrt::TerminalApp::WindowListRequest args);
|
||||
|
||||
// Helper struct. By putting these all into one struct, we can revoke them
|
||||
// all at once, by assigning _revokers to a fresh Revokers instance. That'll
|
||||
@@ -151,12 +156,14 @@ private:
|
||||
winrt::TerminalApp::TerminalWindow::IdentifyWindowsRequested_revoker IdentifyWindowsRequested;
|
||||
winrt::TerminalApp::TerminalWindow::IsQuakeWindowChanged_revoker IsQuakeWindowChanged;
|
||||
winrt::TerminalApp::TerminalWindow::SummonWindowRequested_revoker SummonWindowRequested;
|
||||
winrt::TerminalApp::TerminalWindow::SummonWindowByIdRequested_revoker SummonWindowByIdRequested;
|
||||
winrt::TerminalApp::TerminalWindow::OpenSystemMenu_revoker OpenSystemMenu;
|
||||
winrt::TerminalApp::TerminalWindow::QuitRequested_revoker QuitRequested;
|
||||
winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged;
|
||||
winrt::TerminalApp::TerminalWindow::RequestMoveContent_revoker RequestMoveContent;
|
||||
winrt::TerminalApp::TerminalWindow::RequestReceiveContent_revoker RequestReceiveContent;
|
||||
winrt::TerminalApp::TerminalWindow::RequestLaunchPosition_revoker RequestLaunchPosition;
|
||||
winrt::TerminalApp::TerminalWindow::RequestWindowList_revoker RequestWindowList;
|
||||
winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged;
|
||||
winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged;
|
||||
winrt::TerminalApp::TerminalWindow::WindowSizeChanged_revoker WindowSizeChanged;
|
||||
|
||||
@@ -678,6 +678,19 @@ void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs arg
|
||||
{
|
||||
winrt::TerminalApp::WindowRequestedArgs request{ windowId, std::move(args) };
|
||||
request.WindowName(std::move(windowName));
|
||||
|
||||
// If we're opening a named window that doesn't exist yet, atomically
|
||||
// claim any persisted workspace with that name so we restore it here
|
||||
// and no subsequent window can pick up the same entry.
|
||||
const auto& reqName = request.WindowName();
|
||||
if (!reqName.empty())
|
||||
{
|
||||
if (const auto layout = ApplicationState::SharedInstance().TakeWorkspace(reqName))
|
||||
{
|
||||
request.PersistedLayout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
CreateNewWindow(std::move(request));
|
||||
}
|
||||
}
|
||||
@@ -943,6 +956,22 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
|
||||
// anyway (since we threw and exited this message handler) so this at least gives back our
|
||||
// deterministic window count management.
|
||||
const auto strong = *it;
|
||||
|
||||
// Before destroying a named window, persist its full
|
||||
// tab/buffer state as a workspace so it can be restored later.
|
||||
try
|
||||
{
|
||||
const auto windowName = strong->Logic().WindowProperties().WindowName();
|
||||
if (!windowName.empty())
|
||||
{
|
||||
if (const auto layout = strong->Logic().GetWindowLayout())
|
||||
{
|
||||
ApplicationState::SharedInstance().SaveWorkspace(windowName, layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
_windows.erase(it);
|
||||
try
|
||||
{
|
||||
@@ -970,6 +999,19 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
|
||||
host->Logic().IdentifyWindow();
|
||||
}
|
||||
return 0;
|
||||
case WM_GET_WINDOW_LIST:
|
||||
{
|
||||
auto* result = reinterpret_cast<std::vector<WindowListEntry>*>(lParam);
|
||||
if (result)
|
||||
{
|
||||
for (const auto& host : _windows)
|
||||
{
|
||||
const auto props = host->Logic().WindowProperties();
|
||||
result->emplace_back(WindowListEntry{ props.WindowId(), std::wstring{ props.WindowName() } });
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_NOTIFY_FROM_NOTIFICATION_AREA:
|
||||
switch (LOWORD(lParam))
|
||||
{
|
||||
|
||||
@@ -28,6 +28,16 @@ public:
|
||||
WM_MESSAGE_BOX_CLOSED,
|
||||
WM_IDENTIFY_ALL_WINDOWS,
|
||||
WM_NOTIFY_FROM_NOTIFICATION_AREA,
|
||||
WM_GET_WINDOW_LIST,
|
||||
};
|
||||
|
||||
// Used by WM_GET_WINDOW_LIST. Callers allocate a vector on their
|
||||
// stack and pass a pointer through LPARAM; the emperor fills it in
|
||||
// synchronously via SendMessage.
|
||||
struct WindowListEntry
|
||||
{
|
||||
uint64_t Id;
|
||||
std::wstring Name;
|
||||
};
|
||||
|
||||
HWND GetMainWindow() const noexcept;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ void FontTests::TestCurrentFontAPIsInvalid()
|
||||
}
|
||||
else
|
||||
{
|
||||
hConsoleOutput = (HANDLE)dwConsoleOutput;
|
||||
hConsoleOutput = ULongToHandle(dwConsoleOutput);
|
||||
}
|
||||
|
||||
if (strOperation == L"Get")
|
||||
@@ -107,7 +107,7 @@ void FontTests::TestGetFontSizeInvalid()
|
||||
// Need to make sure that last error is cleared so that we can verify that lasterror was set by GetConsoleFontSize
|
||||
SetLastError(0);
|
||||
|
||||
auto coordFontSize = OneCoreDelay::GetConsoleFontSize((HANDLE)dwConsoleOutput, 0);
|
||||
auto coordFontSize = OneCoreDelay::GetConsoleFontSize(ULongToHandle(dwConsoleOutput), 0);
|
||||
VERIFY_ARE_EQUAL(coordFontSize, c_coordZero, L"Ensure (0,0) coord returned to indicate failure");
|
||||
VERIFY_ARE_EQUAL(GetLastError(), (DWORD)ERROR_INVALID_HANDLE, L"Ensure last error was set appropriately");
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ void ConhostInternalGetSet::UnknownSequence() noexcept
|
||||
// us from using a more conservative solution (e.g. always fetching the cursor position).
|
||||
if (gci.IsInVtIoMode())
|
||||
{
|
||||
gci.GetActiveOutputBuffer().SetConptyCursorPositionMayBeWrong();
|
||||
gci.GetActiveOutputBuffer().GetActiveBuffer().SetConptyCursorPositionMayBeWrong();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
constexpr bool ends_with_insensitive_ascii(const std::basic_string_view<T, Traits>& str, const std::basic_string_view<T, Traits>& suffix) noexcept
|
||||
{
|
||||
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
return str.size() >= suffix.size() && equals_insensitive_ascii<>({ str.data() - suffix.size(), suffix.size() }, suffix);
|
||||
return str.size() >= suffix.size() && equals_insensitive_ascii<>({ str.data() + str.size() - suffix.size(), suffix.size() }, suffix);
|
||||
}
|
||||
|
||||
constexpr bool ends_with_insensitive_ascii(const std::string_view& str, const std::string_view& prefix) noexcept
|
||||
@@ -251,7 +251,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
|
||||
constexpr bool ends_with_insensitive_ascii(const std::wstring_view& str, const std::wstring_view& prefix) noexcept
|
||||
{
|
||||
return ends_with<>(str, prefix);
|
||||
return ends_with_insensitive_ascii<>(str, prefix);
|
||||
}
|
||||
|
||||
template<typename T, typename Traits>
|
||||
|
||||
@@ -6,8 +6,3 @@
|
||||
|
||||
TARGETNAME = ConInteractivityOneCoreLib
|
||||
TARGETTYPE = LIBRARY
|
||||
|
||||
# VSTS 14847240: Locally suppress individual -Wv:17 compiler warnings.
|
||||
# For more information, visit https://osgwiki.com/wiki/Windows_C%2B%2B_Toolset_Status.
|
||||
USER_C_FLAGS=$(USER_C_FLAGS) /wd4302 # 'conversion': truncation from 'type1' to 'type2'
|
||||
USER_C_FLAGS=$(USER_C_FLAGS) /wd4311 # 'variable': pointer truncation from 'type 1' to 'type 2'
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -119,12 +119,15 @@ DWORD WINAPI Renderer::s_renderThread(void* param) noexcept
|
||||
|
||||
DWORD Renderer::_renderThread() noexcept
|
||||
{
|
||||
while (true)
|
||||
while (_threadKeepRunning.load(std::memory_order_relaxed))
|
||||
{
|
||||
_enable.wait();
|
||||
_waitUntilCanRender();
|
||||
_waitUntilTimerOrRedraw();
|
||||
|
||||
// We just completed what could have been a long wait;
|
||||
// eagerly check again to prevent rendering if we don't
|
||||
// need to.
|
||||
if (!_threadKeepRunning.load(std::memory_order_relaxed))
|
||||
{
|
||||
break;
|
||||
@@ -1058,38 +1061,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||
ROW* rowBackup = nullptr;
|
||||
if (row == compositionRow)
|
||||
{
|
||||
auto& scratch = buffer.GetScratchpadRow();
|
||||
scratch.CopyFrom(r);
|
||||
rowBackup = &scratch;
|
||||
|
||||
std::wstring_view text{ activeComposition.text };
|
||||
RowWriteState state{
|
||||
.columnLimit = r.GetReadableColumnCount(),
|
||||
.columnEnd = _compositionCache->absoluteOrigin.x,
|
||||
};
|
||||
|
||||
size_t off = 0;
|
||||
for (const auto& range : activeComposition.attributes)
|
||||
{
|
||||
const auto len = range.len;
|
||||
auto attr = range.attr;
|
||||
|
||||
// Use the color at the cursor if TSF didn't specify any explicit color.
|
||||
if (attr.GetBackground().IsDefault())
|
||||
{
|
||||
attr.SetBackground(_compositionCache->baseAttribute.GetBackground());
|
||||
}
|
||||
if (attr.GetForeground().IsDefault())
|
||||
{
|
||||
attr.SetForeground(_compositionCache->baseAttribute.GetForeground());
|
||||
}
|
||||
|
||||
state.text = text.substr(off, len);
|
||||
state.columnBegin = state.columnEnd;
|
||||
const_cast<ROW&>(r).ReplaceText(state);
|
||||
const_cast<ROW&>(r).ReplaceAttributes(state.columnBegin, state.columnEnd, attr);
|
||||
off += len;
|
||||
}
|
||||
rowBackup = _PaintBufferOutputComposition(buffer, r, activeComposition);
|
||||
}
|
||||
const auto restore = wil::scope_exit([&] {
|
||||
if (rowBackup)
|
||||
@@ -1129,6 +1101,107 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||
}
|
||||
}
|
||||
|
||||
ROW* Renderer::_PaintBufferOutputComposition(TextBuffer& buffer, const ROW& r, const Composition& activeComposition)
|
||||
{
|
||||
auto& scratch = buffer.GetScratchpadRow();
|
||||
scratch.CopyFrom(r);
|
||||
|
||||
// *Overwrite* the original text with the active composition...
|
||||
til::CoordType compositionEnd = 0;
|
||||
{
|
||||
std::wstring_view text{ activeComposition.text };
|
||||
RowWriteState state{
|
||||
.columnLimit = r.GetReadableColumnCount(),
|
||||
.columnEnd = _compositionCache->absoluteOrigin.x,
|
||||
};
|
||||
|
||||
size_t off = 0;
|
||||
for (const auto& range : activeComposition.attributes)
|
||||
{
|
||||
const auto len = range.len;
|
||||
auto attr = range.attr;
|
||||
|
||||
// Use the color at the cursor if TSF didn't specify any explicit color.
|
||||
if (attr.GetBackground().IsDefault())
|
||||
{
|
||||
attr.SetBackground(_compositionCache->baseAttribute.GetBackground());
|
||||
}
|
||||
if (attr.GetForeground().IsDefault())
|
||||
{
|
||||
attr.SetForeground(_compositionCache->baseAttribute.GetForeground());
|
||||
}
|
||||
|
||||
state.text = text.substr(off, len);
|
||||
state.columnBegin = state.columnEnd;
|
||||
const_cast<ROW&>(r).ReplaceText(state);
|
||||
const_cast<ROW&>(r).ReplaceAttributes(state.columnBegin, state.columnEnd, attr);
|
||||
off += len;
|
||||
}
|
||||
|
||||
compositionEnd = state.columnEnd;
|
||||
}
|
||||
|
||||
// The text we've overwritten may have been crucial to the user,
|
||||
// so copy it back by absorbing available whitespace to the right
|
||||
// and re-inserting the non-whitespace characters instead.
|
||||
const auto compositionWidth = compositionEnd - _compositionCache->absoluteOrigin.x;
|
||||
const auto colLimit = r.GetReadableColumnCount();
|
||||
if (compositionWidth > 0 && compositionEnd < colLimit)
|
||||
{
|
||||
const auto text = scratch.GetText();
|
||||
auto srcCol = _compositionCache->absoluteOrigin.x;
|
||||
auto dstCol = compositionEnd;
|
||||
auto remaining = compositionWidth;
|
||||
size_t i = scratch.GetCharOffset(srcCol);
|
||||
|
||||
while (i < text.size() && dstCol < colLimit)
|
||||
{
|
||||
// Treat whitespace we encounter as a credit towards our composition width.
|
||||
// This loop essentially absorbs the whitespace.
|
||||
while (i < text.size() && til::at(text, i) == L' ' && remaining > 0)
|
||||
{
|
||||
remaining--;
|
||||
srcCol++;
|
||||
i++;
|
||||
}
|
||||
if (remaining <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the end of the non-whitespace span: Our span of text to insert.
|
||||
auto spanEnd = i;
|
||||
while (spanEnd < text.size() && til::at(text, spanEnd) != L' ')
|
||||
{
|
||||
spanEnd++;
|
||||
}
|
||||
|
||||
// Copy the non-whitespace segment from the original text (scratch) back in.
|
||||
RowCopyTextFromState state{
|
||||
.source = scratch,
|
||||
.columnBegin = dstCol,
|
||||
.columnLimit = colLimit,
|
||||
.sourceColumnBegin = srcCol,
|
||||
.sourceColumnLimit = scratch.GetLeadingColumnAtCharOffset(spanEnd),
|
||||
};
|
||||
const_cast<ROW&>(r).CopyTextFrom(state);
|
||||
|
||||
const auto srcBeg = gsl::narrow_cast<uint16_t>(srcCol);
|
||||
const auto srcEnd = gsl::narrow_cast<uint16_t>(state.sourceColumnEnd);
|
||||
const auto attr = scratch.Attributes().slice(srcBeg, srcEnd);
|
||||
const auto dstBeg = gsl::narrow_cast<uint16_t>(dstCol);
|
||||
const auto dstEnd = gsl::narrow_cast<uint16_t>(dstCol + attr.size());
|
||||
const_cast<ROW&>(r).Attributes().replace(dstBeg, dstEnd, attr);
|
||||
|
||||
dstCol = state.columnEnd;
|
||||
srcCol = state.sourceColumnEnd;
|
||||
i = spanEnd;
|
||||
}
|
||||
}
|
||||
|
||||
return &scratch;
|
||||
}
|
||||
|
||||
static bool _IsAllSpaces(const std::wstring_view v)
|
||||
{
|
||||
// first non-space char is not found (is npos)
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace Microsoft::Console::Render
|
||||
void _scheduleRenditionBlink();
|
||||
[[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine);
|
||||
void _PaintBufferOutput(_In_ IRenderEngine* const pEngine);
|
||||
ROW* _PaintBufferOutputComposition(TextBuffer& buffer, const ROW& r, const Composition& activeComposition);
|
||||
void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, TextBufferCellIterator it, const til::point target);
|
||||
void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine, const TextAttribute textAttribute, const size_t cchLine, const til::point coordTarget);
|
||||
bool _isHoveredHyperlink(const TextAttribute& textAttribute) const noexcept;
|
||||
|
||||
@@ -595,9 +595,12 @@ constexpr T saturate(auto val)
|
||||
RETURN_IF_FAILED(pObjectHandle->GetScreenBuffer(GENERIC_READ, &pObj));
|
||||
|
||||
// See ConptyCursorPositionMayBeWrong() for details.
|
||||
if (pObj->ConptyCursorPositionMayBeWrong())
|
||||
auto& activeBuffer = pObj->GetActiveBuffer();
|
||||
// GetConsoleScreenBufferInfoExImpl uses GetActiveBuffer internally, but
|
||||
// under the console lock.
|
||||
if (activeBuffer.ConptyCursorPositionMayBeWrong())
|
||||
{
|
||||
pObj->WaitForConptyCursorPositionToBeSynchronized();
|
||||
activeBuffer.WaitForConptyCursorPositionToBeSynchronized();
|
||||
}
|
||||
|
||||
m->_pApiRoutines->GetConsoleScreenBufferInfoExImpl(*pObj, ex);
|
||||
|
||||
@@ -166,7 +166,7 @@ void InteractDispatch::MoveCursor(const VTInt row, const VTInt col)
|
||||
|
||||
// Unblock any callers inside SCREEN_INFORMATION::WaitForConptyCursorPositionToBeSynchronized().
|
||||
// The cursor position has now been updated to the terminal's.
|
||||
info.ResetConptyCursorPositionMayBeWrong();
|
||||
info.GetActiveBuffer().ResetConptyCursorPositionMayBeWrong();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -31,6 +31,7 @@ Enum AssetType {
|
||||
ApplicationBundle
|
||||
PreinstallKit
|
||||
GroupPolicy
|
||||
NugetPackage
|
||||
Zip
|
||||
}
|
||||
|
||||
@@ -87,6 +88,9 @@ Class Asset {
|
||||
} ElseIf (".zip" -eq $local:ext -and $local:filename -like 'GroupPolicy*') {
|
||||
$this.Type = [AssetType]::GroupPolicy
|
||||
$this.Architecture = "all"
|
||||
} ElseIf (".nupkg" -eq $local:ext) {
|
||||
$this.Type = [AssetType]::NugetPackage
|
||||
$this.Architecture = "all"
|
||||
} ElseIf (".zip" -eq $local:ext) {
|
||||
$this.Type = [AssetType]::Zip
|
||||
} ElseIf (".msixbundle" -eq $local:ext) {
|
||||
@@ -108,6 +112,13 @@ Class Asset {
|
||||
Write-Verbose "Parsing AppxManifest.xml"
|
||||
$local:Manifest = [xml](Get-Content (Join-Path $local:directory AppxManifest.xml))
|
||||
$this.ParseManifest($local:Manifest)
|
||||
} ElseIf ($this.Type -Eq [AssetType]::NugetPackage) {
|
||||
Write-Verbose "Cracking nuget package $($local:filename)"
|
||||
& $script:tar -x -f $local:bundlePath -C $local:directory *.nuspec
|
||||
$local:nuspec = Get-ChildItem $local:directory *.nuspec -Recurse | Select-Object -First 1
|
||||
Write-Verbose "Parsing $($local:nuspec.Name)"
|
||||
$local:Manifest = [xml](Get-Content $local:nuspec)
|
||||
$this.ParseNuspec($local:Manifest)
|
||||
} Else {
|
||||
If ($this.Type -Ne [AssetType]::GroupPolicy) {
|
||||
& $script:tar -x -f $this.Path -C $local:directory --strip-components=1 '*/wt.exe'
|
||||
@@ -135,6 +146,12 @@ Class Asset {
|
||||
$this.Version = $Manifest.Package.Identity.Version
|
||||
}
|
||||
|
||||
[void]ParseNuspec([xml]$Nuspec) {
|
||||
$this.Name = $Nuspec.package.metadata.id
|
||||
$this.Version = ($Nuspec.package.metadata.version -split '-')[0]
|
||||
$this.ExpandedVersion = $Nuspec.package.metadata.version
|
||||
}
|
||||
|
||||
[void]ParseFilename([string]$filename) {
|
||||
$parts = [IO.Path]::GetFileNameWithoutExtension($filename).Split("_")
|
||||
$this.Name = $parts[0]
|
||||
@@ -160,6 +177,9 @@ Class Asset {
|
||||
GroupPolicy {
|
||||
"{0}_{1}.zip" -f ($this.Name, $this.Version)
|
||||
}
|
||||
NugetPackage {
|
||||
"{0}.{1}.nupkg" -f ($this.Name, $this.ExpandedVersion)
|
||||
}
|
||||
Default {
|
||||
Throw "Unknown type $($_.Type)"
|
||||
}
|
||||
@@ -253,15 +273,11 @@ Function New-ReleaseBody([Release]$Release) {
|
||||
If (-Not [String]::IsNullOrEmpty($zipAssetVersion)) {
|
||||
$body += "_Binary files inside the unpackaged distribution archive bear the version number ``$zipAssetVersion``._`n`n"
|
||||
}
|
||||
$body += "### Asset Hashes`n`n";
|
||||
ForEach($a in $Release.Assets) {
|
||||
$body += "- {0}`n - SHA256 ``{1}```n" -f ($a.IdealFilename(), (Get-FileHash $a.Path -Algorithm SHA256 | Select-Object -Expand Hash))
|
||||
}
|
||||
Return $body
|
||||
}
|
||||
|
||||
# Collect Assets from $Directory, figure out what those assets are
|
||||
$Assets = Get-ChildItem $Directory -Recurse -Include *.msixbundle, *.zip | ForEach-Object {
|
||||
$Assets = Get-ChildItem $Directory -Recurse -Include *.msixbundle, *.zip, *.nupkg -Exclude *.Wpf.*,*.symbols.nupkg | ForEach-Object {
|
||||
[Asset]::CreateFromFile($_.FullName)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
{
|
||||
"name": "ms-gsl",
|
||||
"version": "4.2.1"
|
||||
"version": "3.1.0"
|
||||
},
|
||||
{
|
||||
"name": "jsoncpp",
|
||||
|
||||
Reference in New Issue
Block a user